@openclaw/openshell-sandbox 2026.9.6 → 2026.9.8

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 (244) hide show
  1. package/assets/icon.png +0 -0
  2. package/dist/index.js +75 -229
  3. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +102 -0
  4. package/node_modules/@openclaw/fs-safe/README.md +24 -6
  5. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +6 -1
  6. package/node_modules/@openclaw/fs-safe/dist/advanced.js +3 -1
  7. package/node_modules/@openclaw/fs-safe/dist/archive-durability.js +1 -1
  8. package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +1 -2
  9. package/node_modules/@openclaw/fs-safe/dist/archive-plan.d.ts +2 -7
  10. package/node_modules/@openclaw/fs-safe/dist/archive-read.js +9 -18
  11. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -2
  12. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +3 -3
  13. package/node_modules/@openclaw/fs-safe/dist/archive-zip-directory.js +4 -0
  14. package/node_modules/@openclaw/fs-safe/dist/archive-zip-entry.d.ts +11 -11
  15. package/node_modules/@openclaw/fs-safe/dist/archive-zip-entry.js +3 -35
  16. package/node_modules/@openclaw/fs-safe/dist/archive-zip-integrity.d.ts +2 -2
  17. package/node_modules/@openclaw/fs-safe/dist/archive-zip-integrity.js +2 -12
  18. package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.d.ts +7 -3
  19. package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +13 -10
  20. package/node_modules/@openclaw/fs-safe/dist/archive-zip-manifest.js +3 -1
  21. package/node_modules/@openclaw/fs-safe/dist/archive-zip-preflight.d.ts +2 -3
  22. package/node_modules/@openclaw/fs-safe/dist/archive-zip-preflight.js +16 -8
  23. package/node_modules/@openclaw/fs-safe/dist/archive.d.ts +6 -3
  24. package/node_modules/@openclaw/fs-safe/dist/archive.js +22 -19
  25. package/node_modules/@openclaw/fs-safe/dist/atomic.d.ts +1 -1
  26. package/node_modules/@openclaw/fs-safe/dist/copy-publication.d.ts +1 -3
  27. package/node_modules/@openclaw/fs-safe/dist/copy-publication.js +2 -6
  28. package/node_modules/@openclaw/fs-safe/dist/deny-mutation-match.d.ts +2 -0
  29. package/node_modules/@openclaw/fs-safe/dist/deny-mutation-match.js +71 -0
  30. package/node_modules/@openclaw/fs-safe/dist/deny-mutations.js +4 -3
  31. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +0 -1
  32. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +0 -3
  33. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +20 -2
  34. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +77 -1
  35. package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +5 -7
  36. package/node_modules/@openclaw/fs-safe/dist/effective-uid.js +1 -4
  37. package/node_modules/@openclaw/fs-safe/dist/errors.d.ts +3 -1
  38. package/node_modules/@openclaw/fs-safe/dist/errors.js +3 -2
  39. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +10 -2
  40. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-acquisition.js +1 -1
  41. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +5 -1
  42. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-held.d.ts +4 -11
  43. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-held.js +1 -4
  44. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.d.ts +3 -6
  45. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +1 -1
  46. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-options.d.ts +2 -10
  47. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.d.ts +2 -4
  48. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +2 -2
  49. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.d.ts +3 -7
  50. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +7 -10
  51. package/node_modules/@openclaw/fs-safe/dist/file-store-prune.js +7 -6
  52. package/node_modules/@openclaw/fs-safe/dist/file-store.d.ts +4 -7
  53. package/node_modules/@openclaw/fs-safe/dist/file-store.js +19 -21
  54. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +23 -31
  55. package/node_modules/@openclaw/fs-safe/dist/guest.js +21 -12
  56. package/node_modules/@openclaw/fs-safe/dist/json-document-store.d.ts +4 -9
  57. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.d.ts +1 -5
  58. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue.d.ts +0 -1
  59. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue.js +2 -7
  60. package/node_modules/@openclaw/fs-safe/dist/local-file-access.js +2 -5
  61. package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.d.ts +2 -5
  62. package/node_modules/@openclaw/fs-safe/dist/local-roots.d.ts +2 -7
  63. package/node_modules/@openclaw/fs-safe/dist/move-path-cleanup.js +4 -4
  64. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +5 -0
  65. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +51 -14
  66. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +8 -2
  67. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.d.ts +13 -0
  68. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +303 -0
  69. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch-worker.d.ts +1 -0
  70. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch-worker.js +54 -0
  71. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.d.ts +5 -0
  72. package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +64 -0
  73. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.d.ts +2 -0
  74. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +3 -0
  75. package/node_modules/@openclaw/fs-safe/dist/path.js +17 -1
  76. package/node_modules/@openclaw/fs-safe/dist/permission-exec.d.ts +6 -0
  77. package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +3 -6
  78. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +3 -1
  79. package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
  80. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +3 -6
  81. package/node_modules/@openclaw/fs-safe/dist/permissions.d.ts +3 -10
  82. package/node_modules/@openclaw/fs-safe/dist/permissions.js +0 -1
  83. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.d.ts +0 -1
  84. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +4 -2
  85. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-shared-route.d.ts +2 -8
  86. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +1 -1
  87. package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +2 -3
  88. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +4 -0
  89. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -5
  90. package/node_modules/@openclaw/fs-safe/dist/regular-file.js +3 -3
  91. package/node_modules/@openclaw/fs-safe/dist/replace-directory.js +5 -17
  92. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +4 -0
  93. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +36 -0
  94. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +4 -10
  95. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +66 -38
  96. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +7 -11
  97. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +10 -2
  98. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +21 -0
  99. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +123 -0
  100. package/node_modules/@openclaw/fs-safe/dist/replace-file-mutation.d.ts +26 -0
  101. package/node_modules/@openclaw/fs-safe/dist/replace-file-mutation.js +47 -0
  102. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +2 -2
  103. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +16 -4
  104. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +55 -0
  105. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.js +1 -0
  106. package/node_modules/@openclaw/fs-safe/dist/replace-file.d.ts +3 -55
  107. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +31 -11
  108. package/node_modules/@openclaw/fs-safe/dist/retained-directory-replacement.d.ts +1 -0
  109. package/node_modules/@openclaw/fs-safe/dist/retained-directory-replacement.js +1 -1
  110. package/node_modules/@openclaw/fs-safe/dist/retained-file-types.d.ts +50 -0
  111. package/node_modules/@openclaw/fs-safe/dist/retained-file-types.js +1 -0
  112. package/node_modules/@openclaw/fs-safe/dist/retained-file.d.ts +3 -0
  113. package/node_modules/@openclaw/fs-safe/dist/retained-file.js +121 -0
  114. package/node_modules/@openclaw/fs-safe/dist/root-context.js +3 -2
  115. package/node_modules/@openclaw/fs-safe/dist/root-directory-entry.d.ts +9 -0
  116. package/node_modules/@openclaw/fs-safe/dist/root-directory-entry.js +28 -0
  117. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +9 -1
  118. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +88 -37
  119. package/node_modules/@openclaw/fs-safe/dist/root-file-final-admission.d.ts +1 -1
  120. package/node_modules/@openclaw/fs-safe/dist/root-file-final-admission.js +1 -6
  121. package/node_modules/@openclaw/fs-safe/dist/root-file.js +2 -5
  122. package/node_modules/@openclaw/fs-safe/dist/root-handle-context.d.ts +4 -0
  123. package/node_modules/@openclaw/fs-safe/dist/root-handle-context.js +12 -0
  124. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +3 -3
  125. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +8 -5
  126. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +2 -7
  127. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +3 -3
  128. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +0 -1
  129. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -3
  130. package/node_modules/@openclaw/fs-safe/dist/root-path-observation.d.ts +4 -11
  131. package/node_modules/@openclaw/fs-safe/dist/root-path.js +7 -10
  132. package/node_modules/@openclaw/fs-safe/dist/root-paths.d.ts +2 -6
  133. package/node_modules/@openclaw/fs-safe/dist/root-remove-identity.d.ts +1 -3
  134. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +19 -12
  135. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +56 -20
  136. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +1 -6
  137. package/node_modules/@openclaw/fs-safe/dist/root-write-complete-parent.d.ts +2 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-write-complete-parent.js +1 -1
  139. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +1 -0
  140. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +8 -2
  141. package/node_modules/@openclaw/fs-safe/dist/secret-file.d.ts +6 -2
  142. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -16
  143. package/node_modules/@openclaw/fs-safe/dist/secret-read-policy.js +1 -1
  144. package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +5 -14
  145. package/node_modules/@openclaw/fs-safe/dist/secure-file.js +3 -2
  146. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.js +2 -7
  147. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +4 -4
  148. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-admission-parser.d.ts +1 -2
  149. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.d.ts +2 -8
  150. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.d.ts +2 -7
  151. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +2 -2
  152. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +11 -12
  153. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-stale-admission.d.ts +1 -5
  154. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock.js +9 -7
  155. package/node_modules/@openclaw/fs-safe/dist/staged-symlink-types.d.ts +39 -0
  156. package/node_modules/@openclaw/fs-safe/dist/staged-symlink-types.js +1 -0
  157. package/node_modules/@openclaw/fs-safe/dist/stat-observation.js +3 -10
  158. package/node_modules/@openclaw/fs-safe/dist/strict-file-identity.d.ts +2 -0
  159. package/node_modules/@openclaw/fs-safe/dist/strict-file-identity.js +6 -6
  160. package/node_modules/@openclaw/fs-safe/dist/symlink-parents.js +58 -7
  161. package/node_modules/@openclaw/fs-safe/dist/temp-target.js +8 -10
  162. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-admission.js +28 -33
  163. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
  164. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +16 -17
  165. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +1 -2
  166. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +4 -9
  167. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +8 -0
  168. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +52 -0
  169. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +5 -1
  170. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +2 -1
  171. package/node_modules/@openclaw/fs-safe/dist/text-atomic.js +2 -0
  172. package/node_modules/@openclaw/fs-safe/dist/trash.js +27 -1
  173. package/node_modules/@openclaw/fs-safe/dist/walk.d.ts +2 -5
  174. package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +6 -0
  175. package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +88 -0
  176. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +9 -0
  177. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +93 -0
  178. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +36 -0
  179. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +73 -0
  180. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +28 -0
  181. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +300 -0
  182. package/node_modules/@openclaw/fs-safe/dist/watch-stream.d.ts +8 -0
  183. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +32 -0
  184. package/node_modules/@openclaw/fs-safe/dist/watch-types.d.ts +60 -0
  185. package/node_modules/@openclaw/fs-safe/dist/watch-types.js +1 -0
  186. package/node_modules/@openclaw/fs-safe/dist/watch.d.ts +5 -0
  187. package/node_modules/@openclaw/fs-safe/dist/watch.js +530 -0
  188. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +3 -7
  189. package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +0 -1
  190. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +6 -6
  191. package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +78 -3
  192. package/node_modules/@openclaw/fs-safe/dist/windows-security-command.d.ts +8 -0
  193. package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +66 -15
  194. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +4 -0
  195. package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +6 -2
  196. package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -2
  197. package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
  198. package/node_modules/@openclaw/fs-safe/docs/atomic.md +93 -3
  199. package/node_modules/@openclaw/fs-safe/docs/contributing.md +102 -4
  200. package/node_modules/@openclaw/fs-safe/docs/copy.md +2 -0
  201. package/node_modules/@openclaw/fs-safe/docs/durability.md +7 -0
  202. package/node_modules/@openclaw/fs-safe/docs/file-store.md +5 -0
  203. package/node_modules/@openclaw/fs-safe/docs/guest.md +7 -1
  204. package/node_modules/@openclaw/fs-safe/docs/index.md +1 -0
  205. package/node_modules/@openclaw/fs-safe/docs/install.md +30 -0
  206. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +20 -4
  207. package/node_modules/@openclaw/fs-safe/docs/native.md +63 -1
  208. package/node_modules/@openclaw/fs-safe/docs/permissions.md +66 -0
  209. package/node_modules/@openclaw/fs-safe/docs/public-api.md +30 -3
  210. package/node_modules/@openclaw/fs-safe/docs/retained-file.md +115 -0
  211. package/node_modules/@openclaw/fs-safe/docs/root.md +35 -9
  212. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +19 -0
  213. package/node_modules/@openclaw/fs-safe/docs/security-model.md +4 -1
  214. package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +5 -1
  215. package/node_modules/@openclaw/fs-safe/docs/staged-symlink.md +124 -0
  216. package/node_modules/@openclaw/fs-safe/docs/store.md +3 -1
  217. package/node_modules/@openclaw/fs-safe/docs/temp.md +24 -4
  218. package/node_modules/@openclaw/fs-safe/docs/testing.md +210 -0
  219. package/node_modules/@openclaw/fs-safe/docs/types.md +6 -0
  220. package/node_modules/@openclaw/fs-safe/docs/walk.md +34 -1
  221. package/node_modules/@openclaw/fs-safe/docs/watch.md +251 -0
  222. package/node_modules/@openclaw/fs-safe/docs/writing.md +21 -0
  223. package/node_modules/@openclaw/fs-safe/package.json +13 -9
  224. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  225. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  226. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  227. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  228. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  229. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  230. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  231. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  232. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  233. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  234. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  235. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  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/node_modules/p-limit/index.d.ts +2 -0
  239. package/node_modules/p-limit/index.js +17 -1
  240. package/node_modules/p-limit/package.json +1 -1
  241. package/node_modules/p-limit/readme.md +2 -0
  242. package/package.json +6 -6
  243. package/node_modules/@openclaw/fs-safe/dist/directory-mode-owner.d.ts +0 -20
  244. package/node_modules/@openclaw/fs-safe/dist/directory-mode-owner.js +0 -78
