@openclaw/openshell-sandbox 2026.10.5-beta.4 → 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 (240) hide show
  1. package/README.md +1 -1
  2. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +148 -0
  3. package/node_modules/@openclaw/fs-safe/README.md +20 -4
  4. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -1
  5. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  6. package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
  7. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  8. package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
  9. package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
  10. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
  11. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +2 -2
  12. package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +1 -0
  13. package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +9 -2
  14. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.d.ts +2 -0
  15. package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +2 -1
  16. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +6 -0
  17. package/node_modules/@openclaw/fs-safe/dist/config.js +5 -0
  18. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +3 -1
  19. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +8 -7
  20. package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
  21. package/node_modules/@openclaw/fs-safe/dist/copy-link.js +170 -0
  22. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
  23. package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
  24. package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +11 -2
  25. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.d.ts +2 -4
  26. package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +4 -2
  27. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
  28. package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
  29. package/node_modules/@openclaw/fs-safe/dist/create.js +1 -1
  30. package/node_modules/@openclaw/fs-safe/dist/creation-boundary.js +7 -4
  31. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
  32. package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
  33. package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +7 -4
  34. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +20 -5
  35. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
  36. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
  37. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.d.ts +7 -7
  38. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +9 -4
  39. package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +2 -1
  40. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +2 -2
  41. package/node_modules/@openclaw/fs-safe/dist/exact-identity.d.ts +17 -0
  42. package/node_modules/@openclaw/fs-safe/dist/exact-identity.js +29 -0
  43. package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
  44. package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +1 -1
  45. package/node_modules/@openclaw/fs-safe/dist/file-hash.js +11 -9
  46. package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +7 -1
  47. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +27 -5
  48. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +1 -2
  49. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -1
  50. package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
  51. package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
  52. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
  53. package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
  54. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +1 -0
  55. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +22 -15
  56. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
  57. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
  58. package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
  59. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -0
  60. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -0
  61. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
  62. package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.js +24 -13
  63. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +85 -18
  64. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +5 -0
  65. package/node_modules/@openclaw/fs-safe/dist/native-config.js +11 -0
  66. package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +6 -4
  67. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.d.ts +2 -1
  68. package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.js +26 -2
  69. package/node_modules/@openclaw/fs-safe/dist/native-guidance.d.ts +3 -0
  70. package/node_modules/@openclaw/fs-safe/dist/native-guidance.js +21 -0
  71. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
  72. package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
  73. package/node_modules/@openclaw/fs-safe/dist/native-operations.js +8 -6
  74. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +44 -4
  75. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +85 -15
  76. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +3 -3
  77. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +33 -6
  78. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
  79. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +22 -3
  80. package/node_modules/@openclaw/fs-safe/dist/native-policy-directory-observation.js +4 -3
  81. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
  82. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
  83. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.d.ts +3 -0
  84. package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.js +16 -0
  85. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
  86. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +15 -5
  87. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.d.ts +3 -0
  88. package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.js +7 -0
  89. package/node_modules/@openclaw/fs-safe/dist/native-status.d.ts +17 -0
  90. package/node_modules/@openclaw/fs-safe/dist/native-status.js +30 -0
  91. package/node_modules/@openclaw/fs-safe/dist/native.d.ts +31 -4
  92. package/node_modules/@openclaw/fs-safe/dist/native.js +107 -9
  93. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
  94. package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
  95. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +1 -1
  96. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -1
  97. package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +9 -1
  98. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
  99. package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
  100. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +83 -47
  101. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +2 -0
  102. package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +8 -3
  103. package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +13 -0
  104. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +10 -8
  105. package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
  106. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +7 -5
  107. package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
  108. package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
  109. package/node_modules/@openclaw/fs-safe/dist/private-directory.js +1 -1
  110. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
  111. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +3 -2
  112. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +38 -10
  113. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
  114. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +3 -0
  115. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +3 -0
  116. package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +5 -3
  117. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +26 -20
  118. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -0
  119. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +3 -4
  120. package/node_modules/@openclaw/fs-safe/dist/realpath.js +32 -0
  121. package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +4 -0
  122. package/node_modules/@openclaw/fs-safe/dist/regular-file.js +11 -15
  123. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +3 -2
  124. package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
  125. package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
  126. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +8 -13
  127. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +252 -27
  128. package/node_modules/@openclaw/fs-safe/dist/root-directory-creation.js +5 -1
  129. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +11 -8
  130. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +35 -13
  131. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.d.ts +8 -0
  132. package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.js +108 -0
  133. package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +7 -0
  134. package/node_modules/@openclaw/fs-safe/dist/root-file.js +15 -0
  135. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +2 -0
  136. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +154 -175
  137. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.d.ts +2 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.js +239 -0
  139. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +37 -15
  140. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.d.ts +32 -0
  141. package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.js +56 -0
  142. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
  143. package/node_modules/@openclaw/fs-safe/dist/root-public-types.d.ts +1 -0
  144. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +95 -16
  145. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +6 -4
  146. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +23 -19
  147. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -6
  148. package/node_modules/@openclaw/fs-safe/dist/root-write-publication.js +5 -1
  149. package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +3 -1
  150. package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +2 -0
  151. package/node_modules/@openclaw/fs-safe/dist/secure-file.js +10 -4
  152. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
  153. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +2 -2
  154. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
  155. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
  156. package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +13 -0
  157. package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +29 -0
  158. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +5 -3
  159. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.d.ts +8 -0
  160. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +13 -2
  161. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +71 -3
  162. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +2 -1
  163. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +22 -0
  164. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-types.d.ts +3 -0
  165. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
  166. package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
  167. package/node_modules/@openclaw/fs-safe/dist/trash.js +2 -2
  168. package/node_modules/@openclaw/fs-safe/dist/types.d.ts +4 -0
  169. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
  170. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.d.ts +5 -0
  171. package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.js +39 -0
  172. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +1 -0
  173. package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +25 -36
  174. package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +24 -5
  175. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +2 -1
  176. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +4 -0
  177. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.d.ts +2 -0
  178. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
  179. package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.js +7 -0
  180. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
  181. package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
  182. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.d.ts +1 -0
  183. package/node_modules/@openclaw/fs-safe/dist/write-file-handle.js +12 -10
  184. package/node_modules/@openclaw/fs-safe/docs/advanced.md +2 -1
  185. package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
  186. package/node_modules/@openclaw/fs-safe/docs/config.md +71 -1
  187. package/node_modules/@openclaw/fs-safe/docs/contributing.md +45 -5
  188. package/node_modules/@openclaw/fs-safe/docs/copy.md +45 -5
  189. package/node_modules/@openclaw/fs-safe/docs/durability.md +34 -7
  190. package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
  191. package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
  192. package/node_modules/@openclaw/fs-safe/docs/install.md +47 -27
  193. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -6
  194. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +1 -1
  195. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +79 -6
  196. package/node_modules/@openclaw/fs-safe/docs/native.md +279 -8
  197. package/node_modules/@openclaw/fs-safe/docs/permissions.md +64 -15
  198. package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
  199. package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
  200. package/node_modules/@openclaw/fs-safe/docs/public-api.md +10 -0
  201. package/node_modules/@openclaw/fs-safe/docs/queue-link-free-design.md +237 -0
  202. package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
  203. package/node_modules/@openclaw/fs-safe/docs/reading.md +56 -2
  204. package/node_modules/@openclaw/fs-safe/docs/regular-file.md +2 -0
  205. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
  206. package/node_modules/@openclaw/fs-safe/docs/secure-file.md +1 -1
  207. package/node_modules/@openclaw/fs-safe/docs/security-model.md +35 -0
  208. package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
  209. package/node_modules/@openclaw/fs-safe/docs/staged-file.md +18 -0
  210. package/node_modules/@openclaw/fs-safe/docs/store.md +15 -0
  211. package/node_modules/@openclaw/fs-safe/docs/temp.md +39 -0
  212. package/node_modules/@openclaw/fs-safe/docs/testing.md +13 -0
  213. package/node_modules/@openclaw/fs-safe/docs/types.md +51 -81
  214. package/node_modules/@openclaw/fs-safe/docs/walk.md +4 -1
  215. package/node_modules/@openclaw/fs-safe/docs/watch.md +3 -2
  216. package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
  217. package/node_modules/@openclaw/fs-safe/package.json +21 -12
  218. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  219. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  220. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  221. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  222. package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
  223. package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
  224. package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
  225. package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
  226. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  227. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  228. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  229. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  230. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  231. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  232. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  233. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  234. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
  235. package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
  236. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  237. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  238. package/package.json +5 -5
  239. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +0 -336
  240. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +0 -90
