@openclaw/matrix 2026.10.5-beta.3 → 2026.10.10-beta.1

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 (313) hide show
  1. package/dist/.setup/{approval-handler.runtime-rxLn8CbP.mjs → approval-handler.runtime-CsqMdj7h.mjs} +9 -9
  2. package/dist/.setup/{approval-reactions-DhB6gNPU.mjs → approval-reactions-D2pEUrFS.mjs} +1 -1
  3. package/dist/.setup/{channel-BcwGspPw.mjs → channel-Dw4_imdd.mjs} +132 -111
  4. package/dist/.setup/{channel.runtime-CUc6rbu1.mjs → channel.runtime-2EEtIlyu.mjs} +6 -6
  5. package/dist/.setup/{channel.setup-CwuYD_CT.mjs → channel.setup-Be8TYKMA.mjs} +8 -3
  6. package/dist/.setup/{cli-mbL9jCro.mjs → cli-CEmWh7wh.mjs} +99 -37
  7. package/dist/.setup/{cli-metadata-6MOGuyHq.mjs → cli-metadata-CBLGwJT6.mjs} +1 -1
  8. package/dist/.setup/{client-BlTvpEWC.mjs → client-BKiiI29H.mjs} +3 -3
  9. package/dist/.setup/{client-vPPbaxNp.mjs → client-C4Gibhzl.mjs} +94 -120
  10. package/dist/.setup/{client-bootstrap-D05xy1HB.mjs → client-bootstrap-kOIDV4z_.mjs} +3 -3
  11. package/dist/.setup/{create-client-CroC4BVc.mjs → create-client-BS8UYx6N.mjs} +23 -15
  12. package/dist/.setup/{credentials-Bf9PQ7xs.mjs → credentials-CPnJZaZB.mjs} +3 -5
  13. package/dist/.setup/{credentials-read-CqRiEAQa.mjs → credentials-read-D2ULV55A.mjs} +12 -4
  14. package/dist/.setup/{crypto-runtime-CpJ0b7vY.mjs → crypto-runtime-D8yY7kjD.mjs} +168 -290
  15. package/dist/.setup/crypto-state-store-D4wrCvFs.mjs +185 -0
  16. package/dist/.setup/crypto-store-ownership-Z_Y5WRKr.mjs +174 -0
  17. package/dist/.setup/crypto-unsafe-state-doctor-C8szCYmf.mjs +54 -0
  18. package/dist/.setup/{directory-live-B1WbfSHo.mjs → directory-live-BpSz-FQB.mjs} +1 -1
  19. package/dist/.setup/{draft-stream-Bdrh7dRt.mjs → draft-stream-DYf0th1n.mjs} +1 -1
  20. package/dist/.setup/{exec-approvals-BgNWWJg1.mjs → exec-approvals-DaM-5OA5.mjs} +33 -43
  21. package/dist/.setup/{file-sync-store-BdKx_gMi.mjs → file-sync-store-CUH8EAJq.mjs} +3 -3
  22. package/dist/.setup/idb-persistence-BHTYPRe_.mjs +233 -0
  23. package/dist/.setup/idb-persistence-lock-Bh3ZLIsE.mjs +14 -0
  24. package/dist/.setup/{messages-B1CZqU8u.mjs → messages-_XHVg3do.mjs} +3 -18
  25. package/dist/.setup/{monitor-Dk_fQL1k.mjs → monitor-DRUSJ_Ev.mjs} +31 -39
  26. package/dist/.setup/{onboarding-CrSexor_.mjs → onboarding-CK9Ksgio.mjs} +3 -3
  27. package/dist/.setup/{outbound-LR3SqPIq.mjs → outbound-D5Z_4FBg.mjs} +28 -36
  28. package/dist/.setup/{profile-update-C7bjaGR6.mjs → profile-update--Pf3jCXD.mjs} +2 -2
  29. package/dist/.setup/{reaction-events-CTSvOlXO.mjs → reaction-events-BG9spedG.mjs} +6 -6
  30. package/dist/.setup/{resolve-targets-BVVbbvP6.mjs → resolve-targets-BJ5sIKeM.mjs} +1 -1
  31. package/dist/.setup/{retired-state-DW5pLH05.mjs → retired-state-DOCruzWd.mjs} +10 -10
  32. package/dist/.setup/{room-info-9Uqlpt1V.mjs → room-info-BE7bbX_c.mjs} +3 -3
  33. package/dist/.setup/runtime-setter-api-i2MOXoT3.mjs +2 -0
  34. package/dist/.setup/{sdk-Cfc6m7fY.mjs → sdk-ByU8So5S.mjs} +242 -139
  35. package/dist/.setup/secret-contract-DxyzKSzf.mjs +90 -0
  36. package/dist/.setup/secret-contract-api-Dukgvike.mjs +2 -0
  37. package/dist/.setup/{send-LCOuTVc1.mjs → send-BprM-Wmx.mjs} +19 -42
  38. package/dist/.setup/{setup-bootstrap-CV-hHBXu.mjs → setup-bootstrap-DmznRqYe.mjs} +2 -6
  39. package/dist/.setup/{setup-core-CKnNTfgD.mjs → setup-core-DcCk1_Tv.mjs} +4 -7
  40. package/dist/.setup/setup-plugin-api-BU6UGksW.mjs +2 -0
  41. package/dist/.setup/{startup-verification-CgfUb4yJ.mjs → startup-verification-UU8DUvi4.mjs} +5 -13
  42. package/dist/.setup/state-layout-walk-BQMgMykU.mjs +27 -0
  43. package/dist/.setup/{storage-DkC6bHgA.mjs → storage-l8V0XsGM.mjs} +7 -166
  44. package/dist/.setup/{subagent-hooks-D8jyeQsO.mjs → subagent-hooks-CSY4iwa_.mjs} +7 -6
  45. package/dist/.setup/{sync-cache-state-D4G-fi3t.mjs → sync-cache-state-Cnm4TCEI.mjs} +1 -1
  46. package/dist/.setup/thread-bindings-rrwQAS6R.mjs +620 -0
  47. package/dist/.setup/{thread-bindings-shared-CbsFNbAY.mjs → thread-bindings-shared-CPjUA0Ib.mjs} +26 -5
  48. package/dist/.setup/{tool-actions-DAp6_XSJ.mjs → tool-actions-DBrk6Yei.mjs} +9 -11
  49. package/dist/.setup/{verification-B3VswneT.mjs → verification--qUbmRd-.mjs} +2 -2
  50. package/dist/api.js +6 -6
  51. package/dist/auth-presence.js +16 -2
  52. package/dist/channel-plugin-api.js +1 -2
  53. package/dist/cli-metadata.js +1 -2
  54. package/dist/contract-api.js +2 -2
  55. package/dist/doctor-contract-api.js +20 -27
  56. package/dist/index.js +38 -12
  57. package/dist/plugin-entry.handlers.runtime.js +32 -5
  58. package/dist/runtime-api.js +3 -3
  59. package/dist/runtime-setter-api.js +1 -2
  60. package/dist/secret-contract-api.js +1 -2
  61. package/dist/session-binding-contract-api.js +1 -1
  62. package/dist/setup-entry.js +3 -3
  63. package/dist/setup-plugin-api.js +1 -2
  64. package/dist/subagent-hooks-api.js +1 -1
  65. package/dist/thread-binding-api.js +1 -2
  66. package/dist/thread-bindings-runtime.js +1 -2
  67. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +148 -0
  68. package/node_modules/@openclaw/fs-safe/README.md +20 -4
  69. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -1
  70. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  71. package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
  72. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  73. package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
  74. package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
  75. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
  76. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +2 -2
  77. package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +1 -0
  78. package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +9 -2
  79. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.d.ts +2 -0
  80. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +2 -1
  81. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +6 -0
  82. package/node_modules/@openclaw/fs-safe/dist/config.js +5 -0
  83. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +3 -1
  84. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +8 -7
  85. package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
  86. package/node_modules/@openclaw/fs-safe/dist/copy-link.js +170 -0
  87. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
  88. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
  89. package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +11 -2
  90. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.d.ts +2 -4
  91. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +4 -2
  92. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
  93. package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
  94. package/node_modules/@openclaw/fs-safe/dist/create.js +1 -1
  95. package/node_modules/@openclaw/fs-safe/dist/creation-boundary.js +7 -4
  96. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
  97. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
  98. package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +7 -4
  99. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +20 -5
  100. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
  101. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
  102. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.d.ts +7 -7
  103. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +9 -4
  104. package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +2 -1
  105. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +2 -2
  106. package/node_modules/@openclaw/fs-safe/dist/exact-identity.d.ts +17 -0
  107. package/node_modules/@openclaw/fs-safe/dist/exact-identity.js +29 -0
  108. package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
  109. package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +1 -1
  110. package/node_modules/@openclaw/fs-safe/dist/file-hash.js +11 -9
  111. package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +7 -1
  112. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +27 -5
  113. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +1 -2
  114. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -1
  115. package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
  116. package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
  117. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
  118. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
  119. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +1 -0
  120. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +22 -15
  121. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
  122. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
  123. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
  124. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -0
  125. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -0
  126. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
  127. package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.js +24 -13
  128. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +85 -18
  129. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +5 -0
  130. package/node_modules/@openclaw/fs-safe/dist/native-config.js +11 -0
  131. package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +6 -4
  132. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.d.ts +2 -1
  133. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.js +26 -2
  134. package/node_modules/@openclaw/fs-safe/dist/native-guidance.d.ts +3 -0
  135. package/node_modules/@openclaw/fs-safe/dist/native-guidance.js +21 -0
  136. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
  137. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
  138. package/node_modules/@openclaw/fs-safe/dist/native-operations.js +8 -6
  139. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +44 -4
  140. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +85 -15
  141. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +3 -3
  142. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +33 -6
  143. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
  144. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +22 -3
  145. package/node_modules/@openclaw/fs-safe/dist/native-policy-directory-observation.js +4 -3
  146. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
  147. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
  148. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.d.ts +3 -0
  149. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.js +16 -0
  150. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
  151. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +15 -5
  152. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.d.ts +3 -0
  153. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.js +7 -0
  154. package/node_modules/@openclaw/fs-safe/dist/native-status.d.ts +17 -0
  155. package/node_modules/@openclaw/fs-safe/dist/native-status.js +30 -0
  156. package/node_modules/@openclaw/fs-safe/dist/native.d.ts +31 -4
  157. package/node_modules/@openclaw/fs-safe/dist/native.js +107 -9
  158. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
  159. package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
  160. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +1 -1
  161. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -1
  162. package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +9 -1
  163. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
  164. package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
  165. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +83 -47
  166. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +2 -0
  167. package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +8 -3
  168. package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +13 -0
  169. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +10 -8
  170. package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
  171. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +7 -5
  172. package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
  173. package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
  174. package/node_modules/@openclaw/fs-safe/dist/private-directory.js +1 -1
  175. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
  176. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +3 -2
  177. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +38 -10
  178. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
  179. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +3 -0
  180. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +3 -0
  181. package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +5 -3
  182. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +26 -20
  183. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -0
  184. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +3 -4
  185. package/node_modules/@openclaw/fs-safe/dist/realpath.js +32 -0
  186. package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +4 -0
  187. package/node_modules/@openclaw/fs-safe/dist/regular-file.js +11 -15
  188. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +3 -2
  189. package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
  190. package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
  191. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +8 -13
  192. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +252 -27
  193. package/node_modules/@openclaw/fs-safe/dist/root-directory-creation.js +5 -1
  194. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +11 -8
  195. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +35 -13
  196. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.d.ts +8 -0
  197. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.js +108 -0
  198. package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +7 -0
  199. package/node_modules/@openclaw/fs-safe/dist/root-file.js +15 -0
  200. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +2 -0
  201. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +154 -175
  202. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.d.ts +2 -0
  203. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.js +239 -0
  204. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +37 -15
  205. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.d.ts +32 -0
  206. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.js +56 -0
  207. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
  208. package/node_modules/@openclaw/fs-safe/dist/root-public-types.d.ts +1 -0
  209. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +95 -16
  210. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +6 -4
  211. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +23 -19
  212. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -6
  213. package/node_modules/@openclaw/fs-safe/dist/root-write-publication.js +5 -1
  214. package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +3 -1
  215. package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +2 -0
  216. package/node_modules/@openclaw/fs-safe/dist/secure-file.js +10 -4
  217. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
  218. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +2 -2
  219. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
  220. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
  221. package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +13 -0
  222. package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +29 -0
  223. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +5 -3
  224. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.d.ts +8 -0
  225. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +13 -2
  226. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +71 -3
  227. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +2 -1
  228. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +22 -0
  229. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-types.d.ts +3 -0
  230. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
  231. package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
  232. package/node_modules/@openclaw/fs-safe/dist/trash.js +2 -2
  233. package/node_modules/@openclaw/fs-safe/dist/types.d.ts +4 -0
  234. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
  235. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.d.ts +5 -0
  236. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.js +39 -0
  237. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +1 -0
  238. package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +25 -36
  239. package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +24 -5
  240. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +2 -1
  241. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +4 -0
  242. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.d.ts +2 -0
  243. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
  244. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.js +7 -0
  245. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
  246. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
  247. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.d.ts +1 -0
  248. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.js +12 -10
  249. package/node_modules/@openclaw/fs-safe/docs/advanced.md +2 -1
  250. package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
  251. package/node_modules/@openclaw/fs-safe/docs/config.md +71 -1
  252. package/node_modules/@openclaw/fs-safe/docs/contributing.md +45 -5
  253. package/node_modules/@openclaw/fs-safe/docs/copy.md +45 -5
  254. package/node_modules/@openclaw/fs-safe/docs/durability.md +34 -7
  255. package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
  256. package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
  257. package/node_modules/@openclaw/fs-safe/docs/install.md +47 -27
  258. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -6
  259. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +1 -1
  260. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +79 -6
  261. package/node_modules/@openclaw/fs-safe/docs/native.md +279 -8
  262. package/node_modules/@openclaw/fs-safe/docs/permissions.md +64 -15
  263. package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
  264. package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
  265. package/node_modules/@openclaw/fs-safe/docs/public-api.md +10 -0
  266. package/node_modules/@openclaw/fs-safe/docs/queue-link-free-design.md +237 -0
  267. package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
  268. package/node_modules/@openclaw/fs-safe/docs/reading.md +56 -2
  269. package/node_modules/@openclaw/fs-safe/docs/regular-file.md +2 -0
  270. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
  271. package/node_modules/@openclaw/fs-safe/docs/secure-file.md +1 -1
  272. package/node_modules/@openclaw/fs-safe/docs/security-model.md +35 -0
  273. package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
  274. package/node_modules/@openclaw/fs-safe/docs/staged-file.md +18 -0
  275. package/node_modules/@openclaw/fs-safe/docs/store.md +15 -0
  276. package/node_modules/@openclaw/fs-safe/docs/temp.md +39 -0
  277. package/node_modules/@openclaw/fs-safe/docs/testing.md +13 -0
  278. package/node_modules/@openclaw/fs-safe/docs/types.md +51 -81
  279. package/node_modules/@openclaw/fs-safe/docs/walk.md +4 -1
  280. package/node_modules/@openclaw/fs-safe/docs/watch.md +3 -2
  281. package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
  282. package/node_modules/@openclaw/fs-safe/package.json +21 -12
  283. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  284. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  285. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  286. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  287. package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
  288. package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
  289. package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
  290. package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
  291. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  292. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  293. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  294. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  295. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  296. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  297. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  298. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  299. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
  300. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
  301. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  302. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  303. package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
  304. package/node_modules/typebox/package.json +1 -1
  305. package/openclaw.plugin.json +3 -0
  306. package/package.json +7 -6
  307. package/dist/.setup/runtime-setter-api-DtxDAM-x.mjs +0 -3
  308. package/dist/.setup/secret-contract-DSBRvhdZ.mjs +0 -95
  309. package/dist/.setup/secret-contract-api-DzEJ-X7n.mjs +0 -3
  310. package/dist/.setup/setup-plugin-api-BDAYk1a5.mjs +0 -3
  311. package/dist/.setup/thread-bindings-CT4P-OSZ.mjs +0 -365
  312. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +0 -336
  313. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +0 -90