@@ -0,0 +1,251 @@
1
+ # Guarded filesystem observation
2
+
3
+ `@openclaw/fs-safe/watch` observes literal paths under an admitted `Root`.
4
+ Notifications are advisory invalidations, not a transaction log, stable content,
5
+ or authority to read a reported pathname. Guarded metadata scans through the
6
+ original Root determine what may be published. Use the Root again to read data.
7
+
8
+ ```ts
9
+ import { root } from "@openclaw/fs-safe/root";
10
+ import { watch } from "@openclaw/fs-safe/watch";
11
+
12
+ const workspace = await root("/trusted/workspace");
13
+ const subscription = watch(workspace, {
14
+ mode: "auto",
15
+ scopes: [
16
+ { path: "config.json", kind: "entry" },
17
+ { path: "skills", kind: "tree", depth: 8 },
18
+ ],
19
+ exclude: entry => entry.kind === "directory" && entry.path.endsWith("node_modules"),
20
+ onInvalidate(invalidation) {
21
+ // Schedule application-owned settling/reload work; this callback is synchronous.
22
+ console.log(invalidation.reason, invalidation.changes);
23
+ },
24
+ });
25
+ await subscription.ready;
26
+ await subscription.setScopes([{ path: "skills", kind: "tree" }]);
27
+ await subscription.reconcile();
28
+ await subscription.close();
29
+ ```
30
+
31
+ ## Contract
32
+
33
+ The subpath exports `watch` and the types `WatchScope`, `WatchEntry`,
34
+ `WatchChange`, `WatchInvalidation`, `WatchFailure`, `WatchHealth`, `WatchOptions`,
35
+ and `WatchSubscription`.
36
+
37
+ An `entry` scope observes only that entry, including its identity and metadata.
38
+ A `tree` also observes descendants to its depth (default 32, maximum 128).
39
+ Depth zero observes only the entry. The empty string and `.` select the Root;
40
+ trailing separators are accepted and normalized after validation. Scopes are
41
+ literal names, never globs or home expansion. Absolute paths, traversal, NULs,
42
+ and platform namespace aliases reject. Windows scopes also reject reserved
43
+ device components and trailing dots/spaces that Win32 would silently alias. Filesystem identity determines ordinary
44
+ case/Unicode aliases; names are not compared by lowercasing.
45
+
46
+ Symlinks are observed as entries and never followed. A symbolic parent fails
47
+ admission: separately admit a caller-trusted link target if needed. Missing
48
+ entries and ordinary blocking files can become directories in later scans.
49
+ Replacing, renaming, or losing the admitted Root fails observation; it never
50
+ silently adopts a new Root. Directory entry scopes ignore child-only mtime/size
51
+ changes, but include permission-mode changes to the directory itself.
52
+
53
+ Each invalidation has `reason: "event" | "reconcile" | "overflow"` and optional
54
+ bounded `changes: { path, type: "content" | "structural" }[]`. Missing detail
55
+ means invalidate **every configured scope**. Initial admission and every
56
+ successful `setScopes` publish one undetailed `reconcile` invalidation. A rename
57
+ or identity replacement is structural; metadata changes to the same ordinary
58
+ file may be content changes. Neither means the file is settled or readable.
59
+ An admitted native hint can invalidate an entry even when its before/after scan
60
+ metadata is identical: the path may have changed and been restored between scans
61
+ (ABA), or content may have changed without a distinguishable metadata change.
62
+ Structural hints remain conservative in that case.
63
+ This does not promise delivery for a differently spelled alias that appears and
64
+ disappears entirely between scans: without an observed identity, it cannot be
65
+ admitted as the selected path. Observation is not a complete transient history.
66
+ Raw event names remain private: detail comes from guarded scans, prior guarded
67
+ snapshots, or explicitly configured targets. Unclassifiable hints inside selected,
68
+ non-excluded territory lose detail. Hints for excluded or unselected paths are
69
+ ignored. Exclusion callbacks are synchronous; excluded directories are recorded
70
+ without descent. Bounded exclusion records recognize late deletion hints.
71
+ Genuine backend event loss and detail-budget exhaustion still invalidate every
72
+ scope, including when an excluded subtree caused the underlying event pressure.
73
+
74
+ ## Transport and mode
75
+
76
+ | Platform/runtime | `auto` | Event transport / limitation |
77
+ | --- | --- | --- |
78
+ | Node.js on Linux with addon | `events` | One shared Rust thread and inotify instance; a nonrecursive watch per distinct directory inode. |
79
+ | Node.js on macOS with addon | `events` | One FSEvents stream per subscription on a shared serial dispatch queue. Pathname activity after a swap remains advisory. |
80
+ | Node.js on Windows with addon | `events` | One recursive ReadDirectoryChangesW Root handle per subscription on the shared IOCP hub; the open handle prevents ordinary renames of the Root's ancestors. |
81
+ | Bun / other unsupported runtimes | `poll` | TSFN lifetime and shutdown have not been qualified; `events` rejects. |
82
+ | Missing/disabled addon | `poll` | `events` rejects with `FsSafeError("helper-unavailable")`. |
83
+
84
+ The shared hub sleeps until a filesystem event, command, or callback acknowledgement:
85
+ Linux blocks on inotify plus eventfd, Windows on IOCP, and macOS on its command
86
+ channel (FSEvents wakes it from the serial dispatch queue). There is no native
87
+ polling timer; the independent JS reconciliation interval remains authoritative.
88
+
89
+ `mode` is required. `poll` never starts or loads the watch hub; guarded scans
90
+ may still use the existing addon. Existing `FS_SAFE_NATIVE_MODE=off` and
91
+ `require` policies apply: `require` plus an unavailable event backend rejects
92
+ `auto` too. Health reports the selected `events` or `poll` mode and failures
93
+ from selection have `operation: "watch"`.
94
+
95
+ Linux installs each watch **before** enumerating children. It opens directories
96
+ beneath the Root using the shared guarded native open (openat2, or its checked
97
+ openat fallback), checks exact identities, verifies the procfs namespace, and
98
+ registers through `/proc/self/fd/N/.`. The final `/.` makes `IN_DONT_FOLLOW` apply
99
+ to the directory instead of rejecting the procfs magic symlink. The descriptor
100
+ closes immediately: inotify retains the inode reference. Reconciliation replaces
101
+ registrations when the directory inventory changes. Queue overflow invalidates
102
+ all owners; exhausted watch capacity reports `failure.code: "watch-limit"`.
103
+ Nonblocking TSFN batches cannot block the hub on JavaScript, and per-owner
104
+ pending detail and queued batches are bounded. The last removal stops and joins
105
+ the native thread. No Worker threads, eval programs, or JS `fs.watch` are used.
106
+
107
+ macOS uses FileEvents, NoDefer and WatchRoot with a 30 ms FSEvents latency.
108
+ After guarded admission, streams use selected tree anchors and entry parents,
109
+ falling back to the nearest admitted ancestor for missing paths. Nested anchors
110
+ are deduplicated, with at most 128 paths. The eight shallowest non-overlapping
111
+ excluded directories are also passed to `FSEventStreamSetExclusionPaths`.
112
+ When the stream paths change, the old stream is stopped, invalidated and released
113
+ on its dispatch queue, then its replacement starts before another guarded pass
114
+ covers the handover. Native exclusions reduce traffic but cannot eliminate real
115
+ FSEvents drops, including during recursive deletion.
116
+ Absolute hints are reduced lexically against the admitted canonical Root;
117
+ outside paths never become detail. Dropped/wrapped streams, RootChanged and
118
+ Unmount trigger guarded reconciliation. Pathname hints can reflect activity
119
+ after a swap, but the Root is never replaced and names require guarded admission.
120
+ Removal synchronously stops, invalidates and releases the stream on its queue.
121
+
122
+ Windows opens one identity-checked Root handle per subscription with
123
+ READ/WRITE/DELETE sharing, backup semantics and overlapped I/O. Each handle
124
+ observes the entire subtree; guarded scans filter hints to configured scopes.
125
+ No descendant watch handles are retained, so directories inside the Root can be
126
+ renamed while watching, including directories containing selected scopes.
127
+ Completed buffers are copied and the
128
+ read re-armed before names are examined. Zero-byte / enumeration-loss completions
129
+ invalidate every scope. Buffers are 1 MiB on confirmed local volumes, and 64 KiB
130
+ on network/UNC or unclassified volumes. Cancellation waits for IOCP completion before closing
131
+ the handle or freeing its buffer; there are no detached retirement waits.
132
+
133
+ On Windows, an open file handle—including fs-safe's pinned reads, editors, and
134
+ antivirus—blocks renaming that file's ancestor directories, even with delete
135
+ sharing. The watch backend retains only the Root's `ReadDirectoryChangesW` handle,
136
+ which permits renames beneath the Root. Callers renaming directories concurrently
137
+ with reads should use bounded retries, as Windows tools do.
138
+
139
+ Windows prevents ordinary renames of the Root's own ancestors while its directory
140
+ handle is open, even with DELETE sharing. Use `mode: "poll"` when callers must not
141
+ retain that handle. Renaming or replacing the Root still fails guarded observation;
142
+ the subscription never adopts another location. Subscriptions have independent
143
+ handles and delivery queues, and closing one does not retire another's observation.
144
+
145
+ ## Budgets and lifecycle
146
+
147
+ | Option | Default / bound |
148
+ | --- | --- |
149
+ | `scopes` | At most 128 literal scopes |
150
+ | `persistent` | `true`; `false` lets Node exit with the subscription still open |
151
+ | `intervalMs` | 30000 with events; 1000 with poll; minimum 20 ms |
152
+ | `pollIntervalMs` | Optional polling override; minimum 20 ms, maximum 2147483647 ms (same as `intervalMs`) |
153
+ | `maxDirectories` | 4096 observed directories, including scope ancestors |
154
+ | `maxEntries` | 100000 examined entries per pass, including excluded entries |
155
+ | `maxPendingPaths` | 256; maximum 4096 |
156
+ | Reconciliation | One active pass and one coalesced pending pass; no convergence/pass budget |
157
+
158
+ Periodic guarded reconciliation runs without needing an event. It catches
159
+ missed events and works on filesystems where native hints are incomplete.
160
+ When polling is selected, the interval is `pollIntervalMs`, then `intervalMs`,
161
+ then 1000 ms, in that order. This applies to explicit `mode: "poll"`, `auto`
162
+ selecting polling, and `auto` falling back after an unsupported event backend.
163
+ `pollIntervalMs` does not change the events reconciliation interval, which
164
+ remains `intervalMs` or 30000 ms. For example, `mode: "auto", pollIntervalMs: 25`
165
+ uses 25 ms polling when needed and retains the 30-second events reconciliation.
166
+ Scans are metadata comparisons: content changes preserving all compared
167
+ metadata may be missed in polling mode. No mode promises transactional
168
+ snapshots, complete history, or hard real-time delivery.
169
+
170
+ `ready` resolves after the first complete guarded scan establishes the baseline,
171
+ even while writes continue. Events mode installs each directory registration
172
+ before listing it (FSEvents and recursive RDCW anchors cover the crawl). A changed
173
+ registration/listing identity is retried up to three times per directory; further
174
+ churn invalidates that subtree. Poll mode starts with its first scan and detects
175
+ changes during the crawl on the next comparison. Neither mode waits for two
176
+ agreeing scans.
177
+ `ready` and `reconcile()` do not drain the operating system's event queue.
178
+ For example, FSEvents can deliver coalesced setup creation activity after `ready`,
179
+ even with its stream starting at the current event ID. Consumers must tolerate
180
+ these advisory invalidations; tests measuring a quiet interval should first
181
+ observe a selected sentinel edit and drain its trailing events.
182
+
183
+ Each later pass compares with the previous snapshot, publishes bounded differences,
184
+ and adopts its result as the next snapshot. Vanishing entries, kind changes, and
185
+ transient descendant scan errors produce structural invalidations, preserving the
186
+ Root identity checks. Events during a pass coalesce into one pending pass and
187
+ retain bounded detail regardless of how long the scan takes. The 25 ms hint
188
+ coalescing window does not impose a scan deadline. A full native callback queue
189
+ retains its bounded pending batch for retry. Genuine backend loss, exhausted
190
+ detail capacity, or an unclassifiable selected hint emits `overflow` without
191
+ detail. Sustained writes cannot exhaust a pass budget or disable observation.
192
+
193
+ `reconcile()` resolves after a complete pass that **started after the call**. Calls
194
+ waiting for the same future pass coalesce; an earlier in-flight pass cannot satisfy
195
+ a new call. It rejects only when observation becomes unavailable or is closed.
196
+ `setScopes` fences the old generation immediately and resolves after the new
197
+ baseline scan; superseded scope calls reject `AbortError`.
198
+ By default, an open subscription keeps the Node event loop alive, matching
199
+ `fs.watch`. Set `persistent: false` for caches used by one-shot commands:
200
+ the subscription's timers and native delivery handle do not keep Node alive,
201
+ including during startup or reconciliation. Invalidations still arrive while
202
+ other work keeps the process alive. Persistent and non-persistent subscriptions
203
+ have independent lifetimes; closing the last persistent one lets Node exit.
204
+ Native environment cleanup retires any remaining event registrations and joins
205
+ the hub at exit. `signal` triggers close;
206
+ await `close()` or `[Symbol.asyncDispose]()` to join owned work.
207
+
208
+ `health()` returns `starting`, `ready`, `reconciling`, `unavailable`, or `closed`,
209
+ the actual mode, observed `directories`, and optional `{ operation, code, error }`
210
+ failure. A running reconciliation reports `reconciling`, then returns to `ready`.
211
+ Observation becomes `unavailable` on loss of Root authority (removed, replaced,
212
+ or inaccessible), fatal native backend/registration failures such as `watch-limit`,
213
+ deterministic size limits (`too-large`, operation `scan`), or callback contract
214
+ violations. Invalid scope admission, including symbolic parents, still rejects;
215
+ it never grants authority through a link. Transient descendant churn does not
216
+ make an admitted subscription unavailable. Callbacks may synchronously retire
217
+ the owner. Returning a thenable or a synchronous or asynchronous generator object
218
+ from `onInvalidate`, `onHealth`, or `exclude` rejects observation without advancing
219
+ generators. Application async
220
+ work remains application-owned and is not joined by the subscription.
221
+
222
+ `close()` is terminal, idempotent, and joined. Observation failures remain in
223
+ health but do not make successful retirement reject. Retirement failures do
224
+ reject, with a `SuppressedError` retaining an earlier observation failure when
225
+ both exist. No new generation or callback can be admitted after close.
226
+
227
+ The stress harness also provides `node scripts/watch-stress.mjs --scenario soak-short`,
228
+ a two-minute lifecycle and collected-memory smoke. It is separate from the
229
+ full one-hour `soak` scenario and its stronger memory-growth qualification.
230
+
231
+ ## Seeded consumer-cache stress test
232
+
233
+ After building the package and host binding, run the model against real temporary
234
+ Roots in both modes:
235
+
236
+ ```sh
237
+ node scripts/watch-stress/model-runner.mjs --seeds 2000 --mode both --output watch-model-results.json
238
+ ```
239
+
240
+ Each seed generates file and directory edits, renames, replacements, deep trees,
241
+ symlink retargets, bursts, scope changes, and subscription retirement. A consumer
242
+ queues refreshes only from `onInvalidate`; checkpoints drain those requests after
243
+ quiescence and `reconcile()`, then compare its cache with a guarded Root walk and
244
+ an independent mutation model. Native hints never authorize consumer reads.
245
+
246
+ `--seed`, `--steps`, `--settle` (milliseconds), and `--concurrency` control a run.
247
+ Failures are shrunk by fast-check and saved beside the report as `.<mode>-<seed>.failure.json`;
248
+ replay one with `--replay <failure.json>`. Event mode requires a working native
249
+ binding and never silently falls back to polling. The small
250
+ `test/watch-model.test.ts` corpus runs in ordinary CI; native-event cases also run
251
+ when `FS_SAFE_TEST_WATCH_EVENTS=1`. Keep fixtures on normal `os.tmpdir()` storage.
@@ -211,6 +211,11 @@ can establish them; native disposal can retain them inside a `SuppressedError`
211
211
  cause. Preserve those details when handling errors: a rejection can follow
