@remnic/core 9.54.4 → 9.54.6

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 (648) hide show
  1. package/dist/access-admin-ops-surface.d.ts +24 -23
  2. package/dist/access-admin-ops-surface.js +55 -50
  3. package/dist/access-authorization-probe.d.ts +25 -22
  4. package/dist/access-authorization-probe.js +58 -51
  5. package/dist/access-boundary.d.ts +26 -23
  6. package/dist/access-boundary.js +59 -52
  7. package/dist/access-cli.js +115 -107
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +19 -18
  11. package/dist/access-extraction-force-flush.js +55 -50
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-lcm-compaction.d.ts +22 -21
  14. package/dist/access-http-lifecycle-flush.d.ts +22 -21
  15. package/dist/access-http-offline-stream.d.ts +19 -18
  16. package/dist/access-http-query.js +55 -50
  17. package/dist/access-http.d.ts +55 -36
  18. package/dist/access-http.js +76 -65
  19. package/dist/access-identity-continuity-surface.d.ts +9 -9
  20. package/dist/access-identity-continuity-surface.js +55 -50
  21. package/dist/access-lcm-surface.d.ts +21 -20
  22. package/dist/access-lcm-surface.js +55 -50
  23. package/dist/access-mcp.d.ts +22 -21
  24. package/dist/access-mcp.js +63 -57
  25. package/dist/access-memory-search-fanout.d.ts +22 -2
  26. package/dist/access-memory-search-fanout.js +1 -1
  27. package/dist/{access-namespace-preflight-C7n4tgWC.d.ts → access-namespace-preflight-CCNn_6NE.d.ts} +1 -1
  28. package/dist/access-namespace-preflight.d.ts +3 -3
  29. package/dist/access-namespace-preflight.js +55 -50
  30. package/dist/access-observe-write-surface.d.ts +22 -21
  31. package/dist/access-observe-write-surface.js +55 -50
  32. package/dist/access-offline-manifest.d.ts +10 -9
  33. package/dist/access-offline-manifest.js +12 -10
  34. package/dist/access-operations-batch.js +57 -52
  35. package/dist/access-operations.d.ts +23 -22
  36. package/dist/access-operations.js +61 -55
  37. package/dist/access-recall-concurrency.d.ts +21 -20
  38. package/dist/access-recall-concurrency.js +55 -50
  39. package/dist/access-recall-response.d.ts +19 -18
  40. package/dist/access-recall-response.js +55 -50
  41. package/dist/access-recall-surface.d.ts +19 -18
  42. package/dist/access-recall-surface.js +55 -50
  43. package/dist/access-schema.d.ts +2 -2
  44. package/dist/access-schema.js +5 -4
  45. package/dist/{access-service-CXvbxLK-.d.ts → access-service-DkSdXK4S.d.ts} +1314 -83
  46. package/dist/access-service-helpers.d.ts +22 -21
  47. package/dist/access-service.d.ts +31 -30
  48. package/dist/access-service.js +55 -50
  49. package/dist/access-surface-catalog.d.ts +22 -21
  50. package/dist/access-surface-catalog.js +209 -2
  51. package/dist/access-surface-catalog.js.map +1 -1
  52. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  53. package/dist/action-confidence.d.ts +1 -1
  54. package/dist/active-memory-bridge.d.ts +14 -1
  55. package/dist/active-memory-bridge.js +6 -1
  56. package/dist/active-recall.d.ts +1 -1
  57. package/dist/active-recall.js +4 -2
  58. package/dist/active-recall.js.map +1 -1
  59. package/dist/adapters/index.js +7 -7
  60. package/dist/adapters/registry.js +3 -3
  61. package/dist/admin-console/public/what-helps-me/app.js +1594 -0
  62. package/dist/admin-console/public/what-helps-me/index.html +339 -0
  63. package/dist/admin-console/public/what-helps-me/model.js +685 -0
  64. package/dist/admin-console/public/what-helps-me/what-helps-me.css +1296 -0
  65. package/dist/ambient-provenance.d.ts +1 -1
  66. package/dist/artifact-search.d.ts +1 -1
  67. package/dist/{auto-sync-TDHLZPVP.js → auto-sync-NR3GPCOA.js} +4 -4
  68. package/dist/behavior-learner.d.ts +1 -1
  69. package/dist/behavior-signals.d.ts +1 -1
  70. package/dist/bootstrap.d.ts +9 -9
  71. package/dist/briefing.d.ts +2 -2
  72. package/dist/briefing.js +33 -12
  73. package/dist/buffer-surprise-report.d.ts +1 -1
  74. package/dist/buffer-turn-helpers.d.ts +1 -1
  75. package/dist/buffer.d.ts +2 -2
  76. package/dist/buffer.js +3 -3
  77. package/dist/bulk-import/index.d.ts +3 -3
  78. package/dist/calibration.d.ts +1 -1
  79. package/dist/calibration.js +1 -1
  80. package/dist/capabilities.d.ts +1 -1
  81. package/dist/{catalog-BOOxl-I2.d.ts → catalog-DGFf8mD9.d.ts} +1 -1
  82. package/dist/causal-behavior.d.ts +1 -1
  83. package/dist/causal-consolidation.d.ts +1 -1
  84. package/dist/causal-consolidation.js +34 -13
  85. package/dist/causal-consolidation.js.map +1 -1
  86. package/dist/causal-trajectory-graph.d.ts +1 -1
  87. package/dist/{chunk-ZYLC7NFO.js → chunk-26ZXBTYN.js} +4 -4
  88. package/dist/chunk-26ZXBTYN.js.map +1 -0
  89. package/dist/{chunk-LLJMAULE.js → chunk-2FKEIPIT.js} +5 -5
  90. package/dist/{chunk-3CSX3ETE.js → chunk-2LWQSV6A.js} +23 -11
  91. package/dist/chunk-2LWQSV6A.js.map +1 -0
  92. package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
  93. package/dist/chunk-37DZZFFM.js +502 -0
  94. package/dist/chunk-37DZZFFM.js.map +1 -0
  95. package/dist/chunk-3HSRWJTC.js +9 -0
  96. package/dist/chunk-3HSRWJTC.js.map +1 -0
  97. package/dist/{chunk-XUAMCLY4.js → chunk-3TIZPSJC.js} +17 -13
  98. package/dist/chunk-3TIZPSJC.js.map +1 -0
  99. package/dist/{chunk-PDDTI7IC.js → chunk-43SEBY2M.js} +1 -1
  100. package/dist/{chunk-HWRD23DW.js → chunk-46SZBVDS.js} +3 -3
  101. package/dist/{chunk-6HPHEFXT.js → chunk-5FD7JMTX.js} +2 -2
  102. package/dist/{chunk-7BZOBA5J.js → chunk-5NB726GV.js} +70 -16
  103. package/dist/chunk-5NB726GV.js.map +1 -0
  104. package/dist/chunk-62OKGIJA.js +39 -0
  105. package/dist/chunk-62OKGIJA.js.map +1 -0
  106. package/dist/{chunk-IBKR2TFR.js → chunk-6CYHUWCO.js} +2 -2
  107. package/dist/{chunk-YN6ASZUG.js → chunk-6QPI2ITJ.js} +5 -5
  108. package/dist/{chunk-UU3G6GUP.js → chunk-76JJSXK7.js} +1 -1
  109. package/dist/chunk-76JJSXK7.js.map +1 -0
  110. package/dist/{chunk-PX2LSHYM.js → chunk-7BD6O7ZV.js} +189 -4
  111. package/dist/chunk-7BD6O7ZV.js.map +1 -0
  112. package/dist/chunk-7TBXNYLO.js +346 -0
  113. package/dist/chunk-7TBXNYLO.js.map +1 -0
  114. package/dist/{chunk-NH5IR2WM.js → chunk-BRR5EUTF.js} +6 -2
  115. package/dist/chunk-BRR5EUTF.js.map +1 -0
  116. package/dist/{chunk-NIS256NW.js → chunk-CIAI24TQ.js} +3 -3
  117. package/dist/{chunk-T2JHZWH6.js → chunk-D3LBIUWV.js} +11703 -7573
  118. package/dist/chunk-D3LBIUWV.js.map +1 -0
  119. package/dist/{chunk-7FGL6CGA.js → chunk-DVYRD4HY.js} +2 -2
  120. package/dist/{chunk-G7Z6EENO.js → chunk-EKTP7MNS.js} +4 -4
  121. package/dist/{chunk-S75M5ZRK.js → chunk-F2JRDPON.js} +16 -1
  122. package/dist/chunk-F2JRDPON.js.map +1 -0
  123. package/dist/{chunk-RZ4XGKOJ.js → chunk-FFRTNF3L.js} +8 -8
  124. package/dist/{chunk-VKXWK44Q.js → chunk-FONWBCVY.js} +3 -3
  125. package/dist/{chunk-DZD576ZU.js → chunk-FPLM2TKD.js} +2 -2
  126. package/dist/{chunk-ZPJ4QSGP.js → chunk-FTPONF6B.js} +2 -2
  127. package/dist/{chunk-LJS7KUPV.js → chunk-FWX7W5PM.js} +2 -2
  128. package/dist/{chunk-UTRO5UJU.js → chunk-G6DFSS6A.js} +5 -1
  129. package/dist/chunk-G6DFSS6A.js.map +1 -0
  130. package/dist/{chunk-6LAPLZOS.js → chunk-GJ776BC2.js} +8 -3
  131. package/dist/chunk-GJ776BC2.js.map +1 -0
  132. package/dist/{chunk-G4T7L557.js → chunk-GN3WRFJY.js} +6 -6
  133. package/dist/{chunk-QT2FRYI4.js → chunk-H7NAZ3UB.js} +2 -2
  134. package/dist/{chunk-APPFNGX6.js → chunk-HBUFQRIM.js} +2 -2
  135. package/dist/{chunk-UU7TRFNE.js → chunk-HGJ6NJ4E.js} +2 -2
  136. package/dist/{chunk-WB7JSQCP.js → chunk-HIEWDJ2O.js} +5 -2
  137. package/dist/{chunk-WB7JSQCP.js.map → chunk-HIEWDJ2O.js.map} +1 -1
  138. package/dist/{chunk-UES4BHA2.js → chunk-I3MSGFGD.js} +17 -6
  139. package/dist/chunk-I3MSGFGD.js.map +1 -0
  140. package/dist/{chunk-C4ZNGVK3.js → chunk-IKP5OYQQ.js} +2 -2
  141. package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
  142. package/dist/{chunk-RDJRFDJY.js → chunk-IQ4NVWEF.js} +2 -2
  143. package/dist/{chunk-CGVHPCP2.js → chunk-JYG4OLWH.js} +2 -2
  144. package/dist/{chunk-WOPA3SGI.js → chunk-K26NWEOC.js} +8 -8
  145. package/dist/{chunk-K4QS7LXU.js → chunk-K2UGPTYY.js} +485 -44
  146. package/dist/chunk-K2UGPTYY.js.map +1 -0
  147. package/dist/{chunk-L6CQ7WIW.js → chunk-KFBOPMF6.js} +11 -11
  148. package/dist/chunk-KFBOPMF6.js.map +1 -0
  149. package/dist/{chunk-G7A5HLUS.js → chunk-KJH6W2I7.js} +2 -2
  150. package/dist/{chunk-IXEBUYO4.js → chunk-KM55SMLG.js} +11 -2
  151. package/dist/{chunk-IXEBUYO4.js.map → chunk-KM55SMLG.js.map} +1 -1
  152. package/dist/{chunk-HOIRB77A.js → chunk-L6Z5OAS4.js} +2 -2
  153. package/dist/chunk-LC2BDILZ.js +57 -0
  154. package/dist/chunk-LC2BDILZ.js.map +1 -0
  155. package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
  156. package/dist/{chunk-6R4G7N4S.js → chunk-M4YHAODQ.js} +388 -748
  157. package/dist/chunk-M4YHAODQ.js.map +1 -0
  158. package/dist/{chunk-3JLPGJ43.js → chunk-MIH3CJPN.js} +5 -5
  159. package/dist/{chunk-QHFV6VNU.js → chunk-MK5VEWCR.js} +2 -2
  160. package/dist/chunk-NISZQW72.js +140 -0
  161. package/dist/chunk-NISZQW72.js.map +1 -0
  162. package/dist/{chunk-GUKBW4HL.js → chunk-NQMSEB5D.js} +6 -6
  163. package/dist/{chunk-G2LUMAUF.js → chunk-OCIP6YMD.js} +12 -5
  164. package/dist/chunk-OCIP6YMD.js.map +1 -0
  165. package/dist/{chunk-47P3RYZP.js → chunk-PG4QBPVG.js} +21 -11
  166. package/dist/chunk-PG4QBPVG.js.map +1 -0
  167. package/dist/{chunk-WRJVB3C2.js → chunk-PO7NXBG2.js} +2 -2
  168. package/dist/{chunk-QWGE2PUH.js → chunk-PT54WISF.js} +190 -56
  169. package/dist/chunk-PT54WISF.js.map +1 -0
  170. package/dist/{chunk-AZM4CCAC.js → chunk-QZLDRQ4I.js} +8 -8
  171. package/dist/{chunk-THPPCISA.js → chunk-RBII7637.js} +5 -5
  172. package/dist/{chunk-JDZPNCMA.js → chunk-RUNORG2P.js} +6 -3
  173. package/dist/chunk-RUNORG2P.js.map +1 -0
  174. package/dist/{chunk-JV5YUBIZ.js → chunk-RZQ6W2W4.js} +50 -44
  175. package/dist/chunk-RZQ6W2W4.js.map +1 -0
  176. package/dist/{chunk-RUMNY5DT.js → chunk-SMNYAMJD.js} +6 -2
  177. package/dist/chunk-SMNYAMJD.js.map +1 -0
  178. package/dist/{chunk-MKQBXQKU.js → chunk-TB6CBZ5V.js} +22 -15
  179. package/dist/chunk-TB6CBZ5V.js.map +1 -0
  180. package/dist/{chunk-VMMQDEN5.js → chunk-TO7ZTWJW.js} +2 -2
  181. package/dist/{chunk-K2TDK7GG.js → chunk-TWTUGCTY.js} +2 -2
  182. package/dist/{chunk-HLYR2TQL.js → chunk-UCTS3DDH.js} +2 -2
  183. package/dist/{chunk-JYIJNNN3.js → chunk-UG6VXIOK.js} +2 -2
  184. package/dist/{chunk-WM65LU67.js → chunk-UKYS3NU5.js} +2 -2
  185. package/dist/{chunk-WDBX6VER.js → chunk-UPLZNJKN.js} +5 -5
  186. package/dist/{chunk-C23BYTJV.js → chunk-UW5NPZJ2.js} +40 -12
  187. package/dist/chunk-UW5NPZJ2.js.map +1 -0
  188. package/dist/{chunk-2GFK22OZ.js → chunk-V6ERBR7U.js} +2 -2
  189. package/dist/{chunk-CYF6WVE7.js → chunk-W5ZKPI34.js} +139 -158
  190. package/dist/chunk-W5ZKPI34.js.map +1 -0
  191. package/dist/chunk-XX4HPFFM.js +370 -0
  192. package/dist/chunk-XX4HPFFM.js.map +1 -0
  193. package/dist/{chunk-ZMYDYNI4.js → chunk-XZHE2DEN.js} +4 -4
  194. package/dist/chunk-YXQEKCZM.js +223 -0
  195. package/dist/chunk-YXQEKCZM.js.map +1 -0
  196. package/dist/{chunk-GYPQL6XN.js → chunk-Z4N7SVVF.js} +35 -47
  197. package/dist/chunk-Z4N7SVVF.js.map +1 -0
  198. package/dist/{chunk-MAXVH44E.js → chunk-Z54EM5YZ.js} +7 -7
  199. package/dist/{chunk-YGLY6QFV.js → chunk-ZRZVLB7O.js} +2 -2
  200. package/dist/{chunk-4IIFL7GC.js → chunk-ZUKIUXHQ.js} +10 -10
  201. package/dist/{cli-DVUFIa5E.d.ts → cli-Bze8S0Qs.d.ts} +5 -5
  202. package/dist/cli.d.ts +22 -21
  203. package/dist/cli.js +90 -80
  204. package/dist/coding/pre-action-gate.d.ts +1 -1
  205. package/dist/compounding/engine.d.ts +2 -2
  206. package/dist/compounding/engine.js +33 -12
  207. package/dist/compounding/preference-consolidator.d.ts +1 -1
  208. package/dist/compression-optimizer.d.ts +1 -1
  209. package/dist/config.d.ts +1 -1
  210. package/dist/config.js +4 -2
  211. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  212. package/dist/connectors/codex-materialize-runner.js +33 -12
  213. package/dist/connectors/codex-materialize.d.ts +1 -1
  214. package/dist/connectors/index.d.ts +1 -1
  215. package/dist/connectors/index.js +32 -12
  216. package/dist/consolidation-provenance-check.d.ts +2 -2
  217. package/dist/consolidation-undo.d.ts +2 -2
  218. package/dist/contradiction/index.d.ts +2 -2
  219. package/dist/contradiction/index.js +3 -1
  220. package/dist/contradiction-scan-JDTBXRGW.js +20 -0
  221. package/dist/converge-config.d.ts +1 -1
  222. package/dist/convergence-refresh.d.ts +2 -2
  223. package/dist/convergence-refresh.js +35 -14
  224. package/dist/conversation-index/backend.d.ts +1 -1
  225. package/dist/conversation-index/backend.js +2 -2
  226. package/dist/conversation-index/chunker.d.ts +1 -1
  227. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  228. package/dist/conversation-index/indexer.d.ts +1 -1
  229. package/dist/conversation-index/search.d.ts +1 -1
  230. package/dist/corpus-watermark.d.ts +1 -1
  231. package/dist/corpus-watermark.js +25 -24
  232. package/dist/day-summary.d.ts +1 -1
  233. package/dist/delinearize.d.ts +1 -1
  234. package/dist/dependency-propagation-config.d.ts +1 -1
  235. package/dist/{dependency-propagation-delivery-XzF76C9n.d.ts → dependency-propagation-delivery-CzEfB31P.d.ts} +1 -1
  236. package/dist/direct-answer-wiring.d.ts +1 -1
  237. package/dist/direct-answer.d.ts +1 -1
  238. package/dist/embedding-fallback.d.ts +1 -1
  239. package/dist/enrichment/index.d.ts +1 -1
  240. package/dist/entity-retrieval.d.ts +2 -2
  241. package/dist/entity-retrieval.js +33 -12
  242. package/dist/entity-schema.d.ts +1 -1
  243. package/dist/explicit-capture.d.ts +9 -9
  244. package/dist/explicit-capture.js +7 -4
  245. package/dist/external-wiki-access.d.ts +22 -21
  246. package/dist/external-wiki-access.js +56 -51
  247. package/dist/external-wiki-collection-registration.d.ts +1 -1
  248. package/dist/external-wiki-collection.d.ts +1 -1
  249. package/dist/external-wiki-mcp-tools.d.ts +22 -21
  250. package/dist/extraction-error-classification.d.ts +1 -1
  251. package/dist/extraction-faithfulness.d.ts +1 -1
  252. package/dist/extraction-faithfulness.js +2 -2
  253. package/dist/extraction-judge-telemetry.d.ts +1 -1
  254. package/dist/extraction-judge-training.d.ts +1 -1
  255. package/dist/extraction-judge.d.ts +1 -1
  256. package/dist/extraction-judge.js +2 -2
  257. package/dist/extraction-liveness.d.ts +1 -1
  258. package/dist/extraction-normalization.d.ts +1 -1
  259. package/dist/extraction-prompt.d.ts +1 -1
  260. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  261. package/dist/extraction-source-grounding.d.ts +1 -1
  262. package/dist/extraction.d.ts +1 -1
  263. package/dist/extraction.js +35 -19
  264. package/dist/fallback-llm.d.ts +14 -1
  265. package/dist/fallback-llm.js +1 -1
  266. package/dist/{first-start-migration-626C5554.js → first-start-migration-CRZCZAJN.js} +11 -3
  267. package/dist/first-start-migration-CRZCZAJN.js.map +1 -0
  268. package/dist/graph-dashboard-diff.d.ts +1 -1
  269. package/dist/graph-dashboard-key.d.ts +1 -1
  270. package/dist/graph-dashboard-parser.d.ts +1 -1
  271. package/dist/graph-edge-reinforcement.d.ts +1 -1
  272. package/dist/graph-path-reconstruction.d.ts +1 -1
  273. package/dist/graph-path-scoring.d.ts +1 -1
  274. package/dist/graph-snapshot.d.ts +1 -1
  275. package/dist/graph.d.ts +1 -1
  276. package/dist/harmonic-retrieval.js +1 -1
  277. package/dist/importance.d.ts +1 -1
  278. package/dist/importers/index.d.ts +1 -1
  279. package/dist/in-flight-reads.d.ts +1 -1
  280. package/dist/index.d.ts +314 -308
  281. package/dist/index.js +304 -133
  282. package/dist/inline-script.d.ts +3 -0
  283. package/dist/inline-script.js +8 -0
  284. package/dist/inline-script.js.map +1 -0
  285. package/dist/intent.d.ts +1 -1
  286. package/dist/lcm/engine.d.ts +1 -1
  287. package/dist/lcm/engine.js +3 -3
  288. package/dist/lcm/index.d.ts +1 -1
  289. package/dist/lcm/index.js +6 -6
  290. package/dist/lcm/tools.d.ts +1 -1
  291. package/dist/lifecycle.d.ts +1 -1
  292. package/dist/live-connectors-runner.d.ts +1 -1
  293. package/dist/local-llm.d.ts +2 -1
  294. package/dist/local-llm.js +2 -2
  295. package/dist/local-model-endpoint.d.ts +1 -1
  296. package/dist/maintenance/memory-governance.d.ts +1 -1
  297. package/dist/maintenance/memory-governance.js +33 -12
  298. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  299. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +33 -12
  300. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  301. package/dist/maintenance/rebuild-memory-projection.js +34 -13
  302. package/dist/{maintenance-BJzNIKBE.d.ts → maintenance-C8aRWfhq.d.ts} +3 -3
  303. package/dist/mcp-memory-inspector-app.d.ts +22 -21
  304. package/dist/mcp-read-only-tools.js +1 -1
  305. package/dist/memory-action-policy.d.ts +1 -1
  306. package/dist/memory-cache.d.ts +1 -1
  307. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  308. package/dist/memory-projection-mutations.js +2 -2
  309. package/dist/memory-projection-store.d.ts +6 -2
  310. package/dist/memory-projection-store.js +1 -1
  311. package/dist/memory-provenance.d.ts +1 -1
  312. package/dist/memory-snapshot.d.ts +1 -1
  313. package/dist/memory-worth-outcomes.d.ts +2 -2
  314. package/dist/models-json.d.ts +1 -1
  315. package/dist/namespaces/migrate.d.ts +3 -3
  316. package/dist/namespaces/migrate.js +24 -23
  317. package/dist/namespaces/principal.d.ts +1 -1
  318. package/dist/namespaces/search.d.ts +1 -1
  319. package/dist/namespaces/search.js +8 -8
  320. package/dist/namespaces/storage.d.ts +3 -3
  321. package/dist/namespaces/storage.js +33 -12
  322. package/dist/native-knowledge.d.ts +1 -1
  323. package/dist/offline-sync-file-io.d.ts +20 -0
  324. package/dist/offline-sync-file-io.js +18 -0
  325. package/dist/offline-sync-file-io.js.map +1 -0
  326. package/dist/offline-sync-impression-drain.d.ts +1 -1
  327. package/dist/offline-sync-impression-drain.js +5 -5
  328. package/dist/offline-sync.d.ts +8 -6
  329. package/dist/offline-sync.js +2 -1
  330. package/dist/operator-doctor-corpus.d.ts +1 -1
  331. package/dist/operator-doctor-corpus.js +26 -25
  332. package/dist/operator-doctor-replica.d.ts +1 -1
  333. package/dist/operator-toolkit.d.ts +3 -3
  334. package/dist/operator-toolkit.js +39 -36
  335. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  336. package/dist/orchestration/maintenance.d.ts +4 -4
  337. package/dist/orchestration/maintenance.js +38 -18
  338. package/dist/{orchestrator-BiXgBJBQ.d.ts → orchestrator-D85CzDDY.d.ts} +11 -8
  339. package/dist/orchestrator.d.ts +9 -9
  340. package/dist/orchestrator.js +117 -109
  341. package/dist/patterns-cli.d.ts +1 -1
  342. package/dist/{pipeline-sz_dcuLg.d.ts → pipeline-DPcUifCO.d.ts} +1 -1
  343. package/dist/policy-runtime.d.ts +1 -1
  344. package/dist/proactive-contention.d.ts +1 -1
  345. package/dist/provenance.d.ts +1 -1
  346. package/dist/public-http-D-SyWNj6.d.ts +25 -0
  347. package/dist/{purge-533FZLS4.js → purge-GB7PZHT2.js} +2 -2
  348. package/dist/{qmd-eAWObK4M.d.ts → qmd-dFCHp1r0.d.ts} +1 -1
  349. package/dist/qmd-preflight.d.ts +2 -2
  350. package/dist/qmd-recall-cache.d.ts +1 -1
  351. package/dist/qmd.d.ts +2 -2
  352. package/dist/recall-concurrency-config.d.ts +1 -1
  353. package/dist/recall-disclosure-escalation.d.ts +1 -1
  354. package/dist/recall-explain-renderer.d.ts +1 -1
  355. package/dist/recall-explain-renderer.js +3 -3
  356. package/dist/recall-memory-map.d.ts +1 -1
  357. package/dist/recall-planner-llm.d.ts +1 -1
  358. package/dist/recall-planner-llm.js +1 -1
  359. package/dist/recall-state.d.ts +1 -1
  360. package/dist/recall-tag-filter.d.ts +1 -1
  361. package/dist/recall-timings.d.ts +1 -1
  362. package/dist/recall-xray-cli.d.ts +1 -1
  363. package/dist/recall-xray-cli.js +4 -4
  364. package/dist/recall-xray-renderer.d.ts +1 -1
  365. package/dist/recall-xray-renderer.js +3 -3
  366. package/dist/recall-xray.d.ts +1 -1
  367. package/dist/recall-xray.js +2 -2
  368. package/dist/reconcile/cursor.d.ts +2 -1
  369. package/dist/reconcile/manifest.d.ts +2 -1
  370. package/dist/reconcile/manifest.js +6 -5
  371. package/dist/reconcile/plan.d.ts +2 -1
  372. package/dist/reconcile/plan.js +3 -2
  373. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  374. package/dist/replay/normalizers/claude.d.ts +1 -1
  375. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  376. package/dist/replay/normalizers/shared.d.ts +1 -1
  377. package/dist/replay/runner.d.ts +1 -1
  378. package/dist/replay/types.d.ts +1 -1
  379. package/dist/replica-divergence.d.ts +1 -1
  380. package/dist/replica-peers-config.d.ts +1 -1
  381. package/dist/resolve-auth-token.d.ts +1 -1
  382. package/dist/resume-bundles.js +5 -3
  383. package/dist/retrieval-agents.d.ts +2 -2
  384. package/dist/retrieval-tiers.d.ts +1 -1
  385. package/dist/routing/engine.d.ts +1 -1
  386. package/dist/routing/store.d.ts +1 -1
  387. package/dist/salvage-envelope.d.ts +1 -1
  388. package/dist/schemas.d.ts +38 -38
  389. package/dist/{scope-profiles-BBQsXuyj.d.ts → scope-profiles-DICK7GtN.d.ts} +1 -1
  390. package/dist/search/embed-helper.d.ts +1 -1
  391. package/dist/search/factory.d.ts +1 -1
  392. package/dist/search/factory.js +6 -6
  393. package/dist/search/index.d.ts +1 -1
  394. package/dist/search/index.js +12 -12
  395. package/dist/search/lancedb-backend.d.ts +1 -1
  396. package/dist/search/meilisearch-backend.d.ts +1 -1
  397. package/dist/search/noop-backend.d.ts +1 -1
  398. package/dist/search/orama-backend.d.ts +1 -1
  399. package/dist/search/port.d.ts +1 -1
  400. package/dist/search/remote-backend.d.ts +1 -1
  401. package/dist/{semantic-consolidation-HD8RkzVA.d.ts → semantic-consolidation-BP7NAX0N.d.ts} +1 -1
  402. package/dist/semantic-consolidation.d.ts +2 -2
  403. package/dist/semantic-consolidation.js +34 -13
  404. package/dist/semantic-rule-promotion.js +33 -12
  405. package/dist/semantic-rule-verifier.d.ts +1 -1
  406. package/dist/semantic-rule-verifier.js +33 -12
  407. package/dist/{service-DIrtHHEg.d.ts → service-DT-pJyda.d.ts} +660 -660
  408. package/dist/session-observer-bands.d.ts +1 -1
  409. package/dist/session-observer-state.d.ts +1 -1
  410. package/dist/shared-context/manager.d.ts +1 -1
  411. package/dist/signal.d.ts +1 -1
  412. package/dist/source-agent-qualifier.d.ts +1 -1
  413. package/dist/source-agent-qualifier.js +31 -15
  414. package/dist/{storage-DqCzXi56.d.ts → storage-qOnaJ1Lw.d.ts} +49 -13
  415. package/dist/storage.d.ts +2 -2
  416. package/dist/storage.js +33 -12
  417. package/dist/summarizer.d.ts +1 -1
  418. package/dist/summarizer.js +5 -5
  419. package/dist/summary-snapshot.d.ts +1 -1
  420. package/dist/support-passport/index.d.ts +518 -0
  421. package/dist/support-passport/index.js +357 -0
  422. package/dist/support-passport/index.js.map +1 -0
  423. package/dist/temporal-supersession.d.ts +2 -2
  424. package/dist/temporal-timeline-recall.d.ts +1 -1
  425. package/dist/temporal-validity.d.ts +1 -1
  426. package/dist/threading.d.ts +1 -1
  427. package/dist/tier-migration.d.ts +2 -2
  428. package/dist/tier-routing.d.ts +2 -2
  429. package/dist/tier-routing.js +7 -1
  430. package/dist/{tier-stats-2JCZYEF7.js → tier-stats-EXYSKSVN.js} +8 -2
  431. package/dist/{tier-stats-2JCZYEF7.js.map → tier-stats-EXYSKSVN.js.map} +1 -1
  432. package/dist/topics.d.ts +1 -1
  433. package/dist/transcript.d.ts +1 -1
  434. package/dist/transfer/autodetect.js +1 -1
  435. package/dist/transfer/backup.js +1 -1
  436. package/dist/transfer/capsule-export.js +2 -2
  437. package/dist/transfer/types.d.ts +36 -36
  438. package/dist/trust-score-stage.d.ts +1 -1
  439. package/dist/trust-score.d.ts +1 -1
  440. package/dist/{types-d_j4vC6N.d.ts → types-ed-SvS2t.d.ts} +8 -1
  441. package/dist/types.d.ts +1 -1
  442. package/dist/types.js +1 -1
  443. package/dist/utility-runtime.d.ts +2 -2
  444. package/dist/verified-recall.js +33 -12
  445. package/dist/write-envelope.d.ts +1 -1
  446. package/dist/write-rate-limiter.d.ts +11 -5
  447. package/dist/write-rate-limiter.js +1 -1
  448. package/package.json +12 -2
  449. package/scripts/build-with-heap.mjs +11 -1
  450. package/src/access-authorization-probe.ts +20 -2
  451. package/src/access-boundary.ts +32 -0
  452. package/src/access-http.test.ts +1971 -643
  453. package/src/access-http.ts +81 -74
  454. package/src/access-mcp.ts +3 -1
  455. package/src/access-memory-search-fanout.ts +24 -1
  456. package/src/access-offline-manifest.ts +5 -0
  457. package/src/access-operations.ts +2 -0
  458. package/src/access-service-namespace.test.ts +14 -0
  459. package/src/access-service-observe-scope.test.ts +2 -0
  460. package/src/access-service-offline-file-content.test.ts +211 -0
  461. package/src/access-service.ts +628 -884
  462. package/src/access-surface-catalog.test.ts +40 -10
  463. package/src/access-surface-catalog.ts +15 -0
  464. package/src/active-memory-bridge.test.ts +151 -0
  465. package/src/active-memory-bridge.ts +95 -21
  466. package/src/briefing.test.ts +38 -0
  467. package/src/briefing.ts +3 -3
  468. package/src/cli.ts +6 -1
  469. package/src/config.ts +18 -9
  470. package/src/connectors/codex-materialize-runner.ts +2 -0
  471. package/src/contradiction/contradiction-scan.ts +2 -0
  472. package/src/contradiction/contradiction.test.ts +42 -0
  473. package/src/correction/correction-access-wiring.ts +8 -2
  474. package/src/correction/correction-storage-integration.test.ts +7 -0
  475. package/src/explicit-capture.test.ts +24 -0
  476. package/src/explicit-capture.ts +2 -0
  477. package/src/fallback-llm.test.ts +54 -0
  478. package/src/fallback-llm.ts +54 -9
  479. package/src/index.ts +3 -0
  480. package/src/inline-script.ts +6 -0
  481. package/src/local-llm-headers-timeout.test.ts +42 -0
  482. package/src/local-llm.ts +8 -8
  483. package/src/maintenance/first-start-migration.ts +2 -1
  484. package/src/maintenance/memory-governance.test.ts +25 -0
  485. package/src/maintenance/memory-governance.ts +4 -1
  486. package/src/maintenance/pattern-reinforcement.test.ts +20 -0
  487. package/src/maintenance/pattern-reinforcement.ts +2 -1
  488. package/src/maintenance/rebuild-memory-projection.ts +8 -8
  489. package/src/mcp-read-only-tools.ts +3 -0
  490. package/src/memory-projection-store.ts +25 -27
  491. package/src/offline-sync-file-io.ts +68 -0
  492. package/src/offline-sync.ts +43 -57
  493. package/src/orchestration/consolidation-run.ts +5 -4
  494. package/src/orchestration/contradiction-linking-coordinator.ts +7 -0
  495. package/src/orchestration/contradiction-localization.integration.test.ts +115 -0
  496. package/src/orchestration/lifecycle-policy-coordinator.ts +2 -1
  497. package/src/orchestration/qmd-result-resolver.test.ts +215 -14
  498. package/src/orchestration/qmd-result-resolver.ts +142 -82
  499. package/src/orchestration/recall-search-pipeline.test.ts +41 -0
  500. package/src/orchestration/recall-search-pipeline.ts +12 -0
  501. package/src/orchestration/semantic-consolidation-coordinator.ts +2 -1
  502. package/src/orchestration/tier-migration-coordinator.ts +23 -56
  503. package/src/orchestration/workspace-ops.test.ts +37 -0
  504. package/src/orchestration/workspace-ops.ts +5 -29
  505. package/src/orchestrator.ts +7 -8
  506. package/src/relay/mission-access.test.ts +11 -11
  507. package/src/relay/mission-ui.test.ts +3 -1
  508. package/src/storage/supersession-audit.ts +7 -1
  509. package/src/storage/supersession-side-effects.ts +17 -5
  510. package/src/storage/tombstone-blocked-capture-sync.ts +31 -0
  511. package/src/storage-contract/failure-semantics.test.ts +102 -0
  512. package/src/storage.ts +18 -19
  513. package/src/support-passport/access-http-base.ts +143 -0
  514. package/src/support-passport/access-http.ts +172 -0
  515. package/src/support-passport/access-operations.ts +272 -0
  516. package/src/support-passport/access-service-base.test.ts +193 -0
  517. package/src/support-passport/access-service-base.ts +176 -0
  518. package/src/support-passport/access-surface.test.ts +283 -0
  519. package/src/support-passport/access-surface.ts +286 -0
  520. package/src/support-passport/card-projection.test.ts +137 -0
  521. package/src/support-passport/card-projection.ts +348 -0
  522. package/src/support-passport/card-replacement.ts +126 -0
  523. package/src/support-passport/card-service-commit-forwarding.test.ts +160 -0
  524. package/src/support-passport/card-service.test.ts +2964 -0
  525. package/src/support-passport/card-service.ts +1184 -0
  526. package/src/support-passport/card-state.ts +158 -0
  527. package/src/support-passport/config-model-routing.test.ts +37 -0
  528. package/src/support-passport/config.test.ts +34 -0
  529. package/src/support-passport/config.ts +41 -0
  530. package/src/support-passport/contracts.test.ts +156 -0
  531. package/src/support-passport/contracts.ts +149 -0
  532. package/src/support-passport/errors.ts +30 -0
  533. package/src/support-passport/generated-batch.test.ts +185 -0
  534. package/src/support-passport/generated-batch.ts +338 -0
  535. package/src/support-passport/grant-contracts.ts +183 -0
  536. package/src/support-passport/grant-generated-batch-scope.test.ts +201 -0
  537. package/src/support-passport/grant-service-cache.test.ts +148 -0
  538. package/src/support-passport/grant-service-cancellation.test.ts +83 -0
  539. package/src/support-passport/grant-service-marker-cache.test.ts +62 -0
  540. package/src/support-passport/grant-service.test.ts +3496 -0
  541. package/src/support-passport/grant-service.ts +513 -0
  542. package/src/support-passport/grant-store-commit-hook.test.ts +123 -0
  543. package/src/support-passport/grant-store-order.test.ts +60 -0
  544. package/src/support-passport/grant-store-path-safety.test.ts +30 -0
  545. package/src/support-passport/grant-store-recovery.test.ts +448 -0
  546. package/src/support-passport/grant-store.ts +906 -0
  547. package/src/support-passport/index.ts +17 -0
  548. package/src/support-passport/mcp-tools.test.ts +222 -0
  549. package/src/support-passport/mcp-tools.ts +187 -0
  550. package/src/support-passport/model-adapter.test.ts +840 -0
  551. package/src/support-passport/model-adapter.ts +602 -0
  552. package/src/support-passport/model-audit-pinned-write.test.ts +59 -0
  553. package/src/support-passport/model-audit.ts +152 -0
  554. package/src/support-passport/model-bridge.test.ts +456 -0
  555. package/src/support-passport/model-bridge.ts +439 -0
  556. package/src/support-passport/model-contracts.ts +124 -0
  557. package/src/support-passport/model-service.test.ts +1731 -0
  558. package/src/support-passport/model-service.ts +399 -0
  559. package/src/support-passport/offline-sync-guard.ts +230 -0
  560. package/src/support-passport/owner-http.test.ts +671 -0
  561. package/src/support-passport/owner-lock.ts +70 -0
  562. package/src/support-passport/private-file.test.ts +99 -0
  563. package/src/support-passport/private-file.ts +498 -0
  564. package/src/support-passport/public-http.test.ts +476 -0
  565. package/src/support-passport/public-http.ts +601 -0
  566. package/src/support-passport/ui.test.ts +181 -0
  567. package/src/tier-routing.ts +28 -17
  568. package/src/training-export/converter.test.ts +24 -0
  569. package/src/training-export/converter.ts +2 -0
  570. package/src/types.ts +8 -1
  571. package/src/wearables/service.test.ts +13 -0
  572. package/src/wearables/service.ts +2 -0
  573. package/src/write-rate-limiter.test.ts +30 -6
  574. package/src/write-rate-limiter.ts +29 -18
  575. package/dist/chunk-3CSX3ETE.js.map +0 -1
  576. package/dist/chunk-47P3RYZP.js.map +0 -1
  577. package/dist/chunk-6LAPLZOS.js.map +0 -1
  578. package/dist/chunk-6R4G7N4S.js.map +0 -1
  579. package/dist/chunk-7BZOBA5J.js.map +0 -1
  580. package/dist/chunk-C23BYTJV.js.map +0 -1
  581. package/dist/chunk-CYF6WVE7.js.map +0 -1
  582. package/dist/chunk-G2LUMAUF.js.map +0 -1
  583. package/dist/chunk-GYPQL6XN.js.map +0 -1
  584. package/dist/chunk-JDZPNCMA.js.map +0 -1
  585. package/dist/chunk-JV5YUBIZ.js.map +0 -1
  586. package/dist/chunk-K4QS7LXU.js.map +0 -1
  587. package/dist/chunk-L6CQ7WIW.js.map +0 -1
  588. package/dist/chunk-MKQBXQKU.js.map +0 -1
  589. package/dist/chunk-NH5IR2WM.js.map +0 -1
  590. package/dist/chunk-PX2LSHYM.js.map +0 -1
  591. package/dist/chunk-QWGE2PUH.js.map +0 -1
  592. package/dist/chunk-RUMNY5DT.js.map +0 -1
  593. package/dist/chunk-S75M5ZRK.js.map +0 -1
  594. package/dist/chunk-T2JHZWH6.js.map +0 -1
  595. package/dist/chunk-UES4BHA2.js.map +0 -1
  596. package/dist/chunk-UTRO5UJU.js.map +0 -1
  597. package/dist/chunk-UU3G6GUP.js.map +0 -1
  598. package/dist/chunk-XUAMCLY4.js.map +0 -1
  599. package/dist/chunk-ZYLC7NFO.js.map +0 -1
  600. package/dist/contradiction-scan-HYLIRMJ2.js +0 -14
  601. package/dist/first-start-migration-626C5554.js.map +0 -1
  602. /package/dist/{auto-sync-TDHLZPVP.js.map → auto-sync-NR3GPCOA.js.map} +0 -0
  603. /package/dist/{chunk-LLJMAULE.js.map → chunk-2FKEIPIT.js.map} +0 -0
  604. /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
  605. /package/dist/{chunk-PDDTI7IC.js.map → chunk-43SEBY2M.js.map} +0 -0
  606. /package/dist/{chunk-HWRD23DW.js.map → chunk-46SZBVDS.js.map} +0 -0
  607. /package/dist/{chunk-6HPHEFXT.js.map → chunk-5FD7JMTX.js.map} +0 -0
  608. /package/dist/{chunk-IBKR2TFR.js.map → chunk-6CYHUWCO.js.map} +0 -0
  609. /package/dist/{chunk-YN6ASZUG.js.map → chunk-6QPI2ITJ.js.map} +0 -0
  610. /package/dist/{chunk-NIS256NW.js.map → chunk-CIAI24TQ.js.map} +0 -0
  611. /package/dist/{chunk-7FGL6CGA.js.map → chunk-DVYRD4HY.js.map} +0 -0
  612. /package/dist/{chunk-G7Z6EENO.js.map → chunk-EKTP7MNS.js.map} +0 -0
  613. /package/dist/{chunk-RZ4XGKOJ.js.map → chunk-FFRTNF3L.js.map} +0 -0
  614. /package/dist/{chunk-VKXWK44Q.js.map → chunk-FONWBCVY.js.map} +0 -0
  615. /package/dist/{chunk-DZD576ZU.js.map → chunk-FPLM2TKD.js.map} +0 -0
  616. /package/dist/{chunk-ZPJ4QSGP.js.map → chunk-FTPONF6B.js.map} +0 -0
  617. /package/dist/{chunk-LJS7KUPV.js.map → chunk-FWX7W5PM.js.map} +0 -0
  618. /package/dist/{chunk-G4T7L557.js.map → chunk-GN3WRFJY.js.map} +0 -0
  619. /package/dist/{chunk-QT2FRYI4.js.map → chunk-H7NAZ3UB.js.map} +0 -0
  620. /package/dist/{chunk-APPFNGX6.js.map → chunk-HBUFQRIM.js.map} +0 -0
  621. /package/dist/{chunk-UU7TRFNE.js.map → chunk-HGJ6NJ4E.js.map} +0 -0
  622. /package/dist/{chunk-C4ZNGVK3.js.map → chunk-IKP5OYQQ.js.map} +0 -0
  623. /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
  624. /package/dist/{chunk-RDJRFDJY.js.map → chunk-IQ4NVWEF.js.map} +0 -0
  625. /package/dist/{chunk-CGVHPCP2.js.map → chunk-JYG4OLWH.js.map} +0 -0
  626. /package/dist/{chunk-WOPA3SGI.js.map → chunk-K26NWEOC.js.map} +0 -0
  627. /package/dist/{chunk-G7A5HLUS.js.map → chunk-KJH6W2I7.js.map} +0 -0
  628. /package/dist/{chunk-HOIRB77A.js.map → chunk-L6Z5OAS4.js.map} +0 -0
  629. /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
  630. /package/dist/{chunk-3JLPGJ43.js.map → chunk-MIH3CJPN.js.map} +0 -0
  631. /package/dist/{chunk-QHFV6VNU.js.map → chunk-MK5VEWCR.js.map} +0 -0
  632. /package/dist/{chunk-GUKBW4HL.js.map → chunk-NQMSEB5D.js.map} +0 -0
  633. /package/dist/{chunk-WRJVB3C2.js.map → chunk-PO7NXBG2.js.map} +0 -0
  634. /package/dist/{chunk-AZM4CCAC.js.map → chunk-QZLDRQ4I.js.map} +0 -0
  635. /package/dist/{chunk-THPPCISA.js.map → chunk-RBII7637.js.map} +0 -0
  636. /package/dist/{chunk-VMMQDEN5.js.map → chunk-TO7ZTWJW.js.map} +0 -0
  637. /package/dist/{chunk-K2TDK7GG.js.map → chunk-TWTUGCTY.js.map} +0 -0
  638. /package/dist/{chunk-HLYR2TQL.js.map → chunk-UCTS3DDH.js.map} +0 -0
  639. /package/dist/{chunk-JYIJNNN3.js.map → chunk-UG6VXIOK.js.map} +0 -0
  640. /package/dist/{chunk-WM65LU67.js.map → chunk-UKYS3NU5.js.map} +0 -0
  641. /package/dist/{chunk-WDBX6VER.js.map → chunk-UPLZNJKN.js.map} +0 -0
  642. /package/dist/{chunk-2GFK22OZ.js.map → chunk-V6ERBR7U.js.map} +0 -0
  643. /package/dist/{chunk-ZMYDYNI4.js.map → chunk-XZHE2DEN.js.map} +0 -0
  644. /package/dist/{chunk-MAXVH44E.js.map → chunk-Z54EM5YZ.js.map} +0 -0
  645. /package/dist/{chunk-YGLY6QFV.js.map → chunk-ZRZVLB7O.js.map} +0 -0
  646. /package/dist/{chunk-4IIFL7GC.js.map → chunk-ZUKIUXHQ.js.map} +0 -0
  647. /package/dist/{contradiction-scan-HYLIRMJ2.js.map → contradiction-scan-JDTBXRGW.js.map} +0 -0
  648. /package/dist/{purge-533FZLS4.js.map → purge-GB7PZHT2.js.map} +0 -0