@@ -5,6 +5,7 @@ import { FsSafeError } from "./errors.js";
5
5
  import { sameFileIdentity } from "./file-identity.js";
6
6
  import { realpathSync } from "./realpath.js";
7
7
  import { resolveRootPathSync, ROOT_PATH_ALIAS_POLICIES } from "./root-path.js";
8
+ import { hasWindowsPathAlias } from "./windows-path-alias.js";
8
9
  function admitUncachedCopyPath(input, create = false, admittedRoot) {
9
10
  if (!path.isAbsolute(input.rootPath) || !path.isAbsolute(input.absolutePath)) {
10
11
  throw new FsSafeError("invalid-path", "copy roots and paths must be absolute");
@@ -50,15 +51,23 @@ export function createCopyPathAdmissionCache() {
50
51
  const assertParent = ({ root, parent, chain }) => {
51
52
  assertSyncDirectoryGuard(root);
52
53
  for (const guard of chain) {
54
+ // The full parent guard immediately below includes this exact identity.
55
+ if (guard === parent)
56
+ continue;
53
57
  inspectDirectoryIdentitySync(guard.dir, { dev: BigInt(guard.stat.dev), ino: BigInt(guard.stat.ino) });
54
58
  }
55
- assertSyncDirectoryGuard(parent);
59
+ if (parent !== root)
60
+ assertSyncDirectoryGuard(parent);
56
61
  assertSyncDirectoryGuard(root);
57
62
  };
58
63
  const admit = (input, create = false) => {
59
64
  // Keep the public resolver for Windows aliases and noncanonical spellings,
60
65
  // including raw dot segments whose traversal must not be normalized away.
61
- if (process.platform === "win32" || !path.isAbsolute(input.rootPath) ||
66
+ const ordinaryWindowsPath = process.platform !== "win32" || (/^[a-z]:\\/iu.test(input.rootPath) &&
67
+ !hasWindowsPathAlias(input.rootPath, "filesystem") &&
68
+ !hasWindowsPathAlias(input.absolutePath, "filesystem") &&
69
+ !input.absolutePath.slice(3).split("\\").some(segment => /[. ]$/u.test(segment) || segment.includes("~")));
70
+ if (!ordinaryWindowsPath || !path.isAbsolute(input.rootPath) ||
62
71
  !path.isAbsolute(input.absolutePath) || input.rootPath.includes("\0") || input.absolutePath.includes("\0") ||
63
72
  path.resolve(input.rootPath) !== input.rootPath || path.resolve(input.absolutePath) !== input.absolutePath ||
64
73
  !input.absolutePath.startsWith(input.rootPath.endsWith(path.sep) ? input.rootPath : `${input.rootPath}${path.sep}`)) {
@@ -1,13 +1,11 @@
1
+ import { type ExactIdentityReceipt, type ExactFileIdentity } from "./file-identity.js";
1
2
  import { type CopyPath } from "./copy-path-admission.js";
2
3
  import { type CopyCloneMode } from "./copy-policy.js";
3
4
  import { type OwnedFileDescriptorSync } from "./create-owned-file.js";
4
5
  export type CopyRootFileSyncOptions = {
5
6
  source: CopyPath;
6
7
  destination: CopyPath;
7
- expectedSourceIdentity?: {
8
- dev: bigint;
9
- ino: bigint;
10
- };
8
+ expectedSourceIdentity?: ExactFileIdentity | ExactIdentityReceipt;
11
9
  clone?: CopyCloneMode;
12
10
  maxBytes?: number;
13
11
  mode?: number;
@@ -1,3 +1,4 @@
1
+ import { identityFromReceipt } from "./file-identity.js";
1
2
  import fs, {} from "node:fs";
2
3
  import path from "node:path";
3
4
  import { normalizeMaxBytes } from "./byte-budget.js";
@@ -50,7 +51,8 @@ function copyRootFileWithAdmissionSync(options, cache) {
50
51
  const targetInput = options.destination;
51
52
  const targetPath = { rootPath: targetInput.rootPath, absolutePath: targetInput.absolutePath };
52
53
  const expectedInput = options.expectedSourceIdentity;
53
- const expectedSourceIdentity = expectedInput === undefined ? undefined : { dev: expectedInput.dev, ino: expectedInput.ino };
54
+ const expected = expectedInput === undefined ? undefined : identityFromReceipt(expectedInput);
55
+ const expectedSourceIdentity = expected && { dev: expected.dev, ino: expected.ino };
54
56
  const clone = resolveCopyCloneMode(options.clone, "never");
55
57
  const maxBytes = normalizeMaxBytes(options.maxBytes);
56
58
  const mode = options.mode;
@@ -68,7 +70,7 @@ function copyRootFileWithAdmissionSync(options, cache) {
68
70
  let parentOpen = false;
69
71
  let target;
70
72
  let identity;
71
- const native = getNativeBinding();
73
+ const native = process.platform !== "win32" ? getNativeBinding("copyFileExclusiveSync", "removeStagedFile") : getNativeBinding();
72
74
  const nativeCopy = process.platform !== "win32" && native?.copyFileExclusiveSync && native.removeStagedFile
73
75
  ? native.copyFileExclusiveSync.bind(native) : undefined;
74
76
  try {
@@ -4,12 +4,9 @@ import fsp, {} from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { FsSafeError } from "./errors.js";
6
6
  import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
7
+ import { timestampSeconds } from "./copy-metadata.js";
8
+ import { readCopyLinkTarget } from "./copy-link.js";
7
9
  import { assertStagedDirectoryCurrent, exactIdentityMatches, openStagedDirectory, } from "./staged-directory.js";
8
- function timestampSeconds(nanoseconds) {
9
- // Dates discard sub-millisecond precision; negative numbers mean "now" in
10
- // Node's utimes API. Numeric strings retain fractional, pre-epoch timestamps.
11
- return String(Number(nanoseconds / 1000000000n) + Number(nanoseconds % 1000000000n) / 1e9);
12
- }
13
10
  /** Byte-copy adapter for the shared immutable-source, caller-owned namespace contract. */
14
11
  export async function copyOwnedTree(source, destination, options) {
15
12
  const callerSignal = options.signal ? AbortSignal.any([options.signal]) : undefined;
@@ -145,7 +142,7 @@ export async function copyOwnedTree(source, destination, options) {
145
142
  else if (child.isSymbolicLink()) {
146
143
  const link = process.platform === "win32"
147
144
  ? await fsp.readlink(childSource)
148
- : await fsp.readlink(childSource, { encoding: "buffer" });
145
+ : await readCopyLinkTarget(childSource);
149
146
  let type;
150
147
  if (process.platform === "win32") {
151
148
  // Older Node releases infer link type from the destination, where a
@@ -21,7 +21,7 @@ function closeDescriptor(fd, failure) {
21
21
  }
22
22
  /** Inspect an existing real directory without creating probe files. */
23
23
  export function probeTreeClone(parentPath) {
24
- const native = getNativeBinding();
24
+ const native = getNativeBinding("probeTreeClone");
25
25
  if (!native)
26
26
  return undefined;
27
27
  const parent = openStagedDirectory(assertAbsolutePathInput(parentPath));
@@ -64,9 +64,9 @@ async function materializeTree(source, destination, options, policy) {
64
64
  throw new FsSafeError("invalid-path", "clone concurrency must be an integer between 1 and 32");
65
65
  }
66
66
  const native = policy === "always"
67
- ? requireNativeBinding()
67
+ ? requireNativeBinding("probeTreeClone", "cloneTree")
68
68
  : policy === "auto"
69
- ? getNativeBinding()
69
+ ? getNativeBinding("probeTreeClone", "cloneTree")
70
70
  : undefined;
71
71
  const target = assertAbsolutePathInput(destination);
72
72
  const name = path.basename(target);
@@ -204,7 +204,7 @@ function privateDirectoryBackend() {
204
204
  if (getFsSafeNativeConfig().mode === "require") {
205
205
  throw new FsSafeError("helper-unavailable", "private directory creation requires an up-to-date native parent identity capability");
206
206
  }
207
- warnNativeFallback("windows-private-directory", "Private Windows directory creation uses a slower built-in system command.");
207
+ warnNativeFallback("windows-private-directory", "Private Windows directory creation launches the packaged .NET helper for each check or creation.", "createPrivateDirectoryWithParentIdentity");
208
208
  return undefined;
209
209
  }
210
210
  export async function createDirectory(targetPath, options = {}) {
@@ -53,17 +53,17 @@ export function assertPrivateFileCreationAvailable() {
53
53
  typeof native.verifyPrivateWindowsFile !== "function")
54
54
  unavailable();
55
55
  }
56
- function commandFallback() {
56
+ function commandFallback(capability) {
57
57
  if (getFsSafeNativeConfig().mode === "require")
58
58
  unavailable();
59
- warnNativeFallback("windows-private-creation", "Private Windows creation uses a slower built-in system command.");
59
+ warnNativeFallback("windows-private-creation", "Private Windows creation launches the packaged .NET helper for each check or creation.", capability);
60
60
  }
61
61
  function creationOperation(name) {
62
62
  const native = getNativeBinding();
63
63
  const operation = native?.[name];
64
64
  if (typeof operation === "function")
65
65
  return operation.bind(native);
66
- commandFallback();
66
+ commandFallback(name);
67
67
  return undefined;
68
68
  }
69
69
  /** Full Windows identity, with security validation when requested; no repairs. */
@@ -300,7 +300,10 @@ export function assertBeforeCreation(selected, permissions, assertion, kind) {
300
300
  if (permissions.private)
301
301
  assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, kind);
302
302
  assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
303
- selected.assertParent();
303
+ // Ordinary callers enter immediately after prepareCreationPath's identical fence.
304
+ // A callback or private ACL inspection needs a fresh observation afterward.
305
+ if (assertion !== undefined || permissions.private)
306
+ selected.assertParent();
304
307
  if (permissions.private && kind === "directory")
305
308
  assertDarwinPrivateDirectoryMode(permissions.mode);
306
309
  if (permissions.private)
@@ -0,0 +1,12 @@
1
+ export type DarwinAclInspection = {
2
+ kind: "none";
3
+ } | {
4
+ kind: "present";
5
+ inheritsToFiles: boolean;
6
+ inheritsToDirectories: boolean;
7
+ } | {
8
+ kind: "unknown";
9
+ reason: string;
10
+ };
11
+ /** Inspect one no-follow descriptor. Unknown is never evidence of no ACL. */
12
+ export declare function inspectDarwinAcl(path: string): DarwinAclInspection;
@@ -0,0 +1,29 @@
1
+ import fs from "node:fs";
2
+ import { getNativeBinding } from "./native.js";
3
+ /** Inspect one no-follow descriptor. Unknown is never evidence of no ACL. */
4
+ export function inspectDarwinAcl(path) {
5
+ if (process.platform !== "darwin")
6
+ return { kind: "unknown", reason: "unsupported-platform" };
7
+ let fd;
8
+ try {
9
+ const native = getNativeBinding();
10
+ if (!native?.inspectDarwinAcl)
11
+ return { kind: "unknown", reason: "helper-unavailable" };
12
+ fd = fs.openSync(path, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
13
+ const facts = native.inspectDarwinAcl(fd);
14
+ if (facts.state === "absent" || facts.state === "empty")
15
+ return { kind: "none" };
16
+ if (facts.state === "present" && typeof facts.inheritsToFiles === "boolean" &&
17
+ typeof facts.inheritsToDirectories === "boolean") {
18
+ return { kind: "present", inheritsToFiles: facts.inheritsToFiles, inheritsToDirectories: facts.inheritsToDirectories };
19
+ }
20
+ return { kind: "unknown", reason: "incomplete-acl-facts" };
21
+ }
22
+ catch (error) {
23
+ return { kind: "unknown", reason: error instanceof Error ? error.message : String(error) };
24
+ }
25
+ finally {
26
+ if (fd !== undefined)
27
+ fs.closeSync(fd);
28
+ }
29
+ }
@@ -1,5 +1,6 @@
1
+ import type { ExactFileIdentity, ExactIdentityReceipt } from "./exact-identity.js";
1
2
  import { type BigIntStats, type Stats } from "node:fs";
2
- import type { FileIdentityStat } from "./file-identity.js";
3
+ import type { FileIdentityInput } from "./file-identity.js";
3
4
  export type DirectorySyncOutcome = {
4
5
  status: "synced";
5
6
  } | {
@@ -10,14 +11,16 @@ export type DirectoryReceipt<T extends Stats | BigIntStats = Stats> = {
10
11
  path: string;
11
12
  realPath: string;
12
13
  identity: T;
14
+ exactIdentity?: ExactFileIdentity;
13
15
  };
16
+ export type ExactDirectoryReceipt = DirectoryReceipt & ExactIdentityReceipt;
14
17
  export type DurableDirectoryReceipt = DirectoryReceipt & {
15
18
  parentSync: DirectorySyncOutcome | {
16
19
  status: "not-needed";
17
20
  };
18
21
  };
19
22
  export type PinnedDirectory = {
20
- readonly receipt: DirectoryReceipt;
23
+ readonly receipt: ExactDirectoryReceipt;
21
24
  assertCurrent(): Promise<void>;
22
25
  sync(): Promise<DirectorySyncOutcome>;
23
26
  close(): Promise<void>;
@@ -26,7 +29,7 @@ export type EnsureDurableDirectoryOptions = {
26
29
  directoryPath: string;
27
30
  label?: string;
28
31
  mode?: number;
29
- expectedExistingIdentity?: FileIdentityStat;
32
+ expectedExistingIdentity?: FileIdentityInput;
30
33
  create?: (directoryPath: string) => Promise<void>;
31
34
  };
32
35
  export declare function pinDirectory(directory: string | DirectoryReceipt<Stats | BigIntStats>, options?: {
@@ -40,4 +43,4 @@ export declare function syncDirectorySync(directory: string | DirectoryReceipt<S
40
43
  }): DirectorySyncOutcome;
41
44
  export declare function syncDirectoryBestEffort(directoryPath: string): Promise<void>;
42
45
  export declare function syncDirectoryBestEffortSync(directoryPath: string): void;
43
- export declare function ensureDurableDirectory(options: EnsureDurableDirectoryOptions): Promise<DurableDirectoryReceipt>;
46
+ export declare function ensureDurableDirectory(options: EnsureDurableDirectoryOptions): Promise<DurableDirectoryReceipt & ExactIdentityReceipt>;
@@ -1,7 +1,7 @@
1
1
  import fsSync from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
- import { sameFileIdentity } from "./file-identity.js";
4
+ import { hasSafeFileIdentity, sameFileIdentity } from "./file-identity.js";
5
5
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
6
6
  import { isNotFoundPathError } from "./path.js";
7
7
  import { directoryComponentNotDirectoryError } from "./root-errors.js";
@@ -9,6 +9,9 @@ import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservi
9
9
  import { realpathSync } from "./realpath.js";
10
10
  import { directoryEntryPath } from "./directory-entry-path.js";
11
11
  import { assertStatObservationSync, inspectStatObservationSync, } from "./stat-observation.js";
12
+ // Numeric metadata is retained for existing internal consumers; rounded IDs
13
+ // must never become authority for a later comparison.
14
+ const exactGuardIdentities = new WeakMap();
12
15
  export async function readDirectoryIdentity(dir) {
13
16
  const guard = await createAsyncDirectoryGuard(dir, { bigint: true });
14
17
  return Object.freeze({ dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
@@ -43,9 +46,19 @@ export function captureDirectoryGuard(dir, mode, options) {
43
46
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
44
47
  throw directoryComponentNotDirectoryError();
45
48
  }
49
+ let exactIdentity;
50
+ if (!hasSafeFileIdentity(stat)) {
51
+ const exact = fsSync.lstatSync(operationPath, { bigint: true });
52
+ if (exact.isSymbolicLink() || !exact.isDirectory())
53
+ throw directoryComponentNotDirectoryError();
54
+ exactIdentity = { dev: exact.dev, ino: exact.ino };
55
+ }
46
56
  const realPath = mode === "native" ? realpathSync.native(operationPath) : realpathSync(operationPath);
47
57
  assertNoWindowsPathAlias(realPath, "filesystem");
48
- return { dir, realPath, stat };
58
+ const guard = { dir, realPath, stat };
59
+ if (exactIdentity)
60
+ exactGuardIdentities.set(guard, exactIdentity);
61
+ return guard;
49
62
  }
50
63
  export async function assertAsyncDirectoryGuard(guard) {
51
64
  assertDirectoryGuard(guard, "native");
@@ -59,9 +72,11 @@ export function assertDirectoryGuard(guard, mode) {
59
72
  const expectedRealPath = guard.realPath;
60
73
  assertNoWindowsPathAlias(expectedRealPath, "filesystem");
61
74
  const expectedStat = guard.stat;
62
- const expectedIdentity = { dev: expectedStat.dev, ino: expectedStat.ino };
75
+ const suppliedIdentity = { dev: expectedStat.dev, ino: expectedStat.ino };
76
+ const retainedIdentity = exactGuardIdentities.get(guard);
77
+ const expectedIdentity = retainedIdentity ?? suppliedIdentity;
63
78
  const stat = typeof expectedIdentity.dev === "bigint" && typeof expectedIdentity.ino === "bigint"
64
- ? inspectDirectoryIdentityAtPathSync(operationPath, {
79
+ ? retainedIdentity ? fsSync.lstatSync(operationPath, { bigint: true }) : inspectDirectoryIdentityAtPathSync(operationPath, {
65
80
  dev: expectedIdentity.dev,
66
81
  ino: expectedIdentity.ino,
67
82
  })
@@ -74,7 +89,7 @@ export function assertDirectoryGuard(guard, mode) {
74
89
  if (mode === "native" && !sameFileIdentity(stat, expectedIdentity)) {
75
90
  throw new FsSafeError("path-mismatch", "directory changed during operation");
76
91
  }
77
- const realPath = mode === "native" || typeof expectedIdentity.ino === "bigint"
92
+ const realPath = mode === "native" || typeof suppliedIdentity.ino === "bigint"
78
93
  ? realpathSync.native(operationPath) : realpathSync(operationPath);
79
94
  assertNoWindowsPathAlias(realPath, "filesystem");
80
95
  if ((mode === "normalized" && !sameFileIdentity(stat, expectedIdentity)) || realPath !== expectedRealPath) {
@@ -5,6 +5,7 @@ export type DirectoryModeChecks = {
5
5
  };
6
6
  export type DirectoryModeOwner = {
7
7
  verify(check?: () => void): Promise<void>;
8
+ verifyCanonical?(): Promise<boolean>;
8
9
  apply(mode: number, checks?: DirectoryModeChecks): Promise<void>;
9
10
  close(): Promise<void>;
10
11
  };
@@ -12,6 +13,7 @@ export declare function assertOwnedDirectory(expected: Stats | BigIntStats, actu
12
13
  /** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
13
14
  export declare function ownDirectoryMode(params: {
14
15
  inspect: () => Promise<number>;
16
+ inspectCanonical?: () => number | undefined;
15
17
  chmod: (mode: number) => Promise<void>;
16
18
  prepareChmod?: () => Promise<void>;
17
19
  verifyChmod?: () => Promise<void>;
@@ -28,6 +30,7 @@ export declare function nodeDirectorySearchOnlyFlags(): {
28
30
  export declare function pinNodeDirectoryForMode(dirPath: string, options?: {
29
31
  expectedIdentity?: BigIntStats;
30
32
  ownerUid?: number;
33
+ canonicalPath?: string;
31
34
  }): Promise<DirectoryModeOwner>;
32
35
  /** Synchronous counterpart with the same no-follow, identity, and owner checks. */
33
36
  export declare function pinNodeDirectoryForModeSync(dirPath: string, options?: {
@@ -4,6 +4,8 @@ import { FsSafeError } from "./errors.js";
4
4
  import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
5
5
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
6
  import { sameFileIdentityForCleanup } from "./file-identity.js";
7
+ import { getNativeBinding } from "./native.js";
8
+ import { nativePolicyDirectoryObserver } from "./native-policy-directory-observation.js";
7
9
  export function assertOwnedDirectory(expected, actual) {
8
10
  if (actual.isSymbolicLink() || !actual.isDirectory()) {
9
11
  throw new FsSafeError("not-file", "directory mode target must be a real directory");
@@ -23,7 +25,7 @@ export function ownDirectoryMode(params) {
23
25
  pending = operation.catch(() => undefined);
24
26
  return operation;
25
27
  };
26
- return {
28
+ const owner = {
27
29
  verify: (check) => enqueue(async () => {
28
30
  check?.();
29
31
  await params.inspect();
@@ -79,6 +81,14 @@ export function ownDirectoryMode(params) {
79
81
  return closing;
80
82
  },
81
83
  };
84
+ if (params.inspectCanonical) {
85
+ owner.verifyCanonical = async () => {
86
+ let verified = false;
87
+ await enqueue(async () => { verified = params.inspectCanonical() !== undefined; });
88
+ return verified;
89
+ };
90
+ }
91
+ return owner;
82
92
  }
83
93
  /** Darwin descriptor inspection avoids requesting directory-content reads. */
84
94
  export function nodeDarwinDirectoryMetadataFlags() {
@@ -131,7 +141,11 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
131
141
  proc = route.proc;
132
142
  return await fs.open(dirPath, route.flags | flags);
133
143
  });
144
+ let observer;
134
145
  try {
146
+ observer = options.canonicalPath === dirPath && ownerUid === undefined
147
+ ? nativePolicyDirectoryObserver(getNativeBinding(), handle.fd, dirPath)
148
+ : undefined;
135
149
  const inspect = async () => {
136
150
  const opened = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), expected);
137
151
  assertOwnedDirectory(expected, opened);
@@ -153,8 +167,18 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
153
167
  assertOwner(opened);
154
168
  assertOwner(followed);
155
169
  };
170
+ const observeCanonical = observer;
156
171
  const owner = ownDirectoryMode({
157
172
  inspect,
173
+ inspectCanonical: observeCanonical ? () => {
174
+ const observed = observeCanonical();
175
+ if (!observed)
176
+ return undefined;
177
+ if (observed.identity.dev !== expected.dev || observed.identity.ino !== expected.ino) {
178
+ throw new FsSafeError("path-mismatch", "directory changed before its mode could be applied");
179
+ }
180
+ return Number(observed.identity.mode & 4095n);
181
+ } : undefined,
158
182
  prepareChmod: proc ? assertProcAuthority : undefined,
159
183
  verifyChmod: proc ? assertProcAuthority : undefined,
160
184
  async chmod(mode) {
@@ -165,12 +189,13 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
165
189
  await handle.chmod(mode);
166
190
  }
167
191
  },
168
- close: () => handle.close(),
192
+ close: () => { observer?.dispose(); return handle.close(); },
169
193
  });
170
194
  await owner.verify();
171
195
  return owner;
172
196
  }
173
197
  catch (error) {
198
+ observer?.dispose();
174
199
  try {
175
200
  await handle.close();
176
201
  }
@@ -1,6 +1,6 @@
1
1
  import { type BigIntStats, type Stats } from "node:fs";
2
- import type { DirectoryReceipt } from "./directory-durability.js";
3
- import type { FileIdentityStat } from "./file-identity.js";
2
+ import type { DirectoryReceipt, ExactDirectoryReceipt } from "./directory-durability.js";
3
+ import { type FileIdentityInput } from "./file-identity.js";
4
4
  type ExactIdentity = Readonly<Pick<BigIntStats, "dev" | "ino">>;
5
5
  declare const numericFields: readonly ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
6
6
  declare const timeFields: readonly ["atime", "mtime", "ctime", "birthtime"];
@@ -13,11 +13,11 @@ type DirectoryAuthority = DirectoryProvenance & Readonly<{
13
13
  path: string;
14
14
  realPath: string;
15
15
  }>;
16
- export declare function directoryReceiptIdentity(identity: FileIdentityStat): ExactIdentity;
16
+ export declare function directoryReceiptIdentity(input: FileIdentityInput): ExactIdentity;
17
17
  export declare function directoryReceiptAuthority(receipt: DirectoryReceipt<Stats | BigIntStats>): DirectoryAuthority;
18
- export declare function ownDirectoryReceipt(receipt: DirectoryReceipt<Stats | BigIntStats>): DirectoryReceipt;
19
- export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt): DirectoryReceipt;
20
- export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats): DirectoryReceipt;
21
- export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string): DirectoryReceipt;
18
+ export declare function ownDirectoryReceipt(receipt: DirectoryReceipt<Stats | BigIntStats>): ExactDirectoryReceipt;
19
+ export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt): ExactDirectoryReceipt;
20
+ export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats): ExactDirectoryReceipt;
21
+ export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string): ExactDirectoryReceipt;
22
22
  export declare function assertDirectoryReceiptCurrentSync(receipt: DirectoryReceipt, label: string, canonicalize?: (pathname: string) => string): void;
23
23
  export {};
@@ -1,6 +1,7 @@
1
1
  import fs, {} from "node:fs";
2
2
  import { inspectDirectoryIdentitySync } from "./directory-guard.js";
3
3
  import { FsSafeError } from "./errors.js";
4
+ import { identityFromReceipt } from "./file-identity.js";
4
5
  import { realpathSync } from "./realpath.js";
5
6
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
6
7
  const numericFields = ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
@@ -9,10 +10,11 @@ const timeFields = ["atime", "mtime", "ctime", "birthtime"];
9
10
  // fields and accept exact provenance only while its public identity is intact.
10
11
  const authorities = new WeakMap();
11
12
  const identities = new WeakMap();
12
- export function directoryReceiptIdentity(identity) {
13
- if (!identity) {
13
+ export function directoryReceiptIdentity(input) {
14
+ if (!input) {
14
15
  throw new FsSafeError("path-mismatch", "directory receipt identity is missing");
15
16
  }
17
+ const identity = identityFromReceipt(input);
16
18
  const known = identities.get(identity);
17
19
  const dev = identity.dev;
18
20
  const ino = identity.ino;
@@ -41,9 +43,11 @@ function snapshotDirectoryReceipt(receipt) {
41
43
  }
42
44
  assertNoWindowsPathAlias(pathname, "filesystem");
43
45
  assertNoWindowsPathAlias(realPath, "filesystem");
44
- const identity = directoryReceiptIdentity(stat);
46
+ const identity = directoryReceiptIdentity(receipt.exactIdentity ?? stat);
45
47
  const known = authorities.get(receipt);
46
- if (known && (pathname !== known.path || realPath !== known.realPath ||
48
+ const matches = (value, expected) => typeof value === "bigint" ? value === expected : value === Number(expected);
49
+ if (known && (!matches(stat.dev, known.identity.dev) || !matches(stat.ino, known.identity.ino) ||
50
+ pathname !== known.path || realPath !== known.realPath ||
47
51
  identity.dev !== known.identity.dev || identity.ino !== known.identity.ino)) {
48
52
  throw new FsSafeError("path-mismatch", "directory receipt changed before admission");
49
53
  }
@@ -79,6 +83,7 @@ export function copyRetainedDirectoryReceipt(receipt) {
79
83
  }
80
84
  function receiptFromAuthority(authority) {
81
85
  const receipt = {
86
+ exactIdentity: authority.identity,
82
87
  path: authority.path,
83
88
  realPath: authority.realPath,
84
89
  identity: Object.assign(Object.create(fs.Stats.prototype), authority.metadata),
@@ -1,4 +1,5 @@
1
- export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, type DirectoryReceipt, type DirectorySyncOutcome, type DurableDirectoryReceipt, type EnsureDurableDirectoryOptions, type PinnedDirectory, } from "./directory-durability.js";
1
+ export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, type DirectoryReceipt, type ExactDirectoryReceipt, type DirectorySyncOutcome, type DurableDirectoryReceipt, type EnsureDurableDirectoryOptions, type PinnedDirectory, } from "./directory-durability.js";
2
2
  export { isHardlinkFallbackError, publishFileExclusive, type PublishFileExclusiveResult, type PublishFileExclusiveStrategy, } from "./publish-file.js";
3
3
  export type { PublishFileExclusiveCleanup, PublishFileExclusiveDirectorySyncFailure, PublishFileExclusiveFailureDetails, PublishFileExclusiveFailurePhase, PublishFileExclusiveSyncFailurePolicy, } from "./publish-file-failure.js";
4
4
  export { sha256File, sha256FileSync, type Sha256FileInput, type Sha256FileSyncInput, type Sha256FileOptions, type Sha256FileResult, } from "./file-hash.js";
5
+ export type { ExactFileIdentity, ExactIdentityReceipt } from "./exact-identity.js";
@@ -18,7 +18,7 @@ function basename(name) {
18
18
  }
19
19
  function identity(value) {
20
20
  const { dev, ino } = value;
21
- if (typeof dev !== "bigint" || typeof ino !== "bigint" || dev < 0n || ino <= 0n ||
21
+ if (typeof dev !== "bigint" || typeof ino !== "bigint" || dev < -(1n << 63n) || ino < -(1n << 63n) || ino === 0n ||
22
22
  dev > 0xffffffffffffffffn || ino > 0xffffffffffffffffn) {
23
23
  throw new FsSafeError("path-mismatch", "publication requires an exact known identity");
24
24
  }
@@ -230,7 +230,7 @@ export function retainEntryForPublication(options) {
230
230
  inspectEntry(sourcePath, source.expected);
231
231
  const sourceFd = kind === "symlink"
232
232
  ? binding.openStagedSymlink(sourceParent.fd, source.basename)
233
- : binding.openBeneath(sourceParent.fd, source.basename, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK |
233
+ : native.openBeneath(sourceParent.fd, source.basename, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK |
234
234
  (kind === "directory" ? fs.constants.O_DIRECTORY : 0))?.fd;
235
235
  if (!Number.isInteger(sourceFd) || sourceFd < 0) {
236
236
  throw new FsSafeError("helper-unavailable", "publication source descriptor is unavailable");
@@ -0,0 +1,17 @@
1
+ import { type BigIntStats, type Stats } from "node:fs";
2
+ export type ExactFileIdentity = Readonly<{
3
+ dev: bigint;
4
+ ino: bigint;
5
+ }>;
6
+ export type ExactIdentityReceipt = {
7
+ readonly exactIdentity: ExactFileIdentity;
8
+ };
9
+ /** Copy scalars from one observation; never try to recover rounded identity bits. */
10
+ export declare function exactFileIdentity(stat: {
11
+ dev: number | bigint;
12
+ ino: number | bigint;
13
+ }): ExactFileIdentity;
14
+ /** Node stores unsigned libuv fields in a signed BigInt64Array. */
15
+ export declare function numberStatScalar(value: number | bigint): number;
16
+ /** Legacy metadata view of the very same bigint observation. */
17
+ export declare function numberStats(stat: BigIntStats): Stats;
@@ -0,0 +1,29 @@
1
+ import fs, {} from "node:fs";
2
+ import { FsSafeError } from "./errors.js";
3
+ /** Copy scalars from one observation; never try to recover rounded identity bits. */
4
+ export function exactFileIdentity(stat) {
5
+ const { dev, ino } = stat;
6
+ if ((typeof dev !== "bigint" && !Number.isSafeInteger(dev)) ||
7
+ (typeof ino !== "bigint" && !Number.isSafeInteger(ino))) {
8
+ throw new FsSafeError("path-mismatch", "file identity is not exact");
9
+ }
10
+ return Object.freeze({ dev: BigInt(dev), ino: BigInt(ino) });
11
+ }
12
+ /** Node stores unsigned libuv fields in a signed BigInt64Array. */
13
+ export function numberStatScalar(value) {
14
+ return typeof value === "bigint" ? Number(BigInt.asUintN(64, value)) : Number(value);
15
+ }
16
+ /** Legacy metadata view of the very same bigint observation. */
17
+ export function numberStats(stat) {
18
+ const result = Object.create(fs.Stats.prototype);
19
+ for (const field of ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"]) {
20
+ result[field] = numberStatScalar(stat[field]);
21
+ }
22
+ for (const field of ["atime", "mtime", "ctime", "birthtime"]) {
23
+ const ns = stat[`${field}Ns`];
24
+ const remainder = ((ns % 1000000000n) + 1000000000n) % 1000000000n;
25
+ result[`${field}Ms`] = Number((ns - remainder) / 1000000000n) * 1_000 + Number(remainder) / 1_000_000;
26
+ // Stats.prototype supplies Node's lazy, correctly rounded Date getters.
27
+ }
28
+ return result;
29
+ }
@@ -113,7 +113,8 @@ export async function copyFileHandle(source, target, options = {}) {
113
113
  const assertBeforeMutation = composeMutationAssertions(undefined, options.assertBeforeMutation);
114
114
  const sourceStat = await inspectFileIdentity(() => {
115
115
  signal?.throwIfAborted();
116
- return source.stat({ bigint: true });
116
+ const fd = source.fd;
117
+ return fd < 0 ? source.stat({ bigint: true }) : fs.fstatSync(fd, { bigint: true });
117
118
  });
118
119
  signal?.throwIfAborted();
119
120
  if (!sourceStat.isFile())
@@ -123,7 +124,8 @@ export async function copyFileHandle(source, target, options = {}) {
123
124
  }
124
125
  const targetStat = await inspectFileIdentity(() => {
125
126
  signal?.throwIfAborted();
126
- return target.stat({ bigint: true });
127
+ const fd = target.fd;
128
+ return fd < 0 ? target.stat({ bigint: true }) : fs.fstatSync(fd, { bigint: true });
127
129
  });
128
130
  signal?.throwIfAborted();
129
131
  if (!targetStat.isFile())
@@ -10,7 +10,7 @@ export type Sha256FileResult = {
10
10
  bytes: number;
11
11
  digest: string;
12
12
  };
13
- export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions): Promise<Sha256FileResult>;
13
+ export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions, ownsPosition?: boolean): Promise<Sha256FileResult>;
14
14
  export declare function sha256File(input: Sha256FileInput, options?: Sha256FileOptions): Promise<Sha256FileResult>;
15
15
  /** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
16
16
  export declare function sha256FileSync(input: Sha256FileSyncInput, options?: Sha256FileOptions): Sha256FileResult;