212
212
  complete publication, and an indeterminate link or native rename must preserve names for
213
213
  recovery. A cleanup or close failure also retains the original operation failure.
214
+ The JavaScript fallback checks the destination again immediately before attempting
215
+ publication. A collision observed there leaves publication unattempted and cleans
216
+ the owned stage. A later collision or other error from the link call remains
217
+ indeterminate, including `EEXIST`; the error code alone does not prove that the
218
+ filesystem left both names unchanged.
214
219
  No later verification, mode, or synchronization failure authorizes deleting an
215
220
  already published complete destination. See [receipt meanings](staged-file.md).
216
221
 
@@ -301,6 +306,12 @@ type RootWriteJsonOptions = RootWriteOptions & {
301
306
 
302
307
  Open in append mode, write, sync the file handle, and close. Honors `mkdir` for the parent directory and syncs the parent directory when the append creates the file. `durable: false` skips both syncs. Pass `prependNewlineIfNeeded: true` to insert a `\n` if the file does not already end in one.
303
308
 
309
+ `mode` selects the creation mode, defaulting to `0o600` when neither the call nor
310
+ the Root supplies it. On POSIX, the process umask can further restrict that mode;
311
+ for example, `mode: 0o640` with umask `0o077` creates a `0o600` file. Existing
312
+ files are not chmodded, even when an explicit `mode` is supplied. Empty appends
313
+ use the same creation rules.
314
+
304
315
  ```ts
305
316
  await fs.append("logs/today.log", `[${ts}] ${line}\n`);
306
317
  await fs.append("notes/scratch.md", "* new bullet", { prependNewlineIfNeeded: true });
@@ -348,6 +359,11 @@ replacing rename. After dispatch it rechecks both parent identities, so a
348
359
  post-operation rejection can mean the no-replace rename completed. Directory
349
360
  moves continue to require `overwrite: true`.
350
361
 
362
+ Linux without `openat2` uses the [guarded native parent walk](native.md#linux-without-openat2).
363
+ The move still uses `renameat2(RENAME_NOREPLACE)` and preserves collisions;
364
+ parent resolution reports the documented `best-effort` containment class.
365
+ Disabling the addon still makes no-clobber moves unavailable.
366
+
351
367
  Both selected canonical endpoints are admitted inside the retained Root after
352
368
  native parent admission. With `mutationSymlinks: "reject"`, both full operation
353
369
  paths are rechecked after the live mutation-authority callback and before
@@ -522,6 +538,11 @@ destination — there is no atomic-rename step. For exclusive publication of a
522
538
  complete stream, use [`create()`](#streamed-creation). For streamed replacement,
523
539
  the [`atomic`](atomic.md) helpers provide a staged writer.
524
540
 
541
+ For all three write modes, `mode` only selects new-file creation permissions,
542
+ defaulting to `0o600` when neither the call nor the Root supplies it. POSIX
543
+ permissions remain subject to the process umask; existing files are not chmodded.
544
+ The returned numeric `stat` records the admitted descriptor before caller writes.
545
+
525
546
  On POSIX, existing-target opens use `O_NONBLOCK` as an admission safeguard so
526
547
  a no-reader FIFO cannot stall regular-file validation. This does not change
527
548
  ordinary regular-file write semantics. `replace` and `update` remain write-only
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
5
5
  "keywords": [
6
6
  "filesystem",
@@ -95,6 +95,10 @@
95
95
  "types": "./dist/file-lock.d.ts",
96
96
  "default": "./dist/file-lock.js"
97
97
  },
98
+ "./watch": {
99
+ "types": "./dist/watch.d.ts",
100
+ "default": "./dist/watch.js"
101
+ },
98
102
  "./walk": {
99
103
  "types": "./dist/walk.d.ts",
100
104
  "default": "./dist/walk.js"
@@ -169,18 +173,18 @@
169
173
  "archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
170
174
  },
171
175
  "optionalDependencies": {
172
- "@openclaw/fs-safe-darwin-arm64": "0.18.1",
173
- "@openclaw/fs-safe-darwin-x64": "0.18.1",
174
- "@openclaw/fs-safe-linux-arm64-gnu": "0.18.1",
175
- "@openclaw/fs-safe-linux-arm64-musl": "0.18.1",
176
- "@openclaw/fs-safe-linux-x64-gnu": "0.18.1",
177
- "@openclaw/fs-safe-linux-x64-musl": "0.18.1",
178
- "@openclaw/fs-safe-win32-x64-msvc": "0.18.1",
176
+ "@openclaw/fs-safe-darwin-arm64": "0.21.1",
177
+ "@openclaw/fs-safe-darwin-x64": "0.21.1",
178
+ "@openclaw/fs-safe-linux-arm64-gnu": "0.21.1",
179
+ "@openclaw/fs-safe-linux-arm64-musl": "0.21.1",
180
+ "@openclaw/fs-safe-linux-x64-gnu": "0.21.1",
181
+ "@openclaw/fs-safe-linux-x64-musl": "0.21.1",
182
+ "@openclaw/fs-safe-win32-x64-msvc": "0.21.1",
179
183
  "jszip": "^3.10.2"
180
184
  },
181
185
  "devDependencies": {
182
186
  "@emnapi/runtime": "2.0.0-alpha.5",
183
- "@napi-rs/cli": "3.10.3",
187
+ "@napi-rs/cli": "3.10.5",
184
188
  "@types/node": "^26.6.1",
185
189
  "@vitest/coverage-v8": "5.0.1",
186
190
  "fast-check": "^4.10.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-darwin-arm64",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "macOS arm64 native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-darwin-x64",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "macOS x64 native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-linux-arm64-gnu",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Linux arm64 glibc native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-linux-arm64-musl",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Linux arm64 musl native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-linux-x64-gnu",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Linux x64 glibc native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-linux-x64-musl",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Linux x64 musl native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe-win32-x64-msvc",
3
- "version": "0.18.1",
3
+ "version": "0.21.1",
4
4
  "description": "Windows x64 MSVC native binding for @openclaw/fs-safe.",
5
5
  "license": "MIT",
6
6
  "author": "OpenClaw Team <dev@openclaw.ai>",
@@ -31,6 +31,8 @@ export type LimitFunction = {
31
31
 
32
32
  The mapper function receives the item value and its index.
33
33
 
34
+ If the iterable throws during iteration, the returned promise rejects with that error. Mapper functions that were already scheduled still run, and their results are discarded.
35
+
34
36
  This is a convenience function for processing inputs that arrive in batches. For more complex use cases, see [p-map](https://github.com/sindresorhus/p-map).
35
37
 
36
38
  @param iterable - An iterable containing an argument for the given function.
@@ -105,7 +105,23 @@ export default function pLimit(concurrency) {
105
105
  },
106
106
  map: {
107
107
  async value(iterable, function_) {
108
- const promises = Array.from(iterable, (value, index) => generator(function_, value, index));
108
+ const promises = [];
109
+
110
+ try {
111
+ Array.from(iterable, (value, index) => {
112
+ const promise = generator(function_, value, index);
113
+ promises.push(promise);
114
+ return promise;
115
+ });
116
+ } catch (error) {
117
+ // The iterable threw, so `Promise.all` never observes the already scheduled promises. Observe them here to avoid unhandled rejections.
118
+ for (const promise of promises) {
119
+ promise.catch(() => {}); // eslint-disable-line promise/prefer-await-to-then
120
+ }
121
+
122
+ throw error;
123
+ }
124
+
109
125
  return Promise.all(promises);
110
126
  },
111
127
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "p-limit",
3
- "version": "7.3.2",
3
+ "version": "7.3.3",
4
4
  "description": "Run multiple promise-returning & async functions with limited concurrency",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/p-limit",
@@ -83,6 +83,8 @@ The mapper function receives the item value and its index.
83
83
 
84
84
  Returns a promise equivalent to `Promise.all(Array.from(iterable, (item, index) => limit(mapperFunction, item, index)))`.
85
85
 
86
+ If the iterable throws during iteration, the returned promise rejects with that error. Mapper functions that were already scheduled still run, and their results are discarded.
87
+
86
88
  This is a convenience function for processing inputs that arrive in batches. For more complex use cases, see [p-map](https://github.com/sindresorhus/p-map).
87
89
 
88
90
  ### limit.activeCount
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/openshell-sandbox",
3
- "version": "2026.9.6",
3
+ "version": "2026.9.8",
4
4
  "description": "OpenClaw sandbox backend for the NVIDIA OpenShell CLI with mirrored local workspaces and SSH command execution.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,8 +8,8 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@openclaw/fs-safe": "0.18.1",
12
- "p-limit": "7.3.2",
11
+ "@openclaw/fs-safe": "0.21.1",
12
+ "p-limit": "7.3.3",
13
13
  "zod": "4.6.5"
14
14
  },
15
15
  "openclaw": {
@@ -23,10 +23,10 @@
23
23
  "minHostVersion": ">=2026.5.12-beta.1"
24
24
  },
25
25
  "compat": {
26
- "pluginApi": ">=2026.9.6"
26
+ "pluginApi": ">=2026.9.8"
27
27
  },
28
28
  "build": {
29
- "openclawVersion": "2026.9.6",
29
+ "openclawVersion": "2026.9.8",
30
30
  "bundledDist": false
31
31
  },
32
32
  "release": {
@@ -45,7 +45,7 @@
45
45
  "assets/activity.svg"
46
46
  ],
47
47
  "peerDependencies": {
48
- "openclaw": ">=2026.9.6"
48
+ "openclaw": ">=2026.9.8"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "openclaw": {
@@ -1,20 +0,0 @@
1
- import type { BigIntStats, Stats } from "node:fs";
2
- export type DirectoryModeChecks = {
3
- check?: () => void;
4
- beforeChmod?: () => Promise<void>;
5
- };
6
- export type DirectoryModeOwner = {
7
- verify(check?: () => void): Promise<void>;
8
- apply(mode: number, checks?: DirectoryModeChecks): Promise<void>;
9
- close(): Promise<void>;
10
- };
11
- export declare function assertOwnedDirectory(expected: Stats | BigIntStats, actual: Stats | BigIntStats): void;
12
- /** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
13
- export declare function ownDirectoryMode(params: {
14
- inspect: () => Promise<number>;
15
- chmod: (mode: number) => Promise<void>;
16
- prepareChmod?: () => Promise<void>;
17
- verifyChmod?: () => Promise<void>;
18
- close: () => Promise<void>;
19
- ignoreChmodError?: boolean;
20
- }): DirectoryModeOwner;
@@ -1,78 +0,0 @@
1
- import { FsSafeError } from "./errors.js";
2
- import { sameFileIdentityForCleanup } from "./file-identity.js";
3
- export function assertOwnedDirectory(expected, actual) {
4
- if (actual.isSymbolicLink() || !actual.isDirectory()) {
5
- throw new FsSafeError("not-file", "directory mode target must be a real directory");
6
- }
7
- if (!sameFileIdentityForCleanup(expected, actual)) {
8
- throw new FsSafeError("path-mismatch", "directory changed before its mode could be applied");
9
- }
10
- }
11
- /** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
12
- export function ownDirectoryMode(params) {
13
- let pending = Promise.resolve();
14
- let closing;
15
- const enqueue = (run) => {
16
- if (closing)
17
- return Promise.reject(new FsSafeError("path-mismatch", "directory mode owner is closed"));
18
- const operation = pending.then(run);
19
- pending = operation.catch(() => undefined);
20
- return operation;
21
- };
22
- return {
23
- verify: (check) => enqueue(async () => {
24
- check?.();
25
- await params.inspect();
26
- check?.();
27
- }),
28
- apply: (mode, checks = {}) => enqueue(async () => {
29
- // inspect() reports permission bits only; chmod ignores file-type bits,
30
- // so tolerate raw stat modes (e.g. S_IFDIR | 0o755) by masking up front.
31
- mode &= 0o7777;
32
- checks.check?.();
33
- const currentMode = await params.inspect();
34
- checks.check?.();
35
- if (currentMode === mode && !checks.beforeChmod && !checks.check)
36
- return;
37
- if (currentMode !== mode) {
38
- await params.prepareChmod?.();
39
- checks.check?.();
40
- }
41
- await checks.beforeChmod?.();
42
- checks.check?.();
43
- // Hooks/ancestor checks can yield; recheck the original named association.
44
- await params.inspect();
45
- checks.check?.();
46
- let dispatchDeadlineFailure;
47
- if (currentMode !== mode) {
48
- checks.check?.();
49
- try {
50
- await params.chmod(mode);
51
- }
52
- catch (error) {
53
- if (!params.ignoreChmodError)
54
- throw error;
55
- }
56
- // Expiry cannot release the fd while post-dispatch verification is pending.
57
- try {
58
- checks.check?.();
59
- }
60
- catch (error) {
61
- dispatchDeadlineFailure = { error };
62
- }
63
- await params.verifyChmod?.();
64
- }
65
- const finalMode = await params.inspect();
66
- if (dispatchDeadlineFailure)
67
- throw dispatchDeadlineFailure.error;
68
- checks.check?.();
69
- if (!params.ignoreChmodError && finalMode !== mode) {
70
- throw new FsSafeError("path-mismatch", "directory final mode could not be verified");
71
- }
72
- }),
73
- close() {
74
- closing ??= pending.then(params.close);
75
- return closing;
76
- },
77
- };
78
- }