@@ -0,0 +1,70 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+
4
+ import type { StorageManager } from "../index.js";
5
+ import {
6
+ type HeldFileLockController,
7
+ serializeMutations,
8
+ withHeldFileLock,
9
+ } from "../utils/serialize-mutations.js";
10
+ import { SupportPassportError } from "./errors.js";
11
+
12
+ const CARD_MUTATION_LOCK_STALE_MS = 60_000;
13
+ const CARD_MUTATION_LOCK_MAX_WAIT_MS = 30_000;
14
+
15
+ export type SupportPassportOwnerLockScope =
16
+ | { namespace: string; principal: string; ownerKey?: never }
17
+ | { namespace: string; ownerKey: string; principal?: never };
18
+
19
+ export function computeSupportPassportOwnerLockKey(namespace: string, principal: string): string {
20
+ return createHash("sha256").update(JSON.stringify([namespace, principal])).digest("hex");
21
+ }
22
+
23
+ export function supportPassportOwnerLockPath(
24
+ storage: StorageManager,
25
+ scope: SupportPassportOwnerLockScope,
26
+ ): string {
27
+ const ownerLockKey = scope.ownerKey ?? computeSupportPassportOwnerLockKey(scope.namespace, scope.principal);
28
+ if (!/^[a-f0-9]{64}$/.test(ownerLockKey)) {
29
+ throw new SupportPassportError("card_data_invalid", "The support passport owner scope is invalid.", 500);
30
+ }
31
+ return path.join(storage.dir, "state", `support-passport-cards-${ownerLockKey}.lock`);
32
+ }
33
+
34
+ export async function withSupportPassportOwnerLock<T>(
35
+ storage: StorageManager,
36
+ scope: SupportPassportOwnerLockScope,
37
+ task: (lock: HeldFileLockController) => Promise<T>,
38
+ ): Promise<T> {
39
+ const lockPath = supportPassportOwnerLockPath(storage, scope);
40
+ return await serializeMutations(lockPath, () =>
41
+ withHeldFileLock(
42
+ lockPath,
43
+ {
44
+ staleMs: CARD_MUTATION_LOCK_STALE_MS,
45
+ maxWaitMs: CARD_MUTATION_LOCK_MAX_WAIT_MS,
46
+ },
47
+ async (acquired, lock) => {
48
+ if (!acquired) {
49
+ throw new SupportPassportError(
50
+ "storage_conflict",
51
+ "The support passport is busy. Try again.",
52
+ 409,
53
+ );
54
+ }
55
+ return await task(lock);
56
+ },
57
+ ),
58
+ );
59
+ }
60
+
61
+ export async function requireSupportPassportOwnerLock(
62
+ lock: HeldFileLockController,
63
+ ): Promise<void> {
64
+ if (await lock.refresh()) return;
65
+ throw new SupportPassportError(
66
+ "storage_conflict",
67
+ "The support passport changed during the request.",
68
+ 409,
69
+ );
70
+ }
@@ -0,0 +1,99 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, mkdtemp, open, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+
7
+ import {
8
+ appendPrivateFileNoFollow,
9
+ ensurePrivateDirectoryNoFollow,
10
+ supportsSupportPassportPrivateFiles,
11
+ writePrivateFileAtomicallyNoFollow,
12
+ } from "./private-file.js";
13
+
14
+ test("private support passport files publish their platform gate", () => {
15
+ assert.equal(supportsSupportPassportPrivateFiles("linux"), true);
16
+ assert.equal(supportsSupportPassportPrivateFiles("darwin"), true);
17
+ assert.equal(supportsSupportPassportPrivateFiles("freebsd"), true);
18
+ assert.equal(supportsSupportPassportPrivateFiles("openbsd"), true);
19
+ assert.equal(supportsSupportPassportPrivateFiles("win32"), false);
20
+ });
21
+
22
+ test("private append cannot escape its pinned directory", async (context) => {
23
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-"));
24
+ context.after(async () => await rm(root, { recursive: true, force: true }));
25
+ const privateDirectory = path.join(root, "private");
26
+ const outsidePath = path.join(root, "outside.jsonl");
27
+ await mkdir(privateDirectory);
28
+
29
+ await assert.rejects(appendPrivateFileNoFollow(privateDirectory, outsidePath, "private\n", "private path escaped"));
30
+ await assert.rejects(readFile(outsidePath, "utf8"), { code: "ENOENT" });
31
+ });
32
+
33
+ test("private append fails before mutation when descriptor pinning is unavailable", async (context) => {
34
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-win32-"));
35
+ context.after(async () => await rm(root, { recursive: true, force: true }));
36
+ const privateDirectory = path.join(root, "private");
37
+ const auditPath = path.join(privateDirectory, "audit.jsonl");
38
+ await assert.rejects(
39
+ ensurePrivateDirectoryNoFollow(root, privateDirectory, "private path escaped", undefined, true, "win32"),
40
+ /private path escaped/
41
+ );
42
+ await assert.rejects(readFile(auditPath, "utf8"), { code: "ENOENT" });
43
+ });
44
+
45
+ test("private append rejects a renamed target after opening it", async (context) => {
46
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-append-rename-"));
47
+ context.after(async () => await rm(root, { recursive: true, force: true }));
48
+ const privateDirectory = path.join(root, "private");
49
+ const auditPath = path.join(privateDirectory, "audit.jsonl");
50
+ const movedPath = path.join(privateDirectory, "moved.jsonl");
51
+ await mkdir(privateDirectory);
52
+ await writeFile(auditPath, "before\n", { mode: 0o600 });
53
+
54
+ await assert.rejects(
55
+ appendPrivateFileNoFollow(
56
+ privateDirectory,
57
+ auditPath,
58
+ "after\n",
59
+ "audit target changed",
60
+ privateDirectory,
61
+ process.platform,
62
+ async (filePath, flags, mode) => {
63
+ const handle = await open(filePath, flags, mode);
64
+ await rename(auditPath, movedPath);
65
+ return handle;
66
+ }
67
+ ),
68
+ /audit target changed/
69
+ );
70
+ assert.equal(await readFile(movedPath, "utf8"), "before\n");
71
+ await assert.rejects(readFile(auditPath, "utf8"), { code: "ENOENT" });
72
+ });
73
+
74
+ test("private atomic writes reject a final pathname replaced after directory sync", async (context) => {
75
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-private-atomic-rename-"));
76
+ context.after(async () => await rm(root, { recursive: true, force: true }));
77
+ const privateDirectory = path.join(root, "private");
78
+ const markerPath = path.join(privateDirectory, "marker.json");
79
+ const movedPath = path.join(privateDirectory, "moved.json");
80
+ await mkdir(privateDirectory);
81
+
82
+ await assert.rejects(
83
+ writePrivateFileAtomicallyNoFollow(
84
+ privateDirectory,
85
+ markerPath,
86
+ "committed\n",
87
+ "marker target changed",
88
+ privateDirectory,
89
+ process.platform,
90
+ async () => {
91
+ await rename(markerPath, movedPath);
92
+ await writeFile(markerPath, "replaced\n", { mode: 0o600 });
93
+ }
94
+ ),
95
+ /marker target changed/
96
+ );
97
+ assert.equal(await readFile(movedPath, "utf8"), "committed\n");
98
+ assert.equal(await readFile(markerPath, "utf8"), "replaced\n");
99
+ });
@@ -0,0 +1,498 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { type Stats, constants as fsConstants } from "node:fs";
3
+ import { type FileHandle, lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
+
6
+ const UNSUPPORTED_DIRECTORY_SYNC_ERRORS = new Set(["EINVAL", "ENOSYS", "ENOTSUP", "EOPNOTSUPP"]);
7
+ const NON_WRITABLE_ANCESTOR_SYNC_ERRORS = new Set(["EROFS", "EPERM", "EACCES"]);
8
+
9
+ type PrivateFileOpener = (filePath: string, flags: number, mode: number) => Promise<FileHandle>;
10
+
11
+ function assertStableDirectory(before: Stats, opened: Stats, after: Stats, errorMessage: string): void {
12
+ if (
13
+ before.isSymbolicLink() ||
14
+ !before.isDirectory() ||
15
+ after.isSymbolicLink() ||
16
+ !after.isDirectory() ||
17
+ before.dev !== opened.dev ||
18
+ before.ino !== opened.ino ||
19
+ after.dev !== opened.dev ||
20
+ after.ino !== opened.ino
21
+ ) {
22
+ throw new Error(errorMessage);
23
+ }
24
+ }
25
+
26
+ function assertStableRegularFile(before: Stats, opened: Stats, after: Stats, errorMessage: string): void {
27
+ if (
28
+ before.isSymbolicLink() ||
29
+ !before.isFile() ||
30
+ after.isSymbolicLink() ||
31
+ !after.isFile() ||
32
+ before.dev !== opened.dev ||
33
+ before.ino !== opened.ino ||
34
+ after.dev !== opened.dev ||
35
+ after.ino !== opened.ino ||
36
+ opened.nlink !== 1
37
+ ) {
38
+ throw new Error(errorMessage);
39
+ }
40
+ }
41
+
42
+ async function lstatPrivateTarget(filePath: string, errorMessage: string): Promise<Stats> {
43
+ try {
44
+ return await lstat(filePath);
45
+ } catch {
46
+ throw new Error(errorMessage);
47
+ }
48
+ }
49
+
50
+ function openFlags(...flags: Array<number | undefined>): number {
51
+ let combined = 0;
52
+ for (const flag of flags) combined |= typeof flag === "number" ? flag : 0;
53
+ return combined;
54
+ }
55
+
56
+ function assertContainedPath(trustedRoot: string, directory: string, errorMessage: string): void {
57
+ const relative = path.relative(path.resolve(trustedRoot), path.resolve(directory));
58
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
59
+ throw new Error(errorMessage);
60
+ }
61
+ }
62
+
63
+ async function openDirectoryNoFollow(
64
+ directory: string,
65
+ errorMessage: string
66
+ ): Promise<{
67
+ before: Stats;
68
+ opened: Stats;
69
+ handle: FileHandle;
70
+ }> {
71
+ const before = await lstat(directory);
72
+ if (before.isSymbolicLink() || !before.isDirectory()) throw new Error(errorMessage);
73
+ const handle = await open(
74
+ directory,
75
+ openFlags(fsConstants.O_RDONLY, fsConstants.O_DIRECTORY, fsConstants.O_NOFOLLOW)
76
+ );
77
+ try {
78
+ const opened = await handle.stat();
79
+ assertStableDirectory(before, opened, await lstat(directory), errorMessage);
80
+ return { before, opened, handle };
81
+ } catch (error) {
82
+ await handle.close().catch(() => undefined);
83
+ throw error;
84
+ }
85
+ }
86
+
87
+ async function closeDirectoryHandles(handles: FileHandle[]): Promise<void> {
88
+ for (const handle of handles.reverse()) await handle.close().catch(() => undefined);
89
+ }
90
+
91
+ async function syncDirectoryHandle(handle: FileHandle): Promise<void> {
92
+ try {
93
+ await handle.sync();
94
+ } catch (error) {
95
+ const code = (error as NodeJS.ErrnoException).code;
96
+ if (!UNSUPPORTED_DIRECTORY_SYNC_ERRORS.has(code ?? "")) throw error;
97
+ }
98
+ }
99
+
100
+ export async function resolvePrivateDirectoryPath(
101
+ directory: string,
102
+ handle: FileHandle,
103
+ opened: Stats,
104
+ errorMessage: string,
105
+ platform: NodeJS.Platform = process.platform
106
+ ): Promise<string> {
107
+ const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
108
+ const pinnedPath = path.join(descriptorRoot, String(handle.fd));
109
+ const metadata = await stat(pinnedPath);
110
+ if (!metadata.isDirectory() || metadata.dev !== opened.dev || metadata.ino !== opened.ino) {
111
+ throw new Error(errorMessage);
112
+ }
113
+ return pinnedPath;
114
+ }
115
+
116
+ export function supportsSupportPassportPrivateFiles(
117
+ platform: NodeJS.Platform = process.platform
118
+ ): boolean {
119
+ return platform === "linux" || platform === "darwin" || platform === "freebsd" || platform === "openbsd";
120
+ }
121
+
122
+ export function requirePrivateFileDescriptorRoot(platform: NodeJS.Platform, errorMessage: string): string {
123
+ if (platform === "linux") return "/proc/self/fd";
124
+ if (platform === "darwin" || platform === "freebsd" || platform === "openbsd") return "/dev/fd";
125
+ throw new Error(errorMessage);
126
+ }
127
+
128
+ async function openStableDirectoryFromRoot(
129
+ trustedRoot: string,
130
+ directory: string,
131
+ errorMessage: string,
132
+ platform: NodeJS.Platform = process.platform
133
+ ): Promise<{
134
+ before: Stats;
135
+ opened: Stats;
136
+ handle?: FileHandle;
137
+ handles: FileHandle[];
138
+ pinnedDirectory: string;
139
+ }> {
140
+ const root = path.resolve(trustedRoot);
141
+ const target = path.resolve(directory);
142
+ assertContainedPath(root, target, errorMessage);
143
+ requirePrivateFileDescriptorRoot(platform, errorMessage);
144
+ const relative = path.relative(root, target);
145
+ const components = relative === "" ? [] : relative.split(path.sep);
146
+ const handles: FileHandle[] = [];
147
+ let currentPath = root;
148
+ try {
149
+ let current = await openDirectoryNoFollow(root, errorMessage);
150
+ handles.push(current.handle);
151
+ for (const component of components) {
152
+ const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
153
+ const child = await openDirectoryNoFollow(path.join(pinnedParent, component), errorMessage);
154
+ handles.push(child.handle);
155
+ assertStableDirectory(current.before, current.opened, await lstat(currentPath), errorMessage);
156
+ currentPath = path.join(currentPath, component);
157
+ current = child;
158
+ }
159
+ assertStableDirectory(current.before, current.opened, await lstat(target), errorMessage);
160
+ return {
161
+ ...current,
162
+ handles,
163
+ pinnedDirectory: await resolvePrivateDirectoryPath(
164
+ target,
165
+ current.handle,
166
+ current.opened,
167
+ errorMessage,
168
+ platform
169
+ ),
170
+ };
171
+ } catch (error) {
172
+ await closeDirectoryHandles(handles);
173
+ throw error;
174
+ }
175
+ }
176
+
177
+ export async function ensurePrivateDirectoryNoFollow(
178
+ trustedRoot: string,
179
+ directory: string,
180
+ errorMessage: string,
181
+ syncVerifiedParent: (handle: FileHandle) => Promise<void> = syncDirectoryHandle,
182
+ hardenExistingChildren = true,
183
+ platform: NodeJS.Platform = process.platform
184
+ ): Promise<void> {
185
+ const root = path.resolve(trustedRoot);
186
+ const target = path.resolve(directory);
187
+ assertContainedPath(root, target, errorMessage);
188
+ const relative = path.relative(root, target);
189
+ requirePrivateFileDescriptorRoot(platform, errorMessage);
190
+ const components = relative === "" ? [] : relative.split(path.sep);
191
+ const handles: FileHandle[] = [];
192
+ let currentPath = root;
193
+ try {
194
+ let current = await openDirectoryNoFollow(root, errorMessage);
195
+ handles.push(current.handle);
196
+ for (const component of components) {
197
+ const pinnedParent = await resolvePrivateDirectoryPath(currentPath, current.handle, current.opened, errorMessage);
198
+ const childPath = path.join(pinnedParent, component);
199
+ let created = false;
200
+ try {
201
+ await mkdir(childPath, { mode: 0o700 });
202
+ created = true;
203
+ } catch (error) {
204
+ if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
205
+ }
206
+ await syncVerifiedParent(current.handle);
207
+ const child = await openDirectoryNoFollow(childPath, errorMessage);
208
+ handles.push(child.handle);
209
+ if (created || (hardenExistingChildren && (child.opened.mode & 0o777) !== 0o700)) {
210
+ await child.handle.chmod(0o700);
211
+ }
212
+ assertStableDirectory(current.before, current.opened, await lstat(currentPath), errorMessage);
213
+ currentPath = path.join(currentPath, component);
214
+ current = child;
215
+ }
216
+ assertStableDirectory(current.before, current.opened, await lstat(target), errorMessage);
217
+ } finally {
218
+ await closeDirectoryHandles(handles);
219
+ }
220
+ }
221
+
222
+ export async function ensurePrivateDirectoryTreeNoFollow(
223
+ directory: string,
224
+ errorMessage: string,
225
+ syncVerifiedParent: (handle: FileHandle) => Promise<void> = syncDirectoryHandle,
226
+ platform: NodeJS.Platform = process.platform
227
+ ): Promise<void> {
228
+ const target = path.resolve(directory);
229
+ const filesystemRoot = path.parse(target).root;
230
+ const rootMetadata = await stat(filesystemRoot);
231
+ await ensurePrivateDirectoryNoFollow(
232
+ filesystemRoot,
233
+ target,
234
+ errorMessage,
235
+ async (handle) => {
236
+ try {
237
+ await syncVerifiedParent(handle);
238
+ } catch (error) {
239
+ const code = (error as NodeJS.ErrnoException).code;
240
+ const metadata = await handle.stat();
241
+ if (
242
+ metadata.ino === rootMetadata.ino &&
243
+ metadata.dev === rootMetadata.dev &&
244
+ NON_WRITABLE_ANCESTOR_SYNC_ERRORS.has(code ?? "")
245
+ ) {
246
+ return;
247
+ }
248
+ throw error;
249
+ }
250
+ },
251
+ false,
252
+ platform
253
+ );
254
+ }
255
+
256
+ export async function withPrivateDirectoryNoFollow<T>(
257
+ trustedRoot: string,
258
+ directory: string,
259
+ errorMessage: string,
260
+ task: (pinnedDirectory: string) => Promise<T>,
261
+ platform: NodeJS.Platform = process.platform
262
+ ): Promise<T> {
263
+ let directoryHandles: FileHandle[] = [];
264
+ try {
265
+ const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
266
+ directoryHandles = stableDirectory.handles;
267
+ const result = await task(stableDirectory.pinnedDirectory);
268
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
269
+ return result;
270
+ } finally {
271
+ await closeDirectoryHandles(directoryHandles);
272
+ }
273
+ }
274
+
275
+ export async function readPrivateFileNoFollow(
276
+ directory: string,
277
+ filePath: string,
278
+ errorMessage: string,
279
+ trustedRoot = directory,
280
+ platform: NodeJS.Platform = process.platform
281
+ ): Promise<string> {
282
+ if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
283
+ const targetName = path.basename(filePath);
284
+ let directoryHandles: FileHandle[] = [];
285
+ let fileHandle: FileHandle | undefined;
286
+ try {
287
+ const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
288
+ directoryHandles = stableDirectory.handles;
289
+ const pinnedDirectory = stableDirectory.pinnedDirectory;
290
+ const targetPath = path.join(pinnedDirectory, targetName);
291
+ const before = await lstat(targetPath);
292
+ if (before.isSymbolicLink() || !before.isFile()) throw new Error(errorMessage);
293
+ try {
294
+ fileHandle = await open(targetPath, openFlags(fsConstants.O_RDONLY, fsConstants.O_NOFOLLOW));
295
+ } catch (error) {
296
+ if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
297
+ throw error;
298
+ }
299
+ const fileMetadata = await fileHandle.stat();
300
+ assertStableRegularFile(before, fileMetadata, await lstat(targetPath), errorMessage);
301
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
302
+ return await fileHandle.readFile("utf8");
303
+ } finally {
304
+ await fileHandle?.close().catch(() => undefined);
305
+ await closeDirectoryHandles(directoryHandles);
306
+ }
307
+ }
308
+
309
+ export async function appendPrivateFileNoFollow(
310
+ directory: string,
311
+ filePath: string,
312
+ content: string,
313
+ errorMessage: string,
314
+ trustedRoot = directory,
315
+ platform: NodeJS.Platform = process.platform,
316
+ openFile: PrivateFileOpener = open
317
+ ): Promise<void> {
318
+ if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
319
+ const targetName = path.basename(filePath);
320
+ let directoryHandles: FileHandle[] = [];
321
+ try {
322
+ const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
323
+ directoryHandles = stableDirectory.handles;
324
+ await appendPrivateFileAtPinnedDirectory(
325
+ stableDirectory.pinnedDirectory,
326
+ targetName,
327
+ content,
328
+ errorMessage,
329
+ stableDirectory.handle,
330
+ openFile
331
+ );
332
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
333
+ } finally {
334
+ await closeDirectoryHandles(directoryHandles);
335
+ }
336
+ }
337
+
338
+ async function appendPrivateFileAtPinnedDirectory(
339
+ pinnedDirectory: string,
340
+ targetName: string,
341
+ content: string,
342
+ errorMessage: string,
343
+ directoryHandle: FileHandle | undefined,
344
+ openFile: PrivateFileOpener
345
+ ): Promise<void> {
346
+ if (
347
+ targetName.length === 0 ||
348
+ targetName === "." ||
349
+ targetName === ".." ||
350
+ path.basename(targetName) !== targetName
351
+ ) {
352
+ throw new Error(errorMessage);
353
+ }
354
+ const targetPath = path.join(pinnedDirectory, targetName);
355
+ let fileHandle: FileHandle | undefined;
356
+ try {
357
+ try {
358
+ fileHandle = await openFile(
359
+ targetPath,
360
+ openFlags(fsConstants.O_WRONLY, fsConstants.O_APPEND, fsConstants.O_CREAT, fsConstants.O_NOFOLLOW),
361
+ 0o600
362
+ );
363
+ } catch (error) {
364
+ if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
365
+ throw error;
366
+ }
367
+ const targetMetadata = await lstatPrivateTarget(targetPath, errorMessage);
368
+ assertStableRegularFile(
369
+ targetMetadata,
370
+ await fileHandle.stat(),
371
+ await lstatPrivateTarget(targetPath, errorMessage),
372
+ errorMessage
373
+ );
374
+ await fileHandle.chmod(0o600);
375
+ await fileHandle.appendFile(content, "utf8");
376
+ await fileHandle.sync();
377
+ assertStableRegularFile(
378
+ targetMetadata,
379
+ await fileHandle.stat(),
380
+ await lstatPrivateTarget(targetPath, errorMessage),
381
+ errorMessage
382
+ );
383
+ if (directoryHandle) await syncDirectoryHandle(directoryHandle);
384
+ } finally {
385
+ await fileHandle?.close().catch(() => undefined);
386
+ }
387
+ }
388
+
389
+ export async function appendPrivateFileInPinnedDirectory(
390
+ pinnedDirectory: string,
391
+ fileName: string,
392
+ content: string,
393
+ errorMessage: string,
394
+ platform: NodeJS.Platform = process.platform
395
+ ): Promise<void> {
396
+ const descriptorRoot = requirePrivateFileDescriptorRoot(platform, errorMessage);
397
+ const resolvedDirectory = path.resolve(pinnedDirectory);
398
+ const descriptor = path.relative(descriptorRoot, resolvedDirectory);
399
+ if (!/^\d+$/.test(descriptor)) throw new Error(errorMessage);
400
+ const before = await stat(resolvedDirectory);
401
+ if (!before.isDirectory()) throw new Error(errorMessage);
402
+ let directoryHandle: FileHandle | undefined;
403
+ try {
404
+ directoryHandle = await open(resolvedDirectory, openFlags(fsConstants.O_RDONLY, fsConstants.O_DIRECTORY));
405
+ const opened = await directoryHandle.stat();
406
+ assertStableDirectory(before, opened, await stat(resolvedDirectory), errorMessage);
407
+ await appendPrivateFileAtPinnedDirectory(resolvedDirectory, fileName, content, errorMessage, directoryHandle, open);
408
+ assertStableDirectory(before, opened, await stat(resolvedDirectory), errorMessage);
409
+ } finally {
410
+ await directoryHandle?.close().catch(() => undefined);
411
+ }
412
+ }
413
+
414
+ export async function writePrivateFileAtomicallyNoFollow(
415
+ directory: string,
416
+ filePath: string,
417
+ content: string,
418
+ errorMessage: string,
419
+ trustedRoot = directory,
420
+ platform: NodeJS.Platform = process.platform,
421
+ syncVerifiedDirectory: (handle: FileHandle) => Promise<void> = syncDirectoryHandle
422
+ ): Promise<void> {
423
+ if (path.dirname(filePath) !== path.resolve(directory)) throw new Error(errorMessage);
424
+ const targetName = path.basename(filePath);
425
+ const tempName = `.${targetName}.${randomUUID()}.tmp`;
426
+ let tempPath: string | undefined;
427
+ let directoryHandles: FileHandle[] = [];
428
+ let fileHandle: FileHandle | undefined;
429
+ try {
430
+ const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
431
+ directoryHandles = stableDirectory.handles;
432
+ const pinnedDirectory = stableDirectory.pinnedDirectory;
433
+ tempPath = path.join(pinnedDirectory, tempName);
434
+ const targetPath = path.join(pinnedDirectory, targetName);
435
+ try {
436
+ fileHandle = await open(
437
+ tempPath,
438
+ openFlags(fsConstants.O_WRONLY, fsConstants.O_CREAT, fsConstants.O_EXCL, fsConstants.O_NOFOLLOW),
439
+ 0o600
440
+ );
441
+ } catch (error) {
442
+ if ((error as NodeJS.ErrnoException).code === "ELOOP") throw new Error(errorMessage);
443
+ throw error;
444
+ }
445
+ const fileMetadata = await fileHandle.stat();
446
+ const tempMetadata = await lstat(tempPath);
447
+ assertStableRegularFile(tempMetadata, fileMetadata, await lstat(tempPath), errorMessage);
448
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
449
+ await fileHandle.chmod(0o600);
450
+ await fileHandle.writeFile(content, "utf8");
451
+ await fileHandle.sync();
452
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
453
+ await rename(tempPath, targetPath);
454
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
455
+ if (stableDirectory.handle) await syncVerifiedDirectory(stableDirectory.handle);
456
+ const committedMetadata = await lstatPrivateTarget(targetPath, errorMessage);
457
+ assertStableRegularFile(
458
+ committedMetadata,
459
+ await fileHandle.stat(),
460
+ await lstatPrivateTarget(targetPath, errorMessage),
461
+ errorMessage
462
+ );
463
+ } finally {
464
+ await fileHandle?.close().catch(() => undefined);
465
+ if (tempPath) await rm(tempPath, { force: true }).catch(() => undefined);
466
+ await closeDirectoryHandles(directoryHandles);
467
+ }
468
+ }
469
+
470
+ export async function removePrivateFilesNoFollow(
471
+ directory: string,
472
+ fileNames: string[],
473
+ errorMessage: string,
474
+ trustedRoot = directory,
475
+ platform: NodeJS.Platform = process.platform
476
+ ): Promise<void> {
477
+ if (
478
+ fileNames.some(
479
+ (fileName) =>
480
+ fileName.length === 0 || fileName === "." || fileName === ".." || path.basename(fileName) !== fileName
481
+ )
482
+ ) {
483
+ throw new Error(errorMessage);
484
+ }
485
+ let directoryHandles: FileHandle[] = [];
486
+ try {
487
+ const stableDirectory = await openStableDirectoryFromRoot(trustedRoot, directory, errorMessage, platform);
488
+ directoryHandles = stableDirectory.handles;
489
+ const pinnedDirectory = stableDirectory.pinnedDirectory;
490
+ for (const fileName of fileNames) {
491
+ await rm(path.join(pinnedDirectory, fileName), { force: true });
492
+ }
493
+ assertStableDirectory(stableDirectory.before, stableDirectory.opened, await lstat(directory), errorMessage);
494
+ if (stableDirectory.handle) await syncDirectoryHandle(stableDirectory.handle);
495
+ } finally {
496
+ await closeDirectoryHandles(directoryHandles);
497
+ }
498
+ }