package/README.md CHANGED
@@ -4,7 +4,7 @@ Official NVIDIA OpenShell sandbox backend for OpenClaw.
4
4
 
5
5
  This plugin lets OpenClaw use OpenShell-managed local or remote sandboxes with
6
6
  SSH command execution. Choose `mirror` mode for a synchronized local workspace
7
- or `remote` mode for a remote-canonical workspace.
7
+ or `remote` mode for a workspace managed on the remote host.
8
8
 
9
9
  Mirror operations sharing a workspace run sequentially so concurrent agent
10
10
  turns cannot overwrite one another. Outbound attachments resolve against the
@@ -2,6 +2,154 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.26.5 - 2026-10-14
6
+
7
+ ### Highlights
8
+
9
+ - **Android/Termux and WSL compatibility:** create temporary workspaces under Android system-owned ancestors and complete short nonblocking reads on WSL/9p filesystems. ([#982](https://github.com/openclaw/fs-safe/pull/982), [#983](https://github.com/openclaw/fs-safe/pull/983))
10
+ - **Windows synchronous read batches:** add `createRootFileOpenBatchSync()` to reuse native root handles while retaining per-file safety checks. ([#984](https://github.com/openclaw/fs-safe/pull/984))
11
+
12
+ ### Features
13
+
14
+ - **Windows synchronous read batches:** reuse native root handles with `createRootFileOpenBatchSync()` while retaining fresh file identity, reparse, hardlink, byte-limit, and confinement checks. ([#984](https://github.com/openclaw/fs-safe/pull/984))
15
+
16
+ ### Fixes
17
+
18
+ - **WSL/9p reads:** finish short nonblocking reads instead of truncating uncapped files, atomic copy fallbacks, or content verification. Thanks @Hawk5150 for [openclaw/openclaw#145589](https://github.com/openclaw/openclaw/issues/145589). ([#983](https://github.com/openclaw/fs-safe/pull/983))
19
+ - **Android/Termux temp workspaces:** admit system-owned ancestors and Android-private writable groups while preserving private roots and full ancestry checks. Thanks @kukirin-ui for [openclaw/openclaw#171406](https://github.com/openclaw/openclaw/issues/171406). ([#982](https://github.com/openclaw/fs-safe/pull/982))
20
+
21
+ ## 0.26.4 - 2026-10-13
22
+
23
+ ### Fixes
24
+
25
+ - **Windows AppContainers:** private files and directories now work inside a Windows AppContainer: the creating container can reopen them and other containers stay denied. Requires the native addon; folder-mounted volumes fail closed. Behavior outside a container is unchanged. Thanks @jonathanlindsay. Reported in [openclaw/openclaw#160287](https://github.com/openclaw/openclaw/issues/160287); limits are in the [permissions guide](docs/permissions.md#advanced-windows-acl-helpers). ([#980](https://github.com/openclaw/fs-safe/pull/980))
26
+
27
+ ### Docs and Tooling
28
+
29
+ - **Windows durability costs:** explain what non-durable writes cost and save on Windows. ([#973](https://github.com/openclaw/fs-safe/pull/973))
30
+
31
+ ## 0.26.3 - 2026-10-13
32
+
33
+ ### Highlights
34
+
35
+ - **Native health and signed Windows binaries:** inspect runtime native support with `getFsSafeNativeStatus()`; Windows addons and the security helper now carry timestamped OpenClaw Foundation signatures. ([#965](https://github.com/openclaw/fs-safe/pull/965), [#960](https://github.com/openclaw/fs-safe/pull/960))
36
+ - **Linux copy compatibility:** recover from refused reflink I/O, including ZFS block cloning, through a guarded byte copy. ([#972](https://github.com/openclaw/fs-safe/pull/972))
37
+ - **Less native filesystem overhead:** retain directory parents, reuse writable-file admission, batch Root verification, and avoid unrelated macOS watch wakeups. ([#971](https://github.com/openclaw/fs-safe/pull/971), [#959](https://github.com/openclaw/fs-safe/pull/959), [#966](https://github.com/openclaw/fs-safe/pull/966), [#955](https://github.com/openclaw/fs-safe/pull/955), [#968](https://github.com/openclaw/fs-safe/pull/968))
38
+
39
+ ### Features
40
+
41
+ - **Native runtime health:** add async `getFsSafeNativeStatus()` with optional probing, path-free load reasons, and actionable guidance, exported from the root and `config` entry points. ([#965](https://github.com/openclaw/fs-safe/pull/965))
42
+ - **Signed Windows binaries:** Authenticode-sign both Windows native addons and the security helper with timestamped OpenClaw Foundation signatures, and verify the signed bytes before publication. ([#960](https://github.com/openclaw/fs-safe/pull/960))
43
+
44
+ ### Fixes
45
+
46
+ - **High-bit filesystem identities:** preserve Node's signed bigint stat identities across native directory checks, allowing guarded writes and build locks on filesystems with high-bit inode numbers. ([#979](https://github.com/openclaw/fs-safe/pull/979))
47
+ - **Linux reflink fallback:** fall back to a guarded byte copy when FICLONE refuses I/O with `EIO` or `EAGAIN`, including ZFS block cloning; real copy and cleanup errors remain fatal. Reported in [openclaw/openclaw#169071](https://github.com/openclaw/openclaw/issues/169071). ([#972](https://github.com/openclaw/fs-safe/pull/972))
48
+ - **Native fallback warnings:** explain why native support is unavailable, name the expected package version, and give cause-specific recovery guidance for Windows security and private-creation fallbacks. ([#961](https://github.com/openclaw/fs-safe/pull/961))
49
+
50
+ ### Performance
51
+
52
+ - **Native directory creation:** retain parent handles during recursive mkdir, reducing the measured case from 160 to 5 promises and 381 to 74 Linux syscalls; nested mkdir also reuses existing parents. ([#971](https://github.com/openclaw/fs-safe/pull/971), [#959](https://github.com/openclaw/fs-safe/pull/959))
53
+ - **Native writable-file creation:** reuse parent admission, reducing measured direct-root append syscalls from 117 to 36 and `openWritable` syscalls from 115 to 34 while retaining identity and cleanup checks. ([#966](https://github.com/openclaw/fs-safe/pull/966))
54
+ - **Native Root verification:** batch verification and checked close to reduce dispatch overhead while retaining identity checks and close-error handling. ([#955](https://github.com/openclaw/fs-safe/pull/955))
55
+ - **macOS file watching:** avoid JavaScript reconciliation for unrelated sibling writes and child-only changes to directory entry scopes while preserving missing-entry discovery and replacement notifications. ([#968](https://github.com/openclaw/fs-safe/pull/968))
56
+
57
+ ### Docs and Tooling
58
+
59
+ - **Durable queue design:** document a link-free ownership protocol and mixed-version boundaries. ([#969](https://github.com/openclaw/fs-safe/pull/969))
60
+ - **Benchmark comparisons:** balance independent build comparisons and restore benchmark setup in relocated checkouts. ([#976](https://github.com/openclaw/fs-safe/pull/976), [#970](https://github.com/openclaw/fs-safe/pull/970))
61
+
62
+ ## 0.26.2 - 2026-10-11
63
+
64
+ ### Highlights
65
+
66
+ - **Windows receipt round trips:** use exact bigint identities from fs-safe receipts with the fail-closed identity checks introduced in 0.26.1. ([#962](https://github.com/openclaw/fs-safe/pull/962))
67
+
68
+ ### Fixes
69
+
70
+ - **Windows file identity:** publication, reads, opens, directory and temporary-workspace receipts, typed listings, and clone metadata now expose bigint `exactIdentity`; publication failures expose `exactTargetIdentity`. Pass the exact form back to identity-checking APIs, including publication and copy expectations, to avoid rejecting rounded legacy Number identities on volumes with large file IDs. Existing exact copy and staged-file receipts remain supported. ([#962](https://github.com/openclaw/fs-safe/pull/962))
71
+
72
+ ### Performance
73
+
74
+ - **Windows permission inspection:** avoid PowerShell startup for portable owner and ACL queries, about 8× faster in a Windows x64 probe, while preserving injected executors and .NET ACL semantics; requires .NET Framework 4.6.2+. ([#958](https://github.com/openclaw/fs-safe/pull/958))
75
+
76
+ ## 0.26.1 - 2026-10-11
77
+
78
+ ### Highlights
79
+
80
+ - **Windows identity checks:** reject rounded file IDs; callers supplying identity stats must use bigint values. ([#949](https://github.com/openclaw/fs-safe/pull/949))
81
+ - **Listings and copies:** measured listings are 38–54% faster on Linux and Windows; direct synchronous file copies are 11–31% faster, with Windows batches up to 48% faster. ([#934](https://github.com/openclaw/fs-safe/pull/934), [#932](https://github.com/openclaw/fs-safe/pull/932))
82
+ - **Windows private creation:** the portable helper is about 5× faster, and private creation works beyond MAX_PATH. ([#930](https://github.com/openclaw/fs-safe/pull/930), [#924](https://github.com/openclaw/fs-safe/pull/924))
83
+ - **Native overhead:** reduce dispatch and repeated admission for direct-child mkdir, removal, moves, existing-file opens, buffered publication, and async temp creation; measured eligible cases reach or improve on fallback performance while retaining native checks. ([#927](https://github.com/openclaw/fs-safe/pull/927), [#928](https://github.com/openclaw/fs-safe/pull/928), [#929](https://github.com/openclaw/fs-safe/pull/929), [#948](https://github.com/openclaw/fs-safe/pull/948), [#953](https://github.com/openclaw/fs-safe/pull/953))
84
+
85
+ ### Fixes
86
+
87
+ - **Windows file identity:** stop accepting rounded Number file IDs as matches. Callers supplying identity stats must now pass bigint stats; unsafe Number identities fail with `path-mismatch`. ([#949](https://github.com/openclaw/fs-safe/pull/949))
88
+ - **Windows long paths:** support private directory and file creation beyond MAX_PATH, including internal staging and pathname verification, while preserving ACL, alias, identity, and cleanup checks. ([#924](https://github.com/openclaw/fs-safe/pull/924))
89
+
90
+ ### Performance
91
+
92
+ - **Listings and traversal:** avoid per-entry filename buffers for losslessly decoded names, making measured Linux and Windows listings 38–54% faster; halve per-entry promise allocation in complete sorted `Root.walk()` scans, improving measured 50,000-entry Linux walks by 3–4%. ([#934](https://github.com/openclaw/fs-safe/pull/934), [#937](https://github.com/openclaw/fs-safe/pull/937))
93
+ - **File copies and moves:** reuse canonical admission in `copyRootFileSync`, improving measured direct copies by 11–31% on Linux and Windows and batches by up to 47–48% on Windows; reduce ordinary `Root.copyIn()` and same-parent move promise allocation without claiming a reproduced latency gain. ([#932](https://github.com/openclaw/fs-safe/pull/932), [#941](https://github.com/openclaw/fs-safe/pull/941), [#936](https://github.com/openclaw/fs-safe/pull/936))
94
+ - **Reads and hashing:** trim file-admission and bounded-read metadata work for 2–9% faster measured Linux reads; reuse privately owned Windows handles for 42% faster native async SHA-256 on small pathname inputs, retaining borrowed-handle positions. ([#931](https://github.com/openclaw/fs-safe/pull/931), [#938](https://github.com/openclaw/fs-safe/pull/938))
95
+ - **Locks and directories:** reduce uncontended sidecar-lock acquisition overhead by up to 34% with native; remove ordinary mkdir dispatch hops for 37% faster native direct-child creation, 17–22% faster existing-directory admission, and 81% faster native `ensureRoot` in the combined Linux benchmark. ([#939](https://github.com/openclaw/fs-safe/pull/939), [#944](https://github.com/openclaw/fs-safe/pull/944), [#927](https://github.com/openclaw/fs-safe/pull/927))
96
+ - **Temporary workspaces and removal:** make measured empty-workspace cleanup 13–32% faster on Linux, create async workspaces through retained parents, and reduce recursive-removal stat allocation for 2–6% faster measured Linux recursive removal; retain identity-bound cleanup. ([#942](https://github.com/openclaw/fs-safe/pull/942), [#953](https://github.com/openclaw/fs-safe/pull/953), [#933](https://github.com/openclaw/fs-safe/pull/933))
97
+ - **Private and exclusive creation:** run portable Windows security checks in a prebuilt helper for about 5× faster private creation; streamline exclusive-creation metadata and skip redundant Windows write-mode changes without removing flushes or permission checks. ([#930](https://github.com/openclaw/fs-safe/pull/930), [#935](https://github.com/openclaw/fs-safe/pull/935), [#940](https://github.com/openclaw/fs-safe/pull/940))
98
+ - **Native operations:** reuse retained roots for removal and moves, defer parent creation until an opened file is missing, and streamline buffered creation and replacement-write publication; preserve descriptor-relative mutation and exact identity checks. Native non-durable publication reaches fallback parity in the measured cases; durable creation remains slower. ([#928](https://github.com/openclaw/fs-safe/pull/928), [#929](https://github.com/openclaw/fs-safe/pull/929), [#948](https://github.com/openclaw/fs-safe/pull/948))
99
+
100
+ ### Docs and Tooling
101
+
102
+ - **Raw Node baseline:** add `pnpm benchmark:node` with alternating raw/safe/control samples and documented operation semantics; raw Node is a performance reference, not a security-equivalent implementation. ([#925](https://github.com/openclaw/fs-safe/pull/925))
103
+ - **Native API reference:** enumerate APIs that require native bindings and their fallback availability. ([#926](https://github.com/openclaw/fs-safe/pull/926))
104
+
105
+ ## 0.26.0 - 2026-10-10
106
+
107
+ ### Highlights
108
+
109
+ - **New platforms:** native packages for FreeBSD x64/arm64 and Windows ARM64. ([#905](https://github.com/openclaw/fs-safe/pull/905), [#893](https://github.com/openclaw/fs-safe/pull/893))
110
+ - **Native capabilities:** anonymous pipes, Linux write leases, Darwin ACL inspection, ReFS block cloning, and metadata- and link-preserving `Root.copyIn`. ([#892](https://github.com/openclaw/fs-safe/pull/892), [#896](https://github.com/openclaw/fs-safe/pull/896), [#920](https://github.com/openclaw/fs-safe/pull/920), [#921](https://github.com/openclaw/fs-safe/pull/921))
111
+ - **Android/Termux publication:** staged no-replace copies and writes work when the OS refuses hard links. ([#888](https://github.com/openclaw/fs-safe/pull/888))
112
+ - **Performance:** faster deep archive extraction, borrowed-handle copies and overwrites, sidecar locks, and Linux descriptor verification, with identity checks retained. ([#912](https://github.com/openclaw/fs-safe/pull/912), [#906](https://github.com/openclaw/fs-safe/pull/906), [#902](https://github.com/openclaw/fs-safe/pull/902), [#901](https://github.com/openclaw/fs-safe/pull/901), [#899](https://github.com/openclaw/fs-safe/pull/899))
113
+
114
+ ### Features
115
+
116
+ - **ReFS file copies:** support guarded `Root.copyIn` block cloning on Windows ReFS and Dev Drive, with exclusive publication, cancellation cleanup, and automatic byte-copy fallback for unavailable clone capabilities. ([#920](https://github.com/openclaw/fs-safe/pull/920))
117
+ - **Guarded file copying:** add `Root.copyIn` metadata preservation and exclusive literal-link copying, including dangling Windows file and directory links; keep permission selection separate from timestamps and attributes. ([#921](https://github.com/openclaw/fs-safe/pull/921))
118
+
119
+ - **Native filesystem capabilities:** add Linux write leases on caller-owned descriptors, no-follow Darwin ACL inspection with separate file and directory inheritance facts, and Windows sharing-lock, attribute, and physical-extent test fixtures. ([#896](https://github.com/openclaw/fs-safe/pull/896))
120
+ - **Anonymous pipes:** add `@openclaw/fs-safe/pipe` with native-owned, reopenable close-on-exec pipe ends for one-shot child input on POSIX, including safe stream closure in Workers. ([#892](https://github.com/openclaw/fs-safe/pull/892))
121
+
122
+ ### Fixes
123
+
124
+ - **Windows creation diagnostics:** retain native Win32 error numbers through private directory and file creation failures, including the command fallback, without changing semantic error codes. ([#919](https://github.com/openclaw/fs-safe/pull/919))
125
+ - **FreeBSD native packages:** add x64 and arm64 packages for FreeBSD 14.4+, with native anonymous pipes and canonicalization, and explicit capability absence for unported operations. ([#905](https://github.com/openclaw/fs-safe/pull/905))
126
+ - **Partial native bindings:** select guarded fallbacks when an operation's native capabilities are absent, and report typed capability errors in required mode before mutation. ([#905](https://github.com/openclaw/fs-safe/pull/905))
127
+ - **Android/Termux publication:** publish staged no-replace copies and writes when hardlinks are refused, using a verified empty placeholder before atomic replacement; include `EACCES` in the public hardlink-to-copy fallback classifier. Reported in [openclaw/openclaw#167791](https://github.com/openclaw/openclaw/issues/167791). ([#888](https://github.com/openclaw/fs-safe/pull/888))
128
+ - **Native ZIP names:** preserve CP437 decoding for unflagged legacy filenames after the ZIP 9 upgrade, including collision rejection and bounded entry selection; continue rejecting invalid Unicode Path checksums and encodings before decoder normalization. ([#885](https://github.com/openclaw/fs-safe/pull/885))
129
+ - **Native ZIP Unicode fields:** retain rejection of invalid Unicode Comment CRCs and UTF-8 before callbacks or bounded reads, and validate sequential Unicode fields in decoder order without repeated hashing of the original name. ([#887](https://github.com/openclaw/fs-safe/pull/887))
130
+ - **Portable ZIP extraction:** accept safe relative root-directory markers such as `./` from libarchive and other producers without turning them into absolute paths; root markers remain skipped before callbacks and publication. ([#886](https://github.com/openclaw/fs-safe/pull/886))
131
+ - **Durable JSON queues on Android/Termux:** claim, failed-entry quarantine, and retirement recovery report typed `helper-unavailable` errors when hardlinks are refused, preserving pending, claimed, and retirement evidence for retry. ([#889](https://github.com/openclaw/fs-safe/pull/889))
132
+ - **Windows exclusive publication:** preserve dangling destination symlinks when a refused hard link falls back to placeholder publication. ([#894](https://github.com/openclaw/fs-safe/pull/894))
133
+ - **Native mkdir collisions:** `Root.mkdir` consistently reports `not-file` for an existing regular-file destination in native require mode. ([#895](https://github.com/openclaw/fs-safe/pull/895))
134
+ - **Guest directory publication:** report a changed source identity instead of a false destination collision when a Linux fallback rename refuses a substituted source. ([#898](https://github.com/openclaw/fs-safe/pull/898))
135
+ - **Native recursive removal:** honor a zero entry budget before ignoring a missing target, including with `force`. ([#904](https://github.com/openclaw/fs-safe/pull/904))
136
+
137
+ ### Performance
138
+
139
+ - **Linux opened-file verification:** read ordinary procfs descriptor paths directly instead of walking realpath, keeping identity checks and deleted-path canonicalization; `readLocalFileSafely` is 25–28% faster on deep paths. ([#899](https://github.com/openclaw/fs-safe/pull/899))
140
+ - **Borrowed handles and locks:** keep descriptor metadata inspection off the asynchronous I/O queue for small overwrites (26–28% faster) and copies (31–34% faster), and skip a redundant parent `mkdir` when acquiring sidecar locks (15–18% faster). ([#901](https://github.com/openclaw/fs-safe/pull/901), [#902](https://github.com/openclaw/fs-safe/pull/902), [#906](https://github.com/openclaw/fs-safe/pull/906))
141
+ - **Deep archive extraction:** native extraction verifies every retained ancestor through its open descriptor instead of repeated realpath walks: about 16% faster at depth 8 and 5–6% at depth 4, with every ancestor substitution check retained. ([#912](https://github.com/openclaw/fs-safe/pull/912))
142
+
143
+ ### Docs and Tooling
144
+
145
+ - **Docs accuracy:** correct native-mode migration guidance for removed Python settings and bundled TAR fallbacks, clarify Root streaming ownership, create-only visibility, and Linux no-clobber move fallback limits, and fix the JSON-store and stale-lock examples so denied process probes keep live locks; README and docs examples are now type-checked in CI. ([#897](https://github.com/openclaw/fs-safe/pull/897), [#900](https://github.com/openclaw/fs-safe/pull/900), [#903](https://github.com/openclaw/fs-safe/pull/903))
146
+
147
+ ### Compatibility
148
+
149
+ - **Windows ARM64:** add a native ARM64 package with real Windows 11 ARM64 and x64-emulation validation, including private ACL creation and Worker descriptor ownership. ([#893](https://github.com/openclaw/fs-safe/pull/893))
150
+
151
+ - **Native ZIP metadata:** invalid UTF-8-flagged names now reject instead of decoding lossily or falling back to CP437. Public ZIP extraction, preflight, and reads already reject these names during physical admission. ([#885](https://github.com/openclaw/fs-safe/pull/885))
152
+
5
153
  ## 0.25.0 - 2026-10-08
6
154
 
7
155
  ### Highlights
@@ -10,6 +10,9 @@
10
10
 
11
11
  Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.
12
12
 
13
+ Platform-specific additions include [Linux write leases, Darwin ACL inspection,
14
+ and Windows test fixtures](docs/platform-capabilities.md).
15
+
13
16
  Think Go's `os.Root` / `OpenInRoot` or Rust's [`cap-std`](https://github.com/bytecodealliance/cap-std), but for Node. Hand `root()` a trusted directory and you get back a handle whose every method resolves relative paths against it and defends against `..`, symlink swaps, hardlink aliases, and TOCTOU rename races. The exact containment strength is reported per mechanism: Linux `openat2` opens are kernel-atomic; guarded Linux fallback, macOS, Windows, and JavaScript paths are best-effort.
14
17
 
15
18
  ```ts
@@ -81,6 +84,12 @@ kernel-atomic. Read the [native helper policy](docs/native-helper.md) and
81
84
  [operation/platform matrix](docs/security-model.md#native-root-mutation-capabilities)
82
85
  when concurrent mutation is in scope.
83
86
 
87
+ For doctor/startup checks, use
88
+ [`await getFsSafeNativeStatus({ probe: true })`](docs/config.md#getfssafenativestatus-options)
89
+ from `@openclaw/fs-safe/config` to report whether the native addon is active,
90
+ why it is unavailable, and what to do next. Calling it without options only reads
91
+ the cache. Status is per module instance; Workers must be checked separately.
92
+
84
93
  ## Migrating from the Python helper
85
94
 
86
95
  Version 0.5 replaced the Python worker with prebuilt native bindings. Follow the
@@ -113,9 +122,14 @@ operation; per-call options handle exceptions. See the [Root reference](docs/roo
113
122
 
114
123
  `write()` replaces contents by default. Use `create()` or `overwrite: false`
115
124
  when an existing destination should be an error. `move()` defaults to no clobber
116
- and requires native support for the atomic collision decision; it fails with
117
- `helper-unavailable` when unavailable. Pass `overwrite: true` when replacement
118
- is intended.
125
+ and requires native support. On Linux, `auto` can replace a concurrently created
126
+ empty directory if no-replace rename is unsupported; use native `require` for
127
+ strict refusal. See the [move fallback contract](docs/writing.md#fs-move-from-to-options).
128
+ Pass `overwrite: true` when replacement is intended.
129
+
130
+ Staged JavaScript copies and creates also work when hardlinks are refused
131
+ (including Android/Termux), using a verified empty placeholder before atomic
132
+ replacement. See the [visibility and concurrency tradeoffs](docs/writing.md#publication-without-hardlinks).
119
133
 
120
134
  See [Writing](docs/writing.md) for copy sources, mutation authority, symlink
121
135
  policy, writable handles, and bounded removal, and [Creation](docs/creation.md)
@@ -159,6 +173,8 @@ and error exports. Prefer focused subpaths when a consumer needs a narrower
159
173
  contract. Low-level helpers that OpenClaw needs to compose higher-level APIs are grouped under
160
174
  `@openclaw/fs-safe/advanced` instead of being separate public leaf contracts.
161
175
 
176
+ Use [`@openclaw/fs-safe/pipe`](docs/pipe.md) for native-owned, reopenable anonymous pipes and one-shot child input.
177
+
162
178
  See the [complete subpath catalogue](docs/install.md#subpath-exports) for every entry point and its contents.
163
179
 
164
180
  ## Failure semantics in the name
@@ -236,7 +252,7 @@ updates:
236
252
  import { fileStore } from "@openclaw/fs-safe/store";
237
253
 
238
254
  const files = fileStore({ rootDir: "/safe/workspace/state", private: true });
239
- const store = files.json("settings.json", { lock: true });
255
+ const store = files.json<{ enabled: boolean }>("settings.json", { lock: true });
240
256
 
241
257
  await store.updateOr({ enabled: false }, (current) => ({ ...current, enabled: true }));
242
258
  ```
@@ -17,7 +17,7 @@ export { readFileWindowFully, readFileWindowFullySync, type ReadFileWindowOption
17
17
  export { writeFileWindowFully, type WriteFileWindowOptions } from "./write-file-handle.js";
18
18
  export { assertNoUnsafeDeviceReadPath, isUnsafeDeviceReadPath, matchUnsafeDeviceReadPath, type UnsafeDeviceReadPathMatch, type UnsafeDeviceReadPathOptions, type UnsafeDeviceReadPathReason, } from "./device-path.js";
19
19
  export { assertAbsolutePathInput, canonicalPathFromExistingAncestor, ensureAbsoluteDirectory, findExistingAncestor, resolveAbsolutePathForRead, resolveAbsolutePathForWrite, type AbsolutePathSymlinkPolicy, type EnsureAbsoluteDirectoryOptions, type EnsureAbsoluteDirectoryResult, type ResolvedAbsolutePath, type ResolvedWritableAbsolutePath, } from "./absolute-path.js";
20
- export { sameFileIdentity, type FileIdentityStat } from "./file-identity.js";
20
+ export { sameFileIdentity, type FileIdentityStat, type FileIdentityInput, type ExactFileIdentity, type ExactIdentityReceipt } from "./file-identity.js";
21
21
  export { sanitizeUntrustedFileName } from "./filename.js";
22
22
  export { pathExists, pathExistsSync } from "./fs.js";
23
23
  export { resolveLocalPathFromRootsSync, readLocalFileFromRoots, type LocalRootsInputOptions, type LocalRootsPathResult, type LocalRootsReadResult, type ReadLocalFileFromRootsOptions, type ResolveLocalPathFromRootsSyncOptions, } from "./local-roots.js";
@@ -26,6 +26,7 @@ export { formatPosixMode } from "./permissions.js";
26
26
  export { configureFsSafeLocks, getFsSafeLockConfig, type FsSafeLockConfig, } from "./lock-config.js";
27
27
  export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICIES, type PathAliasPolicy, } from "./path-policy.js";
28
28
  export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, type OpenRootFileParams, type OpenRootFileSyncParams, type RootFileOpenFailure, type RootFileOpenFailureReason, type RootFileOpenResult, } from "./root-file.js";
29
+ export { createRootFileOpenBatchSync, type RootFileOpenBatchSync } from "./root-file-open-batch-sync.js";
29
30
  export { ROOT_PATH_ALIAS_POLICIES, resolvePathViaExistingAncestorSync, resolveRootPath, resolveRootPathSync, type ResolvedRootPath, type RootPathAliasPolicy, } from "./root-path.js";
30
31
  export { ensureDirectoryWithinRoot, pathScope, resolveExistingPathsWithinRoot, resolvePathWithinRoot, resolvePathsWithinRoot, resolveStrictExistingPathsWithinRoot, resolveWritablePathWithinRoot, type PathScope, type PathScopeOptions, type PathScopeResolveOptions, } from "./root-paths.js";
31
32
  export { safeDirName, safePathSegmentHashed, safePathSegmentHashedV2, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
@@ -27,6 +27,7 @@ export { formatPosixMode } from "./permissions.js";
27
27
  export { configureFsSafeLocks, getFsSafeLockConfig, } from "./lock-config.js";
28
28
  export { assertNoHardlinkedFinalPath, assertNoPathAliasEscape, PATH_ALIAS_POLICIES, } from "./path-policy.js";
29
29
  export { openRootFile, openRootFileSync, canUseRootFileOpen, matchRootFileOpenFailure, } from "./root-file.js";
30
+ export { createRootFileOpenBatchSync } from "./root-file-open-batch-sync.js";
30
31
  export { ROOT_PATH_ALIAS_POLICIES, resolvePathViaExistingAncestorSync, resolveRootPath, resolveRootPathSync, } from "./root-path.js";
31
32
  export { ensureDirectoryWithinRoot, pathScope, resolveExistingPathsWithinRoot, resolvePathWithinRoot, resolvePathsWithinRoot, resolveStrictExistingPathsWithinRoot, resolveWritablePathWithinRoot, } from "./root-paths.js";
32
33
  export { safeDirName, safePathSegmentHashed, safePathSegmentHashedV2, resolveSafeInstallDir, assertCanonicalPathWithinBase, } from "./install-path.js";
@@ -17,6 +17,7 @@ import { syncFileBestEffortSync } from "./file-sync.js";
17
17
  import { finalizeArchivePublication } from "./archive-durability.js";
18
18
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
19
19
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
20
+ import { ArchiveSecurityError } from "./archive-errors.js";
20
21
  export async function withStagedArchivePublication(params, extract) {
21
22
  await withStagedArchiveDestination({
22
23
  destinationRealDir: params.destinationGuard.realPath,
@@ -101,6 +102,23 @@ async function mergeTree(params, publication, durable = true, entryUmask = 0) {
101
102
  assertDirectoryIdentityGuard(destinationGuard);
102
103
  check();
103
104
  for (const ancestor of ancestors) {
105
+ // Verify every retained ancestor, including its current name and physical path.
106
+ // Shallow chains cost less to verify directly than crossing the native bridge.
107
+ if (ancestors.length >= 4 && ancestor.owner.verifyCanonical) {
108
+ let verified;
109
+ try {
110
+ verified = await ancestor.owner.verifyCanonical();
111
+ }
112
+ catch (error) {
113
+ if (error instanceof FsSafeError || isNotFoundPathError(error)) {
114
+ throw createArchiveSymlinkTraversalError(path.relative(destinationDir, ancestor.guard.dir));
115
+ }
116
+ throw error;
117
+ }
118
+ check();
119
+ if (verified)
120
+ continue;
121
+ }
104
122
  assertDirectoryIdentityGuard(ancestor.guard);
105
123
  check();
106
124
  await ancestor.owner.verify(check);
@@ -156,15 +174,22 @@ async function mergeTree(params, publication, durable = true, entryUmask = 0) {
156
174
  await ownExtractionDestinationMutation(params.deadline, async () => {
157
175
  await assertGuards();
158
176
  assertSourceFrontier();
159
- const owner = await pinNodeDirectoryForMode(destinationPath).catch((error) => {
177
+ const guard = await createDirectoryIdentityGuard(destinationPath).catch((error) => {
178
+ if (error instanceof ArchiveSecurityError && error.code === "destination-symlink") {
179
+ throw createArchiveSymlinkTraversalError(originalPath);
180
+ }
181
+ throw error;
182
+ });
183
+ check();
184
+ const owner = await pinNodeDirectoryForMode(destinationPath, {
185
+ expectedIdentity: guard.stat, canonicalPath: guard.realPath,
186
+ }).catch((error) => {
160
187
  if (error instanceof FsSafeError && (error.code === "not-file" || error.code === "path-mismatch")) {
161
188
  throw createArchiveSymlinkTraversalError(originalPath);
162
189
  }
163
190
  throw error;
164
191
  });
165
192
  try {
166
- check();
167
- const guard = await createDirectoryIdentityGuard(destinationPath);
168
193
  check();
169
194
  await owner.verify(check);
170
195
  ancestors.push({ guard, owner });
@@ -17,7 +17,7 @@ import { FsSafeError } from "./errors.js";
17
17
  import { inspectFileIdentity } from "./strict-file-identity.js";
18
18
  import { resolveReadOpenFlags } from "./read-open-flags.js";
19
19
  import { realpathSync } from "./realpath.js";
20
- import { getNativeBinding } from "./native.js";
20
+ import { assertNativeCapabilities, getNativeBinding } from "./native.js";
21
21
  import { admitZipBuffer } from "./archive-zip-admission.js";
22
22
  import { validateNativeZipManifest } from "./archive-zip-manifest.js";
23
23
  import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
@@ -199,9 +199,15 @@ async function readNativeBufferEntry(native, buffer, kind, requested, displayPat
199
199
  try {
200
200
  const signal = new AbortController().signal;
201
201
  const limits = resolveTarMeterLimits();
202
- const reader = kind === "zip"
203
- ? await native.openZipBufferNative(buffer, limits, AbortSignal.any([signal]))
204
- : await native.openTarBufferNative(buffer, kind, limits, AbortSignal.any([signal]));
202
+ let reader;
203
+ if (kind === "zip") {
204
+ assertNativeCapabilities(native, "openZipBufferNative");
205
+ reader = await native.openZipBufferNative(buffer, limits, AbortSignal.any([signal]));
206
+ }
207
+ else {
208
+ assertNativeCapabilities(native, "openTarBufferNative");
209
+ reader = await native.openTarBufferNative(buffer, kind, limits, AbortSignal.any([signal]));
210
+ }
205
211
  const manifest = reader.entries;
206
212
  if (kind === "zip")
207
213
  validateNativeZipManifest(manifest, zipEntries);
@@ -227,7 +233,7 @@ export async function readArchiveEntry(archivePath, entryPath, options) {
227
233
  const zipEntries = [];
228
234
  if (kind === "zip")
229
235
  admitZipBuffer(buffer, resolveExtractLimits(), entry => { zipEntries.push(entry); });
230
- const native = getNativeBinding();
236
+ const native = getNativeBinding(kind === "zip" ? "openZipBufferNative" : "openTarBufferNative");
231
237
  if (native)
232
238
  return await readNativeBufferEntry(native, buffer, kind, requestedEntry, entryPath, maxBytes, zipEntries);
233
239
  return kind === "zip" ? await readZipEntry(buffer, requestedEntry, maxBytes, zipEntries)
@@ -118,11 +118,13 @@ export async function loadAdmittedZipArchive(buffer, admitted) {
118
118
  const entries = new Map();
119
119
  for (const [name, entry, physical] of normalized) {
120
120
  const appendSlash = physical.kind === "directory" && !name.endsWith("/");
121
- const normalizedName = appendSlash ? `${name}/` : name;
121
+ // JSZip reduces a relative root marker such as "./" to an empty name.
122
+ // Keep its directory spelling relative when restoring the terminal slash.
123
+ const normalizedName = appendSlash ? `${name || "."}/` : name;
122
124
  if (Object.hasOwn(files, normalizedName))
123
125
  collision();
124
126
  if (appendSlash) {
125
- entry.name = `${entry.name}/`;
127
+ entry.name = `${entry.name || "."}/`;
126
128
  }
127
129
  files[normalizedName] = entry;
128
130
  entries.set(physical.portableKey, createAdmittedZipEntry(entry, normalizedName, physical));
@@ -154,7 +154,7 @@ export async function extractArchive(params) {
154
154
  const label = kind === "zip" ? "extract zip" : "extract tar";
155
155
  const limits = resolveExtractLimits(params.limits);
156
156
  const tarLimits = resolveTarMeterLimits(limits);
157
- const native = getNativeBinding();
157
+ const native = getNativeBinding("inspectArchiveNative", "extractArchiveNative");
158
158
  assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
159
159
  assertNoWindowsPathAlias(destDir, "filesystem", "archive destination uses a Windows filesystem namespace alias");
160
160
  // Read the declared public fields before private adapters copy options: class
@@ -282,7 +282,7 @@ export async function inspectTarArchive(params) {
282
282
  const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
283
283
  const limits = resolveExtractLimits(params.limits);
284
284
  const tarLimits = resolveTarMeterLimits(limits);
285
- const native = getNativeBinding();
285
+ const native = getNativeBinding("inspectArchiveNative");
286
286
  assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
287
287
  return await withExtractionDeadline(params.timeoutMs, "inspect tar", async (deadline) => {
288
288
  const staged = await stageArchiveFileForExtraction({ archivePath, limits, deadline });
@@ -1,6 +1,6 @@
1
1
  import syncFs, {} from "node:fs";
2
2
  import fs, {} from "node:fs/promises";
3
- import { readBoundedSync } from "./bounded-read.js";
3
+ import { readBoundedSync, readFileHandleBounded } from "./bounded-read.js";
4
4
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
5
5
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
6
  import { sleep, sleepSync } from "./timing.js";
@@ -207,7 +207,7 @@ export class AtomicFile {
207
207
  }
208
208
  *readFile(sizeHint) {
209
209
  if (typeof this.resource !== "number")
210
- return yield* wait(this.resource.readFile());
210
+ return yield* wait(readFileHandleBounded(this.resource, Infinity));
211
211
  if (sizeHint === undefined)
212
212
  return this.io.syncFs.readFileSync(this.resource);
213
213
  const fd = this.resource;
@@ -13,6 +13,7 @@ export declare function readBoundedAsync(maxBytes: number, readChunk: (scratch:
13
13
  * force an unbounded allocation.
14
14
  */
15
15
  export declare function readFileHandleBounded(handle: ReadableFileHandle, maxBytes: number): Promise<Buffer>;
16
+ export declare function readFileHandleBoundedWithSize(handle: ReadableFileHandle, maxBytes: number, initialSize?: number): Promise<Buffer>;
16
17
  /** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
17
18
  export declare function readFileDescriptorBounded(fd: number, maxBytes: number): Promise<Buffer>;
18
19
  export declare function readBoundedSync(maxBytes: number, readChunk: (scratch: Buffer, length: number) => number, options?: {
@@ -73,11 +73,18 @@ export async function readBoundedAsync(maxBytes, readChunk, options = {}) {
73
73
  * force an unbounded allocation.
74
74
  */
75
75
  export async function readFileHandleBounded(handle, maxBytes) {
76
+ return await readFileHandleBoundedWithSize(handle, maxBytes);
77
+ }
78
+ // Internal: the admitted size bounds the first allocation, never the read.
79
+ export function readFileHandleBoundedWithSize(handle, maxBytes, initialSize) {
76
80
  normalizeMaxBytes(maxBytes);
77
81
  const fd = "fd" in handle && typeof handle.fd === "number" ? handle.fd : undefined;
78
- return await readBoundedAsync(maxBytes, async (scratch, length) => {
82
+ return readBoundedAsync(maxBytes, async (scratch, length) => {
79
83
  return (await handle.read(scratch, 0, length, null)).bytesRead;
80
- }, { observeRegularFileSize: fd === undefined ? undefined : () => regularFileSize(fd) });
84
+ }, {
85
+ initialSize,
86
+ observeRegularFileSize: fd === undefined ? undefined : () => regularFileSize(fd),
87
+ });
81
88
  }
82
89
  function regularFileSize(fd) {
83
90
  try {
@@ -1,4 +1,6 @@
1
+ import type { ExactFileIdentity } from "./exact-identity.js";
1
2
  export type CloneFileMetadata = {
3
+ exactIdentity?: ExactFileIdentity;
2
4
  dev: number;
3
5
  type: number;
4
6
  mtimeSec: number;
@@ -6,7 +6,7 @@ import { getNativeBinding, requireNativeBinding } from "./native.js";
6
6
  */
7
7
  export async function readCloneFileMetadata(files) {
8
8
  const paths = files.map(assertAbsolutePathInput);
9
- const native = process.platform === "darwin" ? requireNativeBinding() : getNativeBinding();
9
+ const native = process.platform === "darwin" ? requireNativeBinding("readCloneFileMetadata") : getNativeBinding("readCloneFileMetadata");
10
10
  if (!native)
11
11
  return paths.map(() => undefined);
12
12
  const results = await native.readCloneFileMetadata(paths);
@@ -19,6 +19,7 @@ export async function readCloneFileMetadata(files) {
19
19
  result.readUInt32LE(20) !== 0x100)
20
20
  return undefined;
21
21
  return {
22
+ exactIdentity: Object.freeze({ dev: BigInt(result.readUInt32LE(24)), ino: result.readBigInt64LE(76) }),
22
23
  dev: result.readUInt32LE(24),
23
24
  type: result.readUInt32LE(28),
24
25
  mtimeSec: Number(result.readBigInt64LE(32)),
@@ -1,2 +1,8 @@
1
+ import type { FsSafeNativeStatus } from "./native-status.js";
1
2
  export { configureFsSafeNative, getFsSafeNativeConfig, type FsSafeNativeConfig, type FsSafeNativeMode, } from "./native-config.js";
3
+ export type { FsSafeNativeStatus, FsSafeNativeStatusReason, } from "./native-status.js";
2
4
  export { configureFsSafeLocks, getFsSafeLockConfig, type FsSafeLockConfig, } from "./lock-config.js";
5
+ /** Read native status without loading the native module on config import. */
6
+ export declare function getFsSafeNativeStatus(options?: {
7
+ probe?: boolean;
8
+ }): Promise<FsSafeNativeStatus>;
@@ -1,2 +1,7 @@
1
1
  export { configureFsSafeNative, getFsSafeNativeConfig, } from "./native-config.js";
2
2
  export { configureFsSafeLocks, getFsSafeLockConfig, } from "./lock-config.js";
3
+ /** Read native status without loading the native module on config import. */
4
+ export async function getFsSafeNativeStatus(options) {
5
+ const { readFsSafeNativeStatus } = await import("./native-status.js");
6
+ return readFsSafeNativeStatus(options);
7
+ }
@@ -3,19 +3,21 @@ import type { FileHandle } from "node:fs/promises";
3
3
  import { type CopyCloneMode } from "./copy-policy.js";
4
4
  import { type NativeBinding } from "./native.js";
5
5
  import { type NativeFileCopyResult } from "./native-binding.js";
6
+ import type { CopyMetadata } from "./copy-metadata.js";
6
7
  export type CopyFileInput = {
7
8
  kind: "file";
8
9
  handle: FileHandle;
9
10
  size: number;
10
11
  clone: CopyCloneMode;
11
12
  signal?: AbortSignal;
13
+ metadata?: CopyMetadata;
12
14
  verifySource(): Promise<void>;
13
15
  };
14
16
  export declare function resolveFileCopyCloneMode(mode?: CopyCloneMode): CopyCloneMode;
15
17
  export declare function assertCopySourceCurrent(source: {
16
18
  handle: FileHandle;
17
19
  realPath: string;
18
- }, identity: BigIntStats): Promise<void>;
20
+ }, identity: BigIntStats): void;
19
21
  export declare function writeCopyFileToFd(fd: number, input: CopyFileInput, maxBytes?: number, assertBeforeMutation?: () => void): Promise<void>;
20
22
  export declare function createNativeCopyFile(native: NativeBinding, input: CopyFileInput, parentFd: number, basename: string, maxBytes: number | undefined): Promise<NativeFileCopyResult | undefined>;
21
23
  export declare function assertNativeCopyCompleted(input: CopyFileInput, copied?: NativeFileCopyResult): void;
@@ -1,9 +1,9 @@
1
1
  import fs, {} from "node:fs";
2
2
  import { resolveCopyCloneMode } from "./copy-policy.js";
3
3
  import { FsSafeError } from "./errors.js";
4
- import { getNativeBinding } from "./native.js";
4
+ import { getNativeBinding, selectNativeBinding } from "./native.js";
5
5
  import { captureNativeFdClose } from "./native-binding.js";
6
- import { inspectFileIdentity } from "./strict-file-identity.js";
6
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
7
7
  import { transferFileHandle } from "./file-handle-transfer.js";
8
8
  export function resolveFileCopyCloneMode(mode) {
9
9
  const clone = resolveCopyCloneMode(mode, "never");
@@ -12,9 +12,9 @@ export function resolveFileCopyCloneMode(mode) {
12
12
  }
13
13
  return clone;
14
14
  }
15
- export async function assertCopySourceCurrent(source, identity) {
16
- await inspectFileIdentity(() => fs.fstatSync(source.handle.fd, { bigint: true }), identity);
17
- await inspectFileIdentity(() => {
15
+ export function assertCopySourceCurrent(source, identity) {
16
+ inspectFileIdentitySync(() => fs.fstatSync(source.handle.fd, { bigint: true }), identity);
17
+ inspectFileIdentitySync(() => {
18
18
  const current = fs.lstatSync(source.realPath, { bigint: true });
19
19
  if (current.isSymbolicLink() || !current.isFile()) {
20
20
  throw new FsSafeError("path-mismatch", "copy source path changed");
@@ -29,7 +29,8 @@ export async function writeCopyFileToFd(fd, input, maxBytes, assertBeforeMutatio
29
29
  }
30
30
  export async function createNativeCopyFile(native, input, parentFd, basename, maxBytes) {
31
31
  input.signal?.throwIfAborted();
32
- if (!native.copyFileExclusive) {
32
+ const copier = selectNativeBinding(native, "copyFileExclusive");
33
+ if (!copier) {
33
34
  if (input.clone === "always") {
34
35
  throw new FsSafeError("helper-unavailable", "native file cloning is unavailable");
35
36
  }
@@ -39,7 +40,7 @@ export async function createNativeCopyFile(native, input, parentFd, basename, ma
39
40
  const nativeSignal = input.signal ? AbortSignal.any([input.signal]) : undefined;
40
41
  try {
41
42
  // The caller adopts this descriptor before observing a later cancellation.
42
- return await native.copyFileExclusive(input.handle.fd, parentFd, basename, input.clone, maxBytes !== undefined && Number.isFinite(maxBytes) ? maxBytes : undefined, nativeSignal);
43
+ return await copier.copyFileExclusive(input.handle.fd, parentFd, basename, input.clone, maxBytes !== undefined && Number.isFinite(maxBytes) ? maxBytes : undefined, nativeSignal);
43
44
  }
44
45
  catch (error) {
45
46
  const code = error?.code;
@@ -0,0 +1,26 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
3
+ import type { NativeBinding } from "./native-binding.js";
4
+ import type { PinnedWriteParams } from "./pinned-write-types.js";
5
+ export type CopyLinkInput = {
6
+ kind: "link";
7
+ stageBeforePublish: true;
8
+ sourcePath: string;
9
+ identity: BigIntStats;
10
+ target?: Buffer;
11
+ preserveMetadata: boolean;
12
+ signal?: AbortSignal;
13
+ verifySource(): Promise<void>;
14
+ };
15
+ /** Shared by tree byte copying and per-file copying; never resolves the target. */
16
+ export declare function readCopyLinkTarget(source: string): Promise<Buffer>;
17
+ export declare function assertCopyLinkAvailable(): void;
18
+ export declare function admitCopyLink(sourcePath: string, preserveMetadata: boolean, signal?: AbortSignal): Promise<CopyLinkInput | undefined>;
19
+ /** The caller has admitted the destination parent under Root's mutation policy. */
20
+ export declare function copyLinkAtParent(params: PinnedWriteParams, input: CopyLinkInput, parent: AnyAsyncDirectoryGuard, native?: {
21
+ binding: NativeBinding;
22
+ fd: number;
23
+ }): Promise<{
24
+ dev: bigint;
25
+ ino: bigint;
26
+ }>;