@openclaw/openshell-sandbox 2026.9.2 → 2026.9.4

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 (711) hide show
  1. package/dist/index.js +87 -47
  2. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +700 -0
  3. package/node_modules/@openclaw/fs-safe/LICENSE +21 -0
  4. package/node_modules/@openclaw/fs-safe/README.md +564 -0
  5. package/node_modules/@openclaw/fs-safe/SECURITY.md +45 -0
  6. package/node_modules/@openclaw/fs-safe/dist/absolute-path.d.ts +33 -0
  7. package/node_modules/@openclaw/fs-safe/dist/absolute-path.d.ts.map +1 -0
  8. package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +307 -0
  9. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +26 -0
  10. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts.map +1 -0
  11. package/node_modules/@openclaw/fs-safe/dist/advanced.js +28 -0
  12. package/node_modules/@openclaw/fs-safe/dist/archive-crc32.d.ts +2 -0
  13. package/node_modules/@openclaw/fs-safe/dist/archive-crc32.d.ts.map +1 -0
  14. package/node_modules/@openclaw/fs-safe/dist/archive-crc32.js +14 -0
  15. package/node_modules/@openclaw/fs-safe/dist/archive-deadline.d.ts +12 -0
  16. package/node_modules/@openclaw/fs-safe/dist/archive-deadline.d.ts.map +1 -0
  17. package/node_modules/@openclaw/fs-safe/dist/archive-deadline.js +103 -0
  18. package/node_modules/@openclaw/fs-safe/dist/archive-entry.d.ts +14 -0
  19. package/node_modules/@openclaw/fs-safe/dist/archive-entry.d.ts.map +1 -0
  20. package/node_modules/@openclaw/fs-safe/dist/archive-entry.js +85 -0
  21. package/node_modules/@openclaw/fs-safe/dist/archive-errors.d.ts +13 -0
  22. package/node_modules/@openclaw/fs-safe/dist/archive-errors.d.ts.map +1 -0
  23. package/node_modules/@openclaw/fs-safe/dist/archive-errors.js +23 -0
  24. package/node_modules/@openclaw/fs-safe/dist/archive-gzip-tail.d.ts +18 -0
  25. package/node_modules/@openclaw/fs-safe/dist/archive-gzip-tail.d.ts.map +1 -0
  26. package/node_modules/@openclaw/fs-safe/dist/archive-gzip-tail.js +74 -0
  27. package/node_modules/@openclaw/fs-safe/dist/archive-input.d.ts +19 -0
  28. package/node_modules/@openclaw/fs-safe/dist/archive-input.d.ts.map +1 -0
  29. package/node_modules/@openclaw/fs-safe/dist/archive-input.js +92 -0
  30. package/node_modules/@openclaw/fs-safe/dist/archive-kind.d.ts +9 -0
  31. package/node_modules/@openclaw/fs-safe/dist/archive-kind.d.ts.map +1 -0
  32. package/node_modules/@openclaw/fs-safe/dist/archive-kind.js +82 -0
  33. package/node_modules/@openclaw/fs-safe/dist/archive-limits.d.ts +57 -0
  34. package/node_modules/@openclaw/fs-safe/dist/archive-limits.d.ts.map +1 -0
  35. package/node_modules/@openclaw/fs-safe/dist/archive-limits.js +131 -0
  36. package/node_modules/@openclaw/fs-safe/dist/archive-merge.d.ts +18 -0
  37. package/node_modules/@openclaw/fs-safe/dist/archive-merge.d.ts.map +1 -0
  38. package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +157 -0
  39. package/node_modules/@openclaw/fs-safe/dist/archive-native.d.ts +20 -0
  40. package/node_modules/@openclaw/fs-safe/dist/archive-native.d.ts.map +1 -0
  41. package/node_modules/@openclaw/fs-safe/dist/archive-native.js +107 -0
  42. package/node_modules/@openclaw/fs-safe/dist/archive-options.d.ts +21 -0
  43. package/node_modules/@openclaw/fs-safe/dist/archive-options.d.ts.map +1 -0
  44. package/node_modules/@openclaw/fs-safe/dist/archive-options.js +1 -0
  45. package/node_modules/@openclaw/fs-safe/dist/archive-parser-errors.d.ts +2 -0
  46. package/node_modules/@openclaw/fs-safe/dist/archive-parser-errors.d.ts.map +1 -0
  47. package/node_modules/@openclaw/fs-safe/dist/archive-parser-errors.js +12 -0
  48. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  49. package/node_modules/@openclaw/fs-safe/dist/archive-plan.d.ts +20 -0
  50. package/node_modules/@openclaw/fs-safe/dist/archive-plan.d.ts.map +1 -0
  51. package/node_modules/@openclaw/fs-safe/dist/archive-plan.js +55 -0
  52. package/node_modules/@openclaw/fs-safe/dist/archive-policy.d.ts +22 -0
  53. package/node_modules/@openclaw/fs-safe/dist/archive-policy.d.ts.map +1 -0
  54. package/node_modules/@openclaw/fs-safe/dist/archive-policy.js +38 -0
  55. package/node_modules/@openclaw/fs-safe/dist/archive-read.d.ts +6 -0
  56. package/node_modules/@openclaw/fs-safe/dist/archive-read.d.ts.map +1 -0
  57. package/node_modules/@openclaw/fs-safe/dist/archive-read.js +225 -0
  58. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +31 -0
  59. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts.map +1 -0
  60. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +250 -0
  61. package/node_modules/@openclaw/fs-safe/dist/archive-tar-inspect.d.ts +7 -0
  62. package/node_modules/@openclaw/fs-safe/dist/archive-tar-inspect.d.ts.map +1 -0
  63. package/node_modules/@openclaw/fs-safe/dist/archive-tar-inspect.js +59 -0
  64. package/node_modules/@openclaw/fs-safe/dist/archive-tar-stream.d.ts +18 -0
  65. package/node_modules/@openclaw/fs-safe/dist/archive-tar-stream.d.ts.map +1 -0
  66. package/node_modules/@openclaw/fs-safe/dist/archive-tar-stream.js +95 -0
  67. package/node_modules/@openclaw/fs-safe/dist/archive-tar-wasm.d.ts +18 -0
  68. package/node_modules/@openclaw/fs-safe/dist/archive-tar-wasm.d.ts.map +1 -0
  69. package/node_modules/@openclaw/fs-safe/dist/archive-tar-wasm.js +99 -0
  70. package/node_modules/@openclaw/fs-safe/dist/archive-tar.d.ts +19 -0
  71. package/node_modules/@openclaw/fs-safe/dist/archive-tar.d.ts.map +1 -0
  72. package/node_modules/@openclaw/fs-safe/dist/archive-tar.js +21 -0
  73. package/node_modules/@openclaw/fs-safe/dist/archive-zip-admission.d.ts +8 -0
  74. package/node_modules/@openclaw/fs-safe/dist/archive-zip-admission.d.ts.map +1 -0
  75. package/node_modules/@openclaw/fs-safe/dist/archive-zip-admission.js +60 -0
  76. package/node_modules/@openclaw/fs-safe/dist/archive-zip-count.d.ts +2 -0
  77. package/node_modules/@openclaw/fs-safe/dist/archive-zip-count.d.ts.map +1 -0
  78. package/node_modules/@openclaw/fs-safe/dist/archive-zip-count.js +140 -0
  79. package/node_modules/@openclaw/fs-safe/dist/archive-zip-directory.d.ts +15 -0
  80. package/node_modules/@openclaw/fs-safe/dist/archive-zip-directory.d.ts.map +1 -0
  81. package/node_modules/@openclaw/fs-safe/dist/archive-zip-directory.js +228 -0
  82. package/node_modules/@openclaw/fs-safe/dist/archive-zip-entry.d.ts +21 -0
  83. package/node_modules/@openclaw/fs-safe/dist/archive-zip-entry.d.ts.map +1 -0
  84. package/node_modules/@openclaw/fs-safe/dist/archive-zip-entry.js +27 -0
  85. package/node_modules/@openclaw/fs-safe/dist/archive-zip-integrity.d.ts +5 -0
  86. package/node_modules/@openclaw/fs-safe/dist/archive-zip-integrity.d.ts.map +1 -0
  87. package/node_modules/@openclaw/fs-safe/dist/archive-zip-integrity.js +41 -0
  88. package/node_modules/@openclaw/fs-safe/dist/archive-zip-names.d.ts +12 -0
  89. package/node_modules/@openclaw/fs-safe/dist/archive-zip-names.d.ts.map +1 -0
  90. package/node_modules/@openclaw/fs-safe/dist/archive-zip-names.js +100 -0
  91. package/node_modules/@openclaw/fs-safe/dist/archive-zip-preflight.d.ts +7 -0
  92. package/node_modules/@openclaw/fs-safe/dist/archive-zip-preflight.d.ts.map +1 -0
  93. package/node_modules/@openclaw/fs-safe/dist/archive-zip-preflight.js +44 -0
  94. package/node_modules/@openclaw/fs-safe/dist/archive.d.ts +15 -0
  95. package/node_modules/@openclaw/fs-safe/dist/archive.d.ts.map +1 -0
  96. package/node_modules/@openclaw/fs-safe/dist/archive.js +275 -0
  97. package/node_modules/@openclaw/fs-safe/dist/async-lock.d.ts +2 -0
  98. package/node_modules/@openclaw/fs-safe/dist/async-lock.d.ts.map +1 -0
  99. package/node_modules/@openclaw/fs-safe/dist/async-lock.js +17 -0
  100. package/node_modules/@openclaw/fs-safe/dist/atomic.d.ts +5 -0
  101. package/node_modules/@openclaw/fs-safe/dist/atomic.d.ts.map +1 -0
  102. package/node_modules/@openclaw/fs-safe/dist/atomic.js +4 -0
  103. package/node_modules/@openclaw/fs-safe/dist/bounded-read-stream.d.ts +4 -0
  104. package/node_modules/@openclaw/fs-safe/dist/bounded-read-stream.d.ts.map +1 -0
  105. package/node_modules/@openclaw/fs-safe/dist/bounded-read-stream.js +21 -0
  106. package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +14 -0
  107. package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts.map +1 -0
  108. package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +106 -0
  109. package/node_modules/@openclaw/fs-safe/dist/byte-budget.d.ts +4 -0
  110. package/node_modules/@openclaw/fs-safe/dist/byte-budget.d.ts.map +1 -0
  111. package/node_modules/@openclaw/fs-safe/dist/byte-budget.js +9 -0
  112. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +3 -0
  113. package/node_modules/@openclaw/fs-safe/dist/config.d.ts.map +1 -0
  114. package/node_modules/@openclaw/fs-safe/dist/config.js +2 -0
  115. package/node_modules/@openclaw/fs-safe/dist/containment.d.ts +2 -0
  116. package/node_modules/@openclaw/fs-safe/dist/containment.d.ts.map +1 -0
  117. package/node_modules/@openclaw/fs-safe/dist/containment.js +1 -0
  118. package/node_modules/@openclaw/fs-safe/dist/deny-mutations.d.ts +12 -0
  119. package/node_modules/@openclaw/fs-safe/dist/deny-mutations.d.ts.map +1 -0
  120. package/node_modules/@openclaw/fs-safe/dist/deny-mutations.js +69 -0
  121. package/node_modules/@openclaw/fs-safe/dist/device-path.d.ts +15 -0
  122. package/node_modules/@openclaw/fs-safe/dist/device-path.d.ts.map +1 -0
  123. package/node_modules/@openclaw/fs-safe/dist/device-path.js +137 -0
  124. package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +44 -0
  125. package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts.map +1 -0
  126. package/node_modules/@openclaw/fs-safe/dist/directory-durability.js +299 -0
  127. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +31 -0
  128. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts.map +1 -0
  129. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +82 -0
  130. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +8 -0
  131. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts.map +1 -0
  132. package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +93 -0
  133. package/node_modules/@openclaw/fs-safe/dist/directory-mode-owner.d.ts +21 -0
  134. package/node_modules/@openclaw/fs-safe/dist/directory-mode-owner.d.ts.map +1 -0
  135. package/node_modules/@openclaw/fs-safe/dist/directory-mode-owner.js +78 -0
  136. package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +4 -0
  137. package/node_modules/@openclaw/fs-safe/dist/durability.d.ts.map +1 -0
  138. package/node_modules/@openclaw/fs-safe/dist/durability.js +3 -0
  139. package/node_modules/@openclaw/fs-safe/dist/error-detail.d.ts +3 -0
  140. package/node_modules/@openclaw/fs-safe/dist/error-detail.d.ts.map +1 -0
  141. package/node_modules/@openclaw/fs-safe/dist/error-detail.js +10 -0
  142. package/node_modules/@openclaw/fs-safe/dist/errors.d.ts +14 -0
  143. package/node_modules/@openclaw/fs-safe/dist/errors.d.ts.map +1 -0
  144. package/node_modules/@openclaw/fs-safe/dist/errors.js +26 -0
  145. package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +10 -0
  146. package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts.map +1 -0
  147. package/node_modules/@openclaw/fs-safe/dist/file-hash.js +73 -0
  148. package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +8 -0
  149. package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts.map +1 -0
  150. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +34 -0
  151. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.d.ts +35 -0
  152. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.d.ts.map +1 -0
  153. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +349 -0
  154. package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +24 -0
  155. package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts.map +1 -0
  156. package/node_modules/@openclaw/fs-safe/dist/file-lock.js +46 -0
  157. package/node_modules/@openclaw/fs-safe/dist/file-observation.d.ts +9 -0
  158. package/node_modules/@openclaw/fs-safe/dist/file-observation.d.ts.map +1 -0
  159. package/node_modules/@openclaw/fs-safe/dist/file-observation.js +22 -0
  160. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.d.ts +33 -0
  161. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.d.ts.map +1 -0
  162. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +189 -0
  163. package/node_modules/@openclaw/fs-safe/dist/file-store-limit.d.ts +2 -0
  164. package/node_modules/@openclaw/fs-safe/dist/file-store-limit.d.ts.map +1 -0
  165. package/node_modules/@openclaw/fs-safe/dist/file-store-limit.js +8 -0
  166. package/node_modules/@openclaw/fs-safe/dist/file-store-prune.d.ts +12 -0
  167. package/node_modules/@openclaw/fs-safe/dist/file-store-prune.d.ts.map +1 -0
  168. package/node_modules/@openclaw/fs-safe/dist/file-store-prune.js +95 -0
  169. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.d.ts +9 -0
  170. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.d.ts.map +1 -0
  171. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +127 -0
  172. package/node_modules/@openclaw/fs-safe/dist/file-store.d.ts +62 -0
  173. package/node_modules/@openclaw/fs-safe/dist/file-store.d.ts.map +1 -0
  174. package/node_modules/@openclaw/fs-safe/dist/file-store.js +357 -0
  175. package/node_modules/@openclaw/fs-safe/dist/file-sync.d.ts +5 -0
  176. package/node_modules/@openclaw/fs-safe/dist/file-sync.d.ts.map +1 -0
  177. package/node_modules/@openclaw/fs-safe/dist/file-sync.js +19 -0
  178. package/node_modules/@openclaw/fs-safe/dist/filename.d.ts +8 -0
  179. package/node_modules/@openclaw/fs-safe/dist/filename.d.ts.map +1 -0
  180. package/node_modules/@openclaw/fs-safe/dist/filename.js +91 -0
  181. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +13 -0
  182. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts.map +1 -0
  183. package/node_modules/@openclaw/fs-safe/dist/fs.js +30 -0
  184. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +14 -0
  185. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts.map +1 -0
  186. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +87 -0
  187. package/node_modules/@openclaw/fs-safe/dist/guarded-mutation.d.ts +37 -0
  188. package/node_modules/@openclaw/fs-safe/dist/guarded-mutation.d.ts.map +1 -0
  189. package/node_modules/@openclaw/fs-safe/dist/guarded-mutation.js +82 -0
  190. package/node_modules/@openclaw/fs-safe/dist/home-dir.d.ts +12 -0
  191. package/node_modules/@openclaw/fs-safe/dist/home-dir.d.ts.map +1 -0
  192. package/node_modules/@openclaw/fs-safe/dist/home-dir.js +84 -0
  193. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +7 -0
  194. package/node_modules/@openclaw/fs-safe/dist/index.d.ts.map +1 -0
  195. package/node_modules/@openclaw/fs-safe/dist/index.js +5 -0
  196. package/node_modules/@openclaw/fs-safe/dist/install-path.d.ts +20 -0
  197. package/node_modules/@openclaw/fs-safe/dist/install-path.d.ts.map +1 -0
  198. package/node_modules/@openclaw/fs-safe/dist/install-path.js +94 -0
  199. package/node_modules/@openclaw/fs-safe/dist/json-document-store.d.ts +35 -0
  200. package/node_modules/@openclaw/fs-safe/dist/json-document-store.d.ts.map +1 -0
  201. package/node_modules/@openclaw/fs-safe/dist/json-document-store.js +97 -0
  202. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.d.ts +2 -0
  203. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.d.ts.map +1 -0
  204. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +20 -0
  205. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.d.ts +17 -0
  206. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.d.ts.map +1 -0
  207. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +292 -0
  208. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue.d.ts +46 -0
  209. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue.d.ts.map +1 -0
  210. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue.js +383 -0
  211. package/node_modules/@openclaw/fs-safe/dist/json-store.d.ts +9 -0
  212. package/node_modules/@openclaw/fs-safe/dist/json-store.d.ts.map +1 -0
  213. package/node_modules/@openclaw/fs-safe/dist/json-store.js +12 -0
  214. package/node_modules/@openclaw/fs-safe/dist/json-stringify.d.ts +2 -0
  215. package/node_modules/@openclaw/fs-safe/dist/json-stringify.d.ts.map +1 -0
  216. package/node_modules/@openclaw/fs-safe/dist/json-stringify.js +7 -0
  217. package/node_modules/@openclaw/fs-safe/dist/json.d.ts +50 -0
  218. package/node_modules/@openclaw/fs-safe/dist/json.d.ts.map +1 -0
  219. package/node_modules/@openclaw/fs-safe/dist/json.js +343 -0
  220. package/node_modules/@openclaw/fs-safe/dist/local-file-access.d.ts +9 -0
  221. package/node_modules/@openclaw/fs-safe/dist/local-file-access.d.ts.map +1 -0
  222. package/node_modules/@openclaw/fs-safe/dist/local-file-access.js +87 -0
  223. package/node_modules/@openclaw/fs-safe/dist/local-roots.d.ts +26 -0
  224. package/node_modules/@openclaw/fs-safe/dist/local-roots.d.ts.map +1 -0
  225. package/node_modules/@openclaw/fs-safe/dist/local-roots.js +135 -0
  226. package/node_modules/@openclaw/fs-safe/dist/lock-config.d.ts +10 -0
  227. package/node_modules/@openclaw/fs-safe/dist/lock-config.d.ts.map +1 -0
  228. package/node_modules/@openclaw/fs-safe/dist/lock-config.js +12 -0
  229. package/node_modules/@openclaw/fs-safe/dist/move-path-cleanup.d.ts +43 -0
  230. package/node_modules/@openclaw/fs-safe/dist/move-path-cleanup.d.ts.map +1 -0
  231. package/node_modules/@openclaw/fs-safe/dist/move-path-cleanup.js +161 -0
  232. package/node_modules/@openclaw/fs-safe/dist/move-path.d.ts +21 -0
  233. package/node_modules/@openclaw/fs-safe/dist/move-path.d.ts.map +1 -0
  234. package/node_modules/@openclaw/fs-safe/dist/move-path.js +334 -0
  235. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +93 -0
  236. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts.map +1 -0
  237. package/node_modules/@openclaw/fs-safe/dist/native-binding.js +1 -0
  238. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +17 -0
  239. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts.map +1 -0
  240. package/node_modules/@openclaw/fs-safe/dist/native-config.js +71 -0
  241. package/node_modules/@openclaw/fs-safe/dist/native-operations.d.ts +21 -0
  242. package/node_modules/@openclaw/fs-safe/dist/native-operations.d.ts.map +1 -0
  243. package/node_modules/@openclaw/fs-safe/dist/native-operations.js +132 -0
  244. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +8 -0
  245. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts.map +1 -0
  246. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +96 -0
  247. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +5 -0
  248. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts.map +1 -0
  249. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +161 -0
  250. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +31 -0
  251. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts.map +1 -0
  252. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +362 -0
  253. package/node_modules/@openclaw/fs-safe/dist/native.d.ts +14 -0
  254. package/node_modules/@openclaw/fs-safe/dist/native.d.ts.map +1 -0
  255. package/node_modules/@openclaw/fs-safe/dist/native.js +204 -0
  256. package/node_modules/@openclaw/fs-safe/dist/opened-file-failure.d.ts +8 -0
  257. package/node_modules/@openclaw/fs-safe/dist/opened-file-failure.d.ts.map +1 -0
  258. package/node_modules/@openclaw/fs-safe/dist/opened-file-failure.js +45 -0
  259. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.d.ts +16 -0
  260. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.d.ts.map +1 -0
  261. package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +92 -0
  262. package/node_modules/@openclaw/fs-safe/dist/output.d.ts +15 -0
  263. package/node_modules/@openclaw/fs-safe/dist/output.d.ts.map +1 -0
  264. package/node_modules/@openclaw/fs-safe/dist/output.js +123 -0
  265. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.d.ts +31 -0
  266. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.d.ts.map +1 -0
  267. package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +28 -0
  268. package/node_modules/@openclaw/fs-safe/dist/path-policy.d.ts +25 -0
  269. package/node_modules/@openclaw/fs-safe/dist/path-policy.d.ts.map +1 -0
  270. package/node_modules/@openclaw/fs-safe/dist/path-policy.js +44 -0
  271. package/node_modules/@openclaw/fs-safe/dist/path.d.ts +21 -0
  272. package/node_modules/@openclaw/fs-safe/dist/path.d.ts.map +1 -0
  273. package/node_modules/@openclaw/fs-safe/dist/path.js +136 -0
  274. package/node_modules/@openclaw/fs-safe/dist/permission-exec.d.ts +25 -0
  275. package/node_modules/@openclaw/fs-safe/dist/permission-exec.d.ts.map +1 -0
  276. package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +74 -0
  277. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +4 -0
  278. package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts.map +1 -0
  279. package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +3 -0
  280. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +59 -0
  281. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts.map +1 -0
  282. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +296 -0
  283. package/node_modules/@openclaw/fs-safe/dist/permissions.d.ts +60 -0
  284. package/node_modules/@openclaw/fs-safe/dist/permissions.d.ts.map +1 -0
  285. package/node_modules/@openclaw/fs-safe/dist/permissions.js +125 -0
  286. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +24 -0
  287. package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts.map +1 -0
  288. package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +74 -0
  289. package/node_modules/@openclaw/fs-safe/dist/pinned-operation.d.ts +2 -0
  290. package/node_modules/@openclaw/fs-safe/dist/pinned-operation.d.ts.map +1 -0
  291. package/node_modules/@openclaw/fs-safe/dist/pinned-operation.js +35 -0
  292. package/node_modules/@openclaw/fs-safe/dist/pinned-write.d.ts +36 -0
  293. package/node_modules/@openclaw/fs-safe/dist/pinned-write.d.ts.map +1 -0
  294. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +252 -0
  295. package/node_modules/@openclaw/fs-safe/dist/private-directory.d.ts +5 -0
  296. package/node_modules/@openclaw/fs-safe/dist/private-directory.d.ts.map +1 -0
  297. package/node_modules/@openclaw/fs-safe/dist/private-directory.js +14 -0
  298. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +45 -0
  299. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts.map +1 -0
  300. package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +299 -0
  301. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +28 -0
  302. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts.map +1 -0
  303. package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +22 -0
  304. package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +21 -0
  305. package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts.map +1 -0
  306. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +366 -0
  307. package/node_modules/@openclaw/fs-safe/dist/read-open-flags.d.ts +8 -0
  308. package/node_modules/@openclaw/fs-safe/dist/read-open-flags.d.ts.map +1 -0
  309. package/node_modules/@openclaw/fs-safe/dist/read-open-flags.js +13 -0
  310. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +21 -0
  311. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts.map +1 -0
  312. package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +18 -0
  313. package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +38 -0
  314. package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts.map +1 -0
  315. package/node_modules/@openclaw/fs-safe/dist/regular-file.js +391 -0
  316. package/node_modules/@openclaw/fs-safe/dist/replace-directory.d.ts +7 -0
  317. package/node_modules/@openclaw/fs-safe/dist/replace-directory.d.ts.map +1 -0
  318. package/node_modules/@openclaw/fs-safe/dist/replace-directory.js +42 -0
  319. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +39 -0
  320. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts.map +1 -0
  321. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +331 -0
  322. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +21 -0
  323. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts.map +1 -0
  324. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +107 -0
  325. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +51 -0
  326. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts.map +1 -0
  327. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +161 -0
  328. package/node_modules/@openclaw/fs-safe/dist/replace-file-rename-policy.d.ts +7 -0
  329. package/node_modules/@openclaw/fs-safe/dist/replace-file-rename-policy.d.ts.map +1 -0
  330. package/node_modules/@openclaw/fs-safe/dist/replace-file-rename-policy.js +30 -0
  331. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +53 -0
  332. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts.map +1 -0
  333. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +376 -0
  334. package/node_modules/@openclaw/fs-safe/dist/replace-file.d.ts +58 -0
  335. package/node_modules/@openclaw/fs-safe/dist/replace-file.d.ts.map +1 -0
  336. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +329 -0
  337. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +38 -0
  338. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts.map +1 -0
  339. package/node_modules/@openclaw/fs-safe/dist/root-context.js +141 -0
  340. package/node_modules/@openclaw/fs-safe/dist/root-errors.d.ts +12 -0
  341. package/node_modules/@openclaw/fs-safe/dist/root-errors.d.ts.map +1 -0
  342. package/node_modules/@openclaw/fs-safe/dist/root-errors.js +75 -0
  343. package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +45 -0
  344. package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts.map +1 -0
  345. package/node_modules/@openclaw/fs-safe/dist/root-file.js +120 -0
  346. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +163 -0
  347. package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts.map +1 -0
  348. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +1230 -0
  349. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.d.ts +3 -0
  350. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.d.ts.map +1 -0
  351. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +68 -0
  352. package/node_modules/@openclaw/fs-safe/dist/root-path-symlink.d.ts +7 -0
  353. package/node_modules/@openclaw/fs-safe/dist/root-path-symlink.d.ts.map +1 -0
  354. package/node_modules/@openclaw/fs-safe/dist/root-path-symlink.js +35 -0
  355. package/node_modules/@openclaw/fs-safe/dist/root-path.d.ts +40 -0
  356. package/node_modules/@openclaw/fs-safe/dist/root-path.d.ts.map +1 -0
  357. package/node_modules/@openclaw/fs-safe/dist/root-path.js +481 -0
  358. package/node_modules/@openclaw/fs-safe/dist/root-paths.d.ts +89 -0
  359. package/node_modules/@openclaw/fs-safe/dist/root-paths.d.ts.map +1 -0
  360. package/node_modules/@openclaw/fs-safe/dist/root-paths.js +381 -0
  361. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +41 -0
  362. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts.map +1 -0
  363. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +133 -0
  364. package/node_modules/@openclaw/fs-safe/dist/root-write-mode.d.ts +6 -0
  365. package/node_modules/@openclaw/fs-safe/dist/root-write-mode.d.ts.map +1 -0
  366. package/node_modules/@openclaw/fs-safe/dist/root-write-mode.js +61 -0
  367. package/node_modules/@openclaw/fs-safe/dist/root-write-verification.d.ts +12 -0
  368. package/node_modules/@openclaw/fs-safe/dist/root-write-verification.d.ts.map +1 -0
  369. package/node_modules/@openclaw/fs-safe/dist/root-write-verification.js +97 -0
  370. package/node_modules/@openclaw/fs-safe/dist/root.d.ts +4 -0
  371. package/node_modules/@openclaw/fs-safe/dist/root.d.ts.map +1 -0
  372. package/node_modules/@openclaw/fs-safe/dist/root.js +1 -0
  373. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +12 -0
  374. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts.map +1 -0
  375. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +75 -0
  376. package/node_modules/@openclaw/fs-safe/dist/secret-file.d.ts +25 -0
  377. package/node_modules/@openclaw/fs-safe/dist/secret-file.d.ts.map +1 -0
  378. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +329 -0
  379. package/node_modules/@openclaw/fs-safe/dist/secret-read-async.d.ts +4 -0
  380. package/node_modules/@openclaw/fs-safe/dist/secret-read-async.d.ts.map +1 -0
  381. package/node_modules/@openclaw/fs-safe/dist/secret-read-async.js +73 -0
  382. package/node_modules/@openclaw/fs-safe/dist/secret-read-policy.d.ts +17 -0
  383. package/node_modules/@openclaw/fs-safe/dist/secret-read-policy.d.ts.map +1 -0
  384. package/node_modules/@openclaw/fs-safe/dist/secret-read-policy.js +40 -0
  385. package/node_modules/@openclaw/fs-safe/dist/secret.d.ts +4 -0
  386. package/node_modules/@openclaw/fs-safe/dist/secret.d.ts.map +1 -0
  387. package/node_modules/@openclaw/fs-safe/dist/secret.js +3 -0
  388. package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +32 -0
  389. package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts.map +1 -0
  390. package/node_modules/@openclaw/fs-safe/dist/secure-file.js +182 -0
  391. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +27 -0
  392. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts.map +1 -0
  393. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.js +160 -0
  394. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.d.ts +15 -0
  395. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.d.ts.map +1 -0
  396. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +143 -0
  397. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.d.ts +27 -0
  398. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.d.ts.map +1 -0
  399. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +95 -0
  400. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.d.ts +31 -0
  401. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.d.ts.map +1 -0
  402. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +336 -0
  403. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.d.ts +24 -0
  404. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.d.ts.map +1 -0
  405. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.js +42 -0
  406. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.d.ts +14 -0
  407. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.d.ts.map +1 -0
  408. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.js +71 -0
  409. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +63 -0
  410. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts.map +1 -0
  411. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +305 -0
  412. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-root.d.ts +3 -0
  413. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-root.d.ts.map +1 -0
  414. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-root.js +76 -0
  415. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-types.d.ts +63 -0
  416. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-types.d.ts.map +1 -0
  417. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-types.js +1 -0
  418. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock.d.ts +14 -0
  419. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock.d.ts.map +1 -0
  420. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock.js +265 -0
  421. package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +16 -0
  422. package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts.map +1 -0
  423. package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +60 -0
  424. package/node_modules/@openclaw/fs-safe/dist/staged-file-types.d.ts +56 -0
  425. package/node_modules/@openclaw/fs-safe/dist/staged-file-types.d.ts.map +1 -0
  426. package/node_modules/@openclaw/fs-safe/dist/staged-file-types.js +1 -0
  427. package/node_modules/@openclaw/fs-safe/dist/store.d.ts +4 -0
  428. package/node_modules/@openclaw/fs-safe/dist/store.d.ts.map +1 -0
  429. package/node_modules/@openclaw/fs-safe/dist/store.js +3 -0
  430. package/node_modules/@openclaw/fs-safe/dist/strict-file-identity.d.ts +6 -0
  431. package/node_modules/@openclaw/fs-safe/dist/strict-file-identity.d.ts.map +1 -0
  432. package/node_modules/@openclaw/fs-safe/dist/strict-file-identity.js +51 -0
  433. package/node_modules/@openclaw/fs-safe/dist/string-coerce.d.ts +5 -0
  434. package/node_modules/@openclaw/fs-safe/dist/string-coerce.d.ts.map +1 -0
  435. package/node_modules/@openclaw/fs-safe/dist/string-coerce.js +16 -0
  436. package/node_modules/@openclaw/fs-safe/dist/suppressed-error.d.ts +6 -0
  437. package/node_modules/@openclaw/fs-safe/dist/suppressed-error.d.ts.map +1 -0
  438. package/node_modules/@openclaw/fs-safe/dist/suppressed-error.js +15 -0
  439. package/node_modules/@openclaw/fs-safe/dist/symlink-parents.d.ts +12 -0
  440. package/node_modules/@openclaw/fs-safe/dist/symlink-parents.d.ts.map +1 -0
  441. package/node_modules/@openclaw/fs-safe/dist/symlink-parents.js +78 -0
  442. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.d.ts +14 -0
  443. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.d.ts.map +1 -0
  444. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +87 -0
  445. package/node_modules/@openclaw/fs-safe/dist/temp-target.d.ts +28 -0
  446. package/node_modules/@openclaw/fs-safe/dist/temp-target.d.ts.map +1 -0
  447. package/node_modules/@openclaw/fs-safe/dist/temp-target.js +137 -0
  448. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.d.ts +23 -0
  449. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.d.ts.map +1 -0
  450. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +320 -0
  451. package/node_modules/@openclaw/fs-safe/dist/temp.d.ts +4 -0
  452. package/node_modules/@openclaw/fs-safe/dist/temp.d.ts.map +1 -0
  453. package/node_modules/@openclaw/fs-safe/dist/temp.js +2 -0
  454. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +25 -0
  455. package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts.map +1 -0
  456. package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +13 -0
  457. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +14 -0
  458. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts.map +1 -0
  459. package/node_modules/@openclaw/fs-safe/dist/text-atomic.js +14 -0
  460. package/node_modules/@openclaw/fs-safe/dist/timing.d.ts +8 -0
  461. package/node_modules/@openclaw/fs-safe/dist/timing.d.ts.map +1 -0
  462. package/node_modules/@openclaw/fs-safe/dist/timing.js +40 -0
  463. package/node_modules/@openclaw/fs-safe/dist/trash.d.ts +5 -0
  464. package/node_modules/@openclaw/fs-safe/dist/trash.d.ts.map +1 -0
  465. package/node_modules/@openclaw/fs-safe/dist/trash.js +171 -0
  466. package/node_modules/@openclaw/fs-safe/dist/types.d.ts +23 -0
  467. package/node_modules/@openclaw/fs-safe/dist/types.d.ts.map +1 -0
  468. package/node_modules/@openclaw/fs-safe/dist/types.js +1 -0
  469. package/node_modules/@openclaw/fs-safe/dist/walk.d.ts +37 -0
  470. package/node_modules/@openclaw/fs-safe/dist/walk.d.ts.map +1 -0
  471. package/node_modules/@openclaw/fs-safe/dist/walk.js +207 -0
  472. package/node_modules/@openclaw/fs-safe/dist/windows-command.d.ts +2 -0
  473. package/node_modules/@openclaw/fs-safe/dist/windows-command.d.ts.map +1 -0
  474. package/node_modules/@openclaw/fs-safe/dist/windows-command.js +34 -0
  475. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +32 -0
  476. package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts.map +1 -0
  477. package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +126 -0
  478. package/node_modules/@openclaw/fs-safe/dist/write-open-flags.d.ts +5 -0
  479. package/node_modules/@openclaw/fs-safe/dist/write-open-flags.d.ts.map +1 -0
  480. package/node_modules/@openclaw/fs-safe/dist/write-open-flags.js +35 -0
  481. package/node_modules/@openclaw/fs-safe/dist/write-queue.d.ts +2 -0
  482. package/node_modules/@openclaw/fs-safe/dist/write-queue.d.ts.map +1 -0
  483. package/node_modules/@openclaw/fs-safe/dist/write-queue.js +18 -0
  484. package/node_modules/@openclaw/fs-safe/docs/advanced.md +153 -0
  485. package/node_modules/@openclaw/fs-safe/docs/archive.md +658 -0
  486. package/node_modules/@openclaw/fs-safe/docs/assets/readme-banner.jpg +0 -0
  487. package/node_modules/@openclaw/fs-safe/docs/atomic.md +334 -0
  488. package/node_modules/@openclaw/fs-safe/docs/config.md +131 -0
  489. package/node_modules/@openclaw/fs-safe/docs/contributing.md +180 -0
  490. package/node_modules/@openclaw/fs-safe/docs/durability.md +313 -0
  491. package/node_modules/@openclaw/fs-safe/docs/errors.md +232 -0
  492. package/node_modules/@openclaw/fs-safe/docs/file-store.md +258 -0
  493. package/node_modules/@openclaw/fs-safe/docs/filename.md +96 -0
  494. package/node_modules/@openclaw/fs-safe/docs/index.md +84 -0
  495. package/node_modules/@openclaw/fs-safe/docs/install-path.md +170 -0
  496. package/node_modules/@openclaw/fs-safe/docs/install.md +163 -0
  497. package/node_modules/@openclaw/fs-safe/docs/json-store.md +221 -0
  498. package/node_modules/@openclaw/fs-safe/docs/json.md +213 -0
  499. package/node_modules/@openclaw/fs-safe/docs/local-roots.md +141 -0
  500. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +196 -0
  501. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +43 -0
  502. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +118 -0
  503. package/node_modules/@openclaw/fs-safe/docs/native.md +196 -0
  504. package/node_modules/@openclaw/fs-safe/docs/output.md +139 -0
  505. package/node_modules/@openclaw/fs-safe/docs/path-scope.md +159 -0
  506. package/node_modules/@openclaw/fs-safe/docs/path.md +208 -0
  507. package/node_modules/@openclaw/fs-safe/docs/permissions.md +200 -0
  508. package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +54 -0
  509. package/node_modules/@openclaw/fs-safe/docs/public-api.md +112 -0
  510. package/node_modules/@openclaw/fs-safe/docs/quickstart.md +154 -0
  511. package/node_modules/@openclaw/fs-safe/docs/reading.md +183 -0
  512. package/node_modules/@openclaw/fs-safe/docs/regular-file.md +190 -0
  513. package/node_modules/@openclaw/fs-safe/docs/root.md +266 -0
  514. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +273 -0
  515. package/node_modules/@openclaw/fs-safe/docs/secure-file.md +100 -0
  516. package/node_modules/@openclaw/fs-safe/docs/security-model.md +188 -0
  517. package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +439 -0
  518. package/node_modules/@openclaw/fs-safe/docs/staged-file.md +182 -0
  519. package/node_modules/@openclaw/fs-safe/docs/store.md +94 -0
  520. package/node_modules/@openclaw/fs-safe/docs/temp.md +476 -0
  521. package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +96 -0
  522. package/node_modules/@openclaw/fs-safe/docs/testing.md +189 -0
  523. package/node_modules/@openclaw/fs-safe/docs/timing.md +121 -0
  524. package/node_modules/@openclaw/fs-safe/docs/types.md +188 -0
  525. package/node_modules/@openclaw/fs-safe/docs/walk.md +128 -0
  526. package/node_modules/@openclaw/fs-safe/docs/writing.md +347 -0
  527. package/node_modules/@openclaw/fs-safe/package.json +187 -0
  528. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  529. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +29 -0
  530. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  531. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +29 -0
  532. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  533. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +32 -0
  534. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  535. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +32 -0
  536. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  537. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +32 -0
  538. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  539. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +32 -0
  540. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  541. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +29 -0
  542. package/node_modules/core-util-is/LICENSE +19 -0
  543. package/node_modules/core-util-is/README.md +3 -0
  544. package/node_modules/core-util-is/lib/util.js +107 -0
  545. package/node_modules/core-util-is/package.json +38 -0
  546. package/node_modules/immediate/LICENSE.txt +20 -0
  547. package/node_modules/immediate/README.md +93 -0
  548. package/node_modules/immediate/dist/immediate.js +75 -0
  549. package/node_modules/immediate/dist/immediate.min.js +1 -0
  550. package/node_modules/immediate/lib/browser.js +69 -0
  551. package/node_modules/immediate/lib/index.js +73 -0
  552. package/node_modules/immediate/package.json +42 -0
  553. package/node_modules/inherits/LICENSE +16 -0
  554. package/node_modules/inherits/README.md +42 -0
  555. package/node_modules/inherits/inherits.js +9 -0
  556. package/node_modules/inherits/inherits_browser.js +27 -0
  557. package/node_modules/inherits/package.json +29 -0
  558. package/node_modules/isarray/.npmignore +1 -0
  559. package/node_modules/isarray/.travis.yml +4 -0
  560. package/node_modules/isarray/Makefile +6 -0
  561. package/node_modules/isarray/README.md +60 -0
  562. package/node_modules/isarray/component.json +19 -0
  563. package/node_modules/isarray/index.js +5 -0
  564. package/node_modules/isarray/package.json +45 -0
  565. package/node_modules/isarray/test.js +20 -0
  566. package/node_modules/jszip/.codeclimate.yml +16 -0
  567. package/node_modules/jszip/.editorconfig +8 -0
  568. package/node_modules/jszip/.eslintrc.js +43 -0
  569. package/node_modules/jszip/.github/workflows/pr.yaml +58 -0
  570. package/node_modules/jszip/.jekyll-metadata +0 -0
  571. package/node_modules/jszip/.travis.yml +17 -0
  572. package/node_modules/jszip/CHANGES.md +204 -0
  573. package/node_modules/jszip/LICENSE.markdown +651 -0
  574. package/node_modules/jszip/README.markdown +33 -0
  575. package/node_modules/jszip/deps.js +37 -0
  576. package/node_modules/jszip/dist/jszip.js +11577 -0
  577. package/node_modules/jszip/dist/jszip.min.js +13 -0
  578. package/node_modules/jszip/graph.svg +601 -0
  579. package/node_modules/jszip/index.d.ts +330 -0
  580. package/node_modules/jszip/lib/base64.js +106 -0
  581. package/node_modules/jszip/lib/compressedObject.js +74 -0
  582. package/node_modules/jszip/lib/compressions.js +14 -0
  583. package/node_modules/jszip/lib/crc32.js +77 -0
  584. package/node_modules/jszip/lib/defaults.js +11 -0
  585. package/node_modules/jszip/lib/external.js +18 -0
  586. package/node_modules/jszip/lib/flate.js +85 -0
  587. package/node_modules/jszip/lib/generate/ZipFileWorker.js +539 -0
  588. package/node_modules/jszip/lib/generate/index.js +57 -0
  589. package/node_modules/jszip/lib/index.js +55 -0
  590. package/node_modules/jszip/lib/license_header.js +11 -0
  591. package/node_modules/jszip/lib/load.js +88 -0
  592. package/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +74 -0
  593. package/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +42 -0
  594. package/node_modules/jszip/lib/nodejsUtils.js +57 -0
  595. package/node_modules/jszip/lib/object.js +384 -0
  596. package/node_modules/jszip/lib/readable-stream-browser.js +10 -0
  597. package/node_modules/jszip/lib/reader/ArrayReader.js +57 -0
  598. package/node_modules/jszip/lib/reader/DataReader.js +116 -0
  599. package/node_modules/jszip/lib/reader/NodeBufferReader.js +19 -0
  600. package/node_modules/jszip/lib/reader/StringReader.js +38 -0
  601. package/node_modules/jszip/lib/reader/Uint8ArrayReader.js +22 -0
  602. package/node_modules/jszip/lib/reader/readerFor.js +28 -0
  603. package/node_modules/jszip/lib/signature.js +7 -0
  604. package/node_modules/jszip/lib/stream/ConvertWorker.js +26 -0
  605. package/node_modules/jszip/lib/stream/Crc32Probe.js +24 -0
  606. package/node_modules/jszip/lib/stream/DataLengthProbe.js +29 -0
  607. package/node_modules/jszip/lib/stream/DataWorker.js +116 -0
  608. package/node_modules/jszip/lib/stream/GenericWorker.js +263 -0
  609. package/node_modules/jszip/lib/stream/StreamHelper.js +214 -0
  610. package/node_modules/jszip/lib/support.js +38 -0
  611. package/node_modules/jszip/lib/utf8.js +275 -0
  612. package/node_modules/jszip/lib/utils.js +501 -0
  613. package/node_modules/jszip/lib/zipEntries.js +261 -0
  614. package/node_modules/jszip/lib/zipEntry.js +293 -0
  615. package/node_modules/jszip/lib/zipObject.js +133 -0
  616. package/node_modules/jszip/package.json +67 -0
  617. package/node_modules/jszip/sponsors.md +21 -0
  618. package/node_modules/jszip/tsconfig.json +101 -0
  619. package/node_modules/jszip/vendor/FileSaver.js +247 -0
  620. package/node_modules/lie/README.md +62 -0
  621. package/node_modules/lie/dist/lie.js +350 -0
  622. package/node_modules/lie/dist/lie.min.js +1 -0
  623. package/node_modules/lie/dist/lie.polyfill.js +358 -0
  624. package/node_modules/lie/dist/lie.polyfill.min.js +1 -0
  625. package/node_modules/lie/lib/browser.js +273 -0
  626. package/node_modules/lie/lib/index.js +298 -0
  627. package/node_modules/lie/license.md +7 -0
  628. package/node_modules/lie/lie.d.ts +244 -0
  629. package/node_modules/lie/package.json +69 -0
  630. package/node_modules/lie/polyfill.js +4 -0
  631. package/node_modules/pako/CHANGELOG.md +164 -0
  632. package/node_modules/pako/LICENSE +21 -0
  633. package/node_modules/pako/README.md +191 -0
  634. package/node_modules/pako/dist/pako.js +6818 -0
  635. package/node_modules/pako/dist/pako.min.js +1 -0
  636. package/node_modules/pako/dist/pako_deflate.js +3997 -0
  637. package/node_modules/pako/dist/pako_deflate.min.js +1 -0
  638. package/node_modules/pako/dist/pako_inflate.js +3300 -0
  639. package/node_modules/pako/dist/pako_inflate.min.js +1 -0
  640. package/node_modules/pako/index.js +14 -0
  641. package/node_modules/pako/lib/deflate.js +400 -0
  642. package/node_modules/pako/lib/inflate.js +423 -0
  643. package/node_modules/pako/lib/utils/common.js +105 -0
  644. package/node_modules/pako/lib/utils/strings.js +187 -0
  645. package/node_modules/pako/lib/zlib/README +59 -0
  646. package/node_modules/pako/lib/zlib/adler32.js +51 -0
  647. package/node_modules/pako/lib/zlib/constants.js +68 -0
  648. package/node_modules/pako/lib/zlib/crc32.js +59 -0
  649. package/node_modules/pako/lib/zlib/deflate.js +1874 -0
  650. package/node_modules/pako/lib/zlib/gzheader.js +58 -0
  651. package/node_modules/pako/lib/zlib/inffast.js +345 -0
  652. package/node_modules/pako/lib/zlib/inflate.js +1556 -0
  653. package/node_modules/pako/lib/zlib/inftrees.js +343 -0
  654. package/node_modules/pako/lib/zlib/messages.js +32 -0
  655. package/node_modules/pako/lib/zlib/trees.js +1222 -0
  656. package/node_modules/pako/lib/zlib/zstream.js +47 -0
  657. package/node_modules/pako/package.json +44 -0
  658. package/node_modules/process-nextick-args/index.js +45 -0
  659. package/node_modules/process-nextick-args/license.md +19 -0
  660. package/node_modules/process-nextick-args/package.json +25 -0
  661. package/node_modules/process-nextick-args/readme.md +18 -0
  662. package/node_modules/readable-stream/.travis.yml +34 -0
  663. package/node_modules/readable-stream/CONTRIBUTING.md +38 -0
  664. package/node_modules/readable-stream/GOVERNANCE.md +136 -0
  665. package/node_modules/readable-stream/LICENSE +47 -0
  666. package/node_modules/readable-stream/README.md +58 -0
  667. package/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +60 -0
  668. package/node_modules/readable-stream/duplex-browser.js +1 -0
  669. package/node_modules/readable-stream/duplex.js +1 -0
  670. package/node_modules/readable-stream/lib/_stream_duplex.js +131 -0
  671. package/node_modules/readable-stream/lib/_stream_passthrough.js +47 -0
  672. package/node_modules/readable-stream/lib/_stream_readable.js +1019 -0
  673. package/node_modules/readable-stream/lib/_stream_transform.js +214 -0
  674. package/node_modules/readable-stream/lib/_stream_writable.js +685 -0
  675. package/node_modules/readable-stream/lib/internal/streams/BufferList.js +78 -0
  676. package/node_modules/readable-stream/lib/internal/streams/destroy.js +84 -0
  677. package/node_modules/readable-stream/lib/internal/streams/stream-browser.js +1 -0
  678. package/node_modules/readable-stream/lib/internal/streams/stream.js +1 -0
  679. package/node_modules/readable-stream/package.json +52 -0
  680. package/node_modules/readable-stream/passthrough.js +1 -0
  681. package/node_modules/readable-stream/readable-browser.js +7 -0
  682. package/node_modules/readable-stream/readable.js +19 -0
  683. package/node_modules/readable-stream/transform.js +1 -0
  684. package/node_modules/readable-stream/writable-browser.js +1 -0
  685. package/node_modules/readable-stream/writable.js +8 -0
  686. package/node_modules/safe-buffer/LICENSE +21 -0
  687. package/node_modules/safe-buffer/README.md +584 -0
  688. package/node_modules/safe-buffer/index.d.ts +187 -0
  689. package/node_modules/safe-buffer/index.js +62 -0
  690. package/node_modules/safe-buffer/package.json +37 -0
  691. package/node_modules/setimmediate/LICENSE.txt +20 -0
  692. package/node_modules/setimmediate/package.json +30 -0
  693. package/node_modules/setimmediate/setImmediate.js +186 -0
  694. package/node_modules/string_decoder/.travis.yml +50 -0
  695. package/node_modules/string_decoder/LICENSE +48 -0
  696. package/node_modules/string_decoder/README.md +47 -0
  697. package/node_modules/string_decoder/lib/string_decoder.js +296 -0
  698. package/node_modules/string_decoder/package.json +31 -0
  699. package/node_modules/util-deprecate/History.md +16 -0
  700. package/node_modules/util-deprecate/LICENSE +24 -0
  701. package/node_modules/util-deprecate/README.md +53 -0
  702. package/node_modules/util-deprecate/browser.js +67 -0
  703. package/node_modules/util-deprecate/node.js +6 -0
  704. package/node_modules/util-deprecate/package.json +27 -0
  705. package/openclaw.plugin.json +1 -0
  706. package/package.json +7 -4
  707. /package/node_modules/{p-limit/node_modules/yocto-queue → yocto-queue}/index.d.ts +0 -0
  708. /package/node_modules/{p-limit/node_modules/yocto-queue → yocto-queue}/index.js +0 -0
  709. /package/node_modules/{p-limit/node_modules/yocto-queue → yocto-queue}/license +0 -0
  710. /package/node_modules/{p-limit/node_modules/yocto-queue → yocto-queue}/package.json +0 -0
  711. /package/node_modules/{p-limit/node_modules/yocto-queue → yocto-queue}/readme.md +0 -0
@@ -0,0 +1,658 @@
1
+ # Archive extraction
2
+
3
+ `@openclaw/fs-safe/archive` extracts ZIP and TAR archives behind one API, with traversal checks, blocked-link-type rejection, and entry-count and byte budgets. When the native binding is available for the current platform, Rust streams ZIP, TAR, gzip, zstd, and bzip2 while TypeScript remains the sole policy owner; every accepted output is created fd-relative in a private staging root. Extraction then merges through the same safe-open boundary used by direct writes — a symlinked entry can't trick the merge into following an out-of-tree path.
4
+
5
+ TAR admission uses one Rust core compiled into both the native binding and a
6
+ bundled, import-free WebAssembly module. The guarded JavaScript fallback uses
7
+ that module for TAR/gzip and optional `jszip` for ZIP. TAR needs no optional
8
+ parser dependency, runtime download, install script, or consumer Rust toolchain.
9
+ Installs omitting optional dependencies can import every public subpath and use
10
+ TAR/gzip in `auto` or `off`; ZIP fallback still requires `jszip`.
11
+
12
+ ```ts
13
+ import { extractArchive, resolveArchiveKind } from "@openclaw/fs-safe/archive";
14
+ ```
15
+
16
+ ## `extractArchive`
17
+
18
+ ```ts
19
+ await extractArchive({
20
+ archivePath: "/srv/uploads/plugin.zip",
21
+ destDir: "/srv/workspace/plugins/plugin",
22
+ kind: "zip", // optional; resolveArchiveKind() can infer
23
+ timeoutMs: 15_000, // hard budget; active destination mutation is joined
24
+ stripComponents: 0, // tar-style strip-leading-dirs
25
+ entryModes: "clamp", // default; use "preserve" for archive rwx bits
26
+ entryFilter: ({ path, kind, size }) => "extract",
27
+ onFiltered: "reject-archive", // default; opt into "skip-entry" explicitly
28
+ limits: {
29
+ maxArchiveBytes: 256 * 1024 * 1024,
30
+ maxEntries: 50_000,
31
+ maxExtractedBytes: 512 * 1024 * 1024,
32
+ maxEntryBytes: 256 * 1024 * 1024,
33
+ maxMetaEntryBytes: 1024 * 1024,
34
+ maxEntryPathComponents: 256,
35
+ },
36
+ });
37
+ ```
38
+
39
+ ### Parameters
40
+
41
+ ```ts
42
+ type ExtractArchiveOptions = {
43
+ archivePath: string; // absolute path to the archive
44
+ destDir: string; // absolute destination directory; must already exist
45
+ timeoutMs: number; // positive wall-clock budget; <= 0/non-finite disables it
46
+ kind?: ArchiveKind; // "zip" | "tar" | "tar-zstd" | "tar-bzip2"
47
+ stripComponents?: number; // strip N leading dirs from entry paths
48
+ tarGzip?: boolean; // when archive is .tar.gz/.tgz
49
+ limits?: ArchiveExtractLimits;
50
+ logger?: ArchiveLogger; // { info?, warn? }
51
+ entryModes?: "clamp" | "preserve";
52
+ entryFilter?: (entry: { path: string; kind: ArchiveEntryKind; size: number }) =>
53
+ "extract" | "skip";
54
+ onFiltered?: "reject-archive" | "skip-entry";
55
+ };
56
+ ```
57
+
58
+ `entryModes` defaults to `"clamp"`: directories become `0o755`; files become
59
+ `0o644`, or `0o755` when the archived owner-execute bit is set. `"preserve"`
60
+ keeps archived read/write/execute bits. Both policies strip setuid, setgid, and
61
+ sticky bits, and neither applies archived ownership. An explicit zero mode,
62
+ including a mode containing only stripped special bits, stays zero under
63
+ `"preserve"`. Absent metadata defaults to `0o644` for files and `0o755` for
64
+ directories; ZIP UNIX creator records with zero attributes are explicit zero,
65
+ while non-UNIX ZIP records use the absent-metadata defaults.
66
+
67
+ TAR mode fields containing only NUL/ASCII-space padding use absent defaults.
68
+ Both backends recognize GNU binary modes, including signed values,
69
+ within JavaScript's safe-integer range before masking permission bits.
70
+ Malformed or unsupported mode fields consistently fall back to zero, matching
71
+ the former native behavior. This replaces JavaScript's decoder-dependent octal
72
+ prefix parsing, defaulting, or rejection for malformed fields. Ordinary octal,
73
+ absent, explicit zero, and supported GNU binary fields retain their behavior.
74
+
75
+ Final modes remain separate from private working staging permissions: files
76
+ stay `0o600` and directories `0o700` until publication. Files receive their final
77
+ mode through the guarded copy's owned writer descriptor. Directories are pinned
78
+ before descending and finalized after their children, including empty and
79
+ restrictive directories. Explicit accepted directory modes win regardless of
80
+ archive order; implicit parents receive `0o755`. Existing destination directories
81
+ also receive the requested final mode. They are never temporarily widened to
82
+ allow child writes; insufficient write/search access still rejects.
83
+
84
+ Directory mode changes use a retained no-follow read descriptor when possible.
85
+ On macOS x64/arm64, read-denied directories can use a retained search descriptor.
86
+ On Linux x64/arm64, the Node-only search route retains an `O_PATH` descriptor and
87
+ changes modes through its exact `/proc/self/fd/N` reference after verifying the
88
+ procfs namespace and followed identity. The descriptor stays open through the
89
+ operation and verification; original root, ancestor and named-directory checks
90
+ still apply. This route trusts host mount-namespace integrity and does not claim
91
+ atomic ancestry checks or protection against privileged mount replacement.
92
+ Readable directories do not depend on procfs. A Linux search-only directory
93
+ needing a mode change requires accessible, genuine procfs; unavailable or
94
+ untrusted authority rejects explicitly instead of silently accepting a wrong
95
+ mode. Other unsupported search-only routes also fail closed. Windows retains
96
+ its existing bounded lack of POSIX mode enforcement.
97
+
98
+ Native extraction is deliberately split into two phases. Rust first reports an
99
+ entry manifest without creating paths. TypeScript validates paths, applies
100
+ `stripComponents`, filters, limits, and mode policy, then passes an explicit
101
+ accepted-entry plan back to Rust. Rust owns raw-stream admission, decompression,
102
+ and fd-relative `mkdirBeneath`/exclusive-open writes. This keeps filter policy identical
103
+ between native and JavaScript paths rather than reimplementing it in Rust.
104
+
105
+ ZIP extraction and bounded reads admit every physical central-directory record and its referenced local header before either decoder can normalize or collapse names. Raw names and valid Unicode Path names must pass traversal checks before stripping, filtering, or selecting a requested member; duplicate or colliding names reject with `entry-path`, even in unrelated or skipped members. Materially conflicting local/central or Unicode interpretations, malformed critical metadata, and ambiguous framing reject with `ArchiveFormatError`. Harmless separator and dot-component equivalence is allowed only after validation. Ordinary legacy filename decoding remains backend-selected.
106
+
107
+ `stripComponents` removes leading nonempty, non-`.` path components after
108
+ normalizing separators. For example, `./pkg/hello.txt` with
109
+ `stripComponents: 1` extracts to `hello.txt` on both backends. Entries with no
110
+ remaining components are skipped before the filter callback, but still count
111
+ toward `maxEntries` and undergo traversal validation. JavaScript TAR extraction
112
+ copies the admitted payload range to this accepted output path, so depth checks,
113
+ collision checks, writes, and mode application agree.
114
+
115
+ An `entryFilter` sees the validated **canonical effective archive path before
116
+ stripping**, entry kind, and declared size. On every JavaScript and native
117
+ ZIP/TAR backend (including gzip and native zstd/bzip2), backslashes become `/`,
118
+ empty and `.` components are removed, and trailing separators are removed from
119
+ directory paths. For example, `./pkg//state\cache/value` is presented as
120
+ `pkg/state/cache/value`, even with `stripComponents: 1`. Case and Unicode
121
+ spelling are preserved. Local PAX `path`, GNU long-name, and supported ZIP
122
+ Unicode Path names use the same canonicalization.
123
+
124
+ Raw paths undergo traversal, absolute/drive-path, and NUL validation **before**
125
+ canonicalization; normalization cannot turn an unsafe path into an accepted
126
+ one. Stripping and output collision checks use this same canonical identity.
127
+ On Windows, archive admission also rejects reserved device segments such as
128
+ `NUL`, `CON.txt`, and `nul .txt` with `ArchiveSecurityError("entry-path")`,
129
+ before extraction or bounded member reads. Ignored trailing spaces in the stem
130
+ before an extension do not bypass this check. Ordinary members with these names
131
+ remain valid on POSIX; this is a host-specific device guard, not a portable-name
132
+ restriction.
133
+ Filters that compare exact strings should use canonical pre-strip paths,
134
+ including directory names without a trailing `/`.
135
+ Returning `"skip"` rejects the whole archive unless `onFiltered` is
136
+ explicitly `"skip-entry"`. Runtime values other than `"reject-archive"` and
137
+ `"skip-entry"` reject before extraction starts instead of falling through to
138
+ skip behavior. Path traversal and archive-wide entry-count checks still apply
139
+ to skipped entries.
140
+ `maxEntryBytes` and `maxExtractedBytes` charge only entries accepted after
141
+ stripping and filtering. Skipping a large member does not consume these payload
142
+ budgets. The separate complete-stream decoded limit still applies to all TAR
143
+ content, including skipped or fully stripped members.
144
+
145
+ For example, a fleet restore can omit regenerated cache entries while rejecting
146
+ any other policy mismatch by default:
147
+
148
+ ```ts
149
+ await extractArchive({
150
+ archivePath: snapshotPath,
151
+ destDir: restoreRoot,
152
+ timeoutMs: 30_000,
153
+ entryFilter: ({ path: entryPath, kind }) =>
154
+ kind === "directory" && entryPath === "state/cache"
155
+ ? "skip"
156
+ : entryPath.startsWith("state/cache/")
157
+ ? "skip"
158
+ : "extract",
159
+ onFiltered: "skip-entry",
160
+ limits: { maxEntries: 50_000, maxEntryPathComponents: 64 },
161
+ });
162
+ ```
163
+
164
+ If skipping was not explicitly part of the restore contract, omit
165
+ `onFiltered`; the first `"skip"` then rejects the complete archive with
166
+ `ArchiveSecurityError("entry-filtered")`.
167
+
168
+ Both TAR routes finish bounded admission before TypeScript policy evaluation,
169
+ so a rejected plan never starts extraction. The JavaScript path owns its input,
170
+ decoder, and WASM parser streams, joining their teardown on validation, write,
171
+ or timeout failure.
172
+
173
+ TAR character devices, block devices, and FIFOs are presented to the filter as
174
+ `kind: "other"`. Accepted entries of these types reject with
175
+ `ArchiveSecurityError("entry-link")`; an explicit `"skip-entry"` filter can omit
176
+ them. GNU typeflag `D` (`GNUDumpDir`) is a directory on both backends, including
177
+ its filter kind, canonical path, and directory creation policy. Its declared
178
+ body size follows the existing TAR strip/filter payload budgets; dump contents
179
+ are not restored as files.
180
+
181
+ Unsupported logical TAR records, including volume headers (`V`), Solaris ACL
182
+ records (`A`), inodes (`I`), continuations (`M`), and unrecognized typeflags,
183
+ still undergo entry counting, raw/effective path validation, stripping, depth
184
+ and output collision checks in physical order. Each remaining record reaches
185
+ `entryFilter` once with its canonical pre-strip path, `kind: "other"`, and
186
+ declared effective size. A filter skip rejects with `"entry-filtered"` unless
187
+ `onFiltered: "skip-entry"` is explicit. Accepted unsupported records are safely
188
+ omitted and do not consume output payload budgets. The shared core admits these records explicitly. GNU long names describe one such
189
+ record and are then cleared; local PAX on unsupported types and GNU sparse
190
+ `S` records retain their existing fail-closed format policy.
191
+
192
+ If `kind` is omitted, the helper calls `resolveArchiveKind(archivePath)` and throws if the extension is not recognized. Pass `kind` explicitly when the archive name doesn't carry the type (e.g. content-addressed names). Archive inputs must remain regular files from preview through descriptor admission; POSIX opens are no-follow and nonblocking, so a FIFO swap cannot stall before deadline checks resume. A positive finite `timeoutMs` is a wall-clock budget; zero, negative, `NaN`, and infinity disable the deadline. Non-mutating work rejects promptly when the budget expires. If a live destination mutation is already in flight, rejection waits only for that mutation and any rollback to finish; no later destination mutation can begin.
193
+
194
+ The destination merge is nontransactional: each file is published atomically,
195
+ but completed files and directories can remain when a later copy, post-copy
196
+ check, mode application, or deadline fails. This also applies to
197
+ `mergeExtractedTreeIntoDestination()`. Guarded `Root.copyIn()` owns cleanup for
198
+ its operation; the archive merge does not unlink the current destination name
199
+ on error because it has no publication receipt proving ownership. A failure
200
+ before publication preserves a pre-existing file, and rejection does not grant
201
+ authority to delete a substituted file or alias. Failed extraction does not
202
+ restore overwritten contents. Active destination mutations and their guarded
203
+ cleanup still finish before rejection; no later destination mutation begins.
204
+ New directories whose postorder finalization was never reached can retain their
205
+ private working mode after failure. Failure cleanup closes retained descriptors;
206
+ it does not run a final chmod sweep or roll back the archive. The public merge
207
+ helper still derives modes from its external source tree and must be able to
208
+ read that source; it never chmods an unreadable external source to admit it.
209
+
210
+ ### Limits
211
+
212
+ ```ts
213
+ type ArchiveExtractLimits = {
214
+ maxArchiveBytes?: number; // refuse if archivePath stat'd size exceeds this
215
+ maxEntries?: number; // refuse before extracting if entry count > this
216
+ maxExtractedBytes?: number; // cap total payload bytes accepted after strip/filter
217
+ maxEntryBytes?: number; // cap one accepted entry after strip/filter
218
+ maxMetaEntryBytes?: number; // refuse one PAX/GNU metadata body above this
219
+ maxEntryPathComponents?: number; // bound output path depth after stripComponents
220
+ };
221
+ ```
222
+
223
+ Defaults exist for each (`DEFAULT_MAX_ARCHIVE_BYTES_ZIP`, `DEFAULT_MAX_ENTRIES`, `DEFAULT_MAX_EXTRACTED_BYTES`, `DEFAULT_MAX_ENTRY_BYTES`, `DEFAULT_MAX_META_ENTRY_BYTES`, `DEFAULT_MAX_ENTRY_PATH_COMPONENTS`). An explicit zero remains zero rather than selecting the default. `maxEntries` counts every archive entry, including entries removed by `stripComponents` or an explicit filter. The path-component default is 256. It is evaluated after `stripComponents` and before TypeScript accepts an entry for either JavaScript or native extraction, so rejected entries cannot cause implicit parent-directory creation. The same resolved 1 MiB metadata default applies to the native and WASM core.
224
+
225
+ A limit violation throws `ArchiveLimitError`. Its constant and string code are:
226
+
227
+ | Constant | Code |
228
+ |---|---|
229
+ | `ARCHIVE_SIZE_EXCEEDS_LIMIT` | `archive-size-exceeds-limit` |
230
+ | `ENTRY_COUNT_EXCEEDS_LIMIT` | `archive-entry-count-exceeds-limit` |
231
+ | `EXTRACTED_SIZE_EXCEEDS_LIMIT` | `archive-extracted-size-exceeds-limit` |
232
+ | `DECODED_SIZE_EXCEEDS_LIMIT` | `archive-decoded-size-exceeds-limit` |
233
+ | `ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT` | `archive-entry-extracted-size-exceeds-limit` |
234
+ | `META_ENTRY_SIZE_EXCEEDS_LIMIT` | `archive-meta-entry-size-exceeds-limit` |
235
+ | `ENTRY_PATH_COMPONENTS_EXCEEDS_LIMIT` | `archive-entry-path-components-exceeds-limit` |
236
+ | `MANIFEST_SIZE_EXCEEDS_LIMIT` | `archive-manifest-size-exceeds-limit` |
237
+
238
+ `MANIFEST_SIZE_EXCEEDS_LIMIT` is an active internal TAR admission limit, shared
239
+ by JavaScript and native extraction and bounded reads. Each logical member,
240
+ including ignored, filtered, and fully stripped members, charges
241
+ `64 + 2 * UTF-8 byte length of its effective pre-strip path` before emission or
242
+ retention. PAX/GNU metadata headers do not themselves charge a member cost.
243
+ The allowance is independent of `maxArchiveBytes`: derive a per-member path
244
+ allowance of `max(256, min(maxMetaEntryBytes, max(1, maxEntryPathComponents) * 256))`,
245
+ apply the same 64-byte overhead and doubled path cost, multiply by `maxEntries`,
246
+ and cap the total at 64 MiB using saturating arithmetic. Zero and very large
247
+ public limits remain deterministic. There is no public `maxManifestBytes`
248
+ option; this charged manifest budget supplements the decoded and metadata
249
+ limits rather than bounding the complete process heap.
250
+
251
+ Catch and branch on the code to surface a meaningful response to the caller.
252
+
253
+ Entry policy failures throw `ArchiveSecurityError`. Its entry-related codes
254
+ are `"entry-path"`, `"entry-link"`, and `"entry-filtered"`; destination-race
255
+ codes remain `"destination-not-directory"`, `"destination-symlink"`, and
256
+ `"destination-symlink-traversal"`.
257
+
258
+ ## What it defends against
259
+
260
+ - **Path traversal:** entries with `..`, absolute paths, NUL bytes, or Windows drive-relative segments such as `C:secret` and `nested/C:secret` are rejected (`ArchiveSecurityError`). On Windows, path segments containing `:` are also rejected as alternate data stream names before either backend writes to the filesystem.
261
+ - **Symlink/hardlink entries:** rejected by default, including ZIP entries whose Unix mode says symlink while their name ends in a slash or their DOS directory bit is set. An explicit `entryFilter` with `onFiltered: "skip-entry"` can omit these entries. Some archives ship symlink/hardlink entries that point outside the destination once resolved; `extractArchive` does not follow them.
262
+ - **Ambiguous output names:** duplicate names and distinct names that collide after `stripComponents`, case normalization, or Unicode normalization are rejected instead of relying on backend- or volume-specific overwrite order.
263
+ - **TOCTOU during merge:** extraction first writes to a private temp dir, then merges into `destDir` using the same boundary checks as `root().write()`. Destination symlink swaps are checked with the selected platform mechanism; non-Linux routes retain the best-effort race window documented in the [security model](security-model.md#containment-guarantees-by-platform).
264
+ - **Zip bombs:** `maxExtractedBytes` and `maxEntryBytes` apply to *post-decompression* bytes, so highly-compressed payloads hit the cap before they exhaust disk.
265
+ - **Corrupt ZIP payloads:** streamed output must match both the central-directory CRC and declared uncompressed size before it can leave private staging.
266
+ - **Gzip container integrity:** every concatenated gzip member must have a complete valid header, body, CRC32, and ISIZE trailer. A completed member may be followed by all-zero compressed-container padding (including system-tar stdout padding), bounded by the original archive-byte limit. The padding must remain zero through physical EOF; nonzero bytes or another member after padding reject. Truncation and corruption reject before publication or selected bytes return on both backends. Compressed padding is separate from decoded TAR EOF and does not bypass its checks.
267
+ - **Slow-loris archives:** `timeoutMs` is a hard wall-clock budget for non-mutating work. Extraction is aborted on overrun; if a destination mutation is already in flight, that mutation and rollback are joined before rejection so archive-controlled publication cannot continue afterward.
268
+ - **Metadata bombs:** a streaming pass-through reader rejects oversized PAX, GNU long-name, and GNU long-link bodies before buffering their bodies. It understands octal and base-256 fixed sizes and validates bounded local PAX bodies before using their size overrides for member framing. Original archive bytes remain unchanged.
269
+
270
+ ### Raw TAR framing
271
+
272
+ Extraction and bounded reads admit the complete decoded TAR stream through the
273
+ shared Rust core. This applies to plain TAR,
274
+ gzip, and native-supported zstd/bzip2, without changing native-mode availability
275
+ or fallback policy. The native and WASM builds enforce the same
276
+ framing rules:
277
+
278
+ - Every nonzero header must have a valid unsigned octal checksum, delimited
279
+ within its field. Checksum validation precedes metadata allocation and member
280
+ policy. Fixed name, prefix, and linkname fields require strict UTF-8 and NUL
281
+ padding. Raw hardlink (`1`) and symlink (`2`) headers require a nonempty
282
+ linkname; every other type, including PAX/GNU metadata, requires an empty
283
+ linkname. This check precedes metadata handling and member/filter policy.
284
+ - Directory (`5`), hardlink (`1`), and symlink (`2`) raw headers must declare
285
+ zero body bytes, whether or not local PAX metadata is present. Valid zero-size
286
+ links remain subject to the existing link/filter policy.
287
+ - EOF requires two consecutive, complete 512-byte zero blocks at a header
288
+ boundary. A header after just one zero block, a missing/partial EOF marker,
289
+ and any nonzero bytes after EOF reject. Additional zero padding after EOF may
290
+ have any byte length within the decoded ceiling; zero blocks inside a declared
291
+ member body are payload.
292
+ - Headers and padded bodies must be complete. Size fields accept unsigned
293
+ octal with ASCII-space/NUL padding or supported positive base-256 encoding;
294
+ malformed numbers and non-padding bytes after a NUL reject. Raw sizes and
295
+ padded sizes must fit `Number.MAX_SAFE_INTEGER`, even with PAX overrides,
296
+ before member budgets are considered.
297
+
298
+ Framing failures use `ArchiveFormatError("archive-header-invalid")`, except
299
+ invalid UTF-8 or nonzero bytes after the first NUL in fixed name, linkname,
300
+ and USTAR prefix fields, which use `ArchiveSecurityError("entry-path")`.
301
+ Missing linknames on links and nonempty linknames on non-links still use the
302
+ format error. PAX `x` and GNU long-name/long-link `L`/`K` payloads retain their
303
+ existing support and metadata limits; the zero-body rule is not applied to all
304
+ non-regular types.
305
+ PAX effective sizes determine regular-member framing. Admission preserves input
306
+ bytes and emits an ordered manifest with exact effective names, types, modes,
307
+ sizes, and decoded payload offsets. TypeScript owns filtering, stripping,
308
+ collisions, permissions, and accepted-output limits. Executors replay admitted
309
+ ranges from the immutable staged input; no second TAR parser interprets PAX,
310
+ GNU names, or payload lengths. Native writes remain descriptor-relative;
311
+ JavaScript writes use the shared guarded private staging and pinned-write helpers.
312
+
313
+ Original member names and USTAR prefixes are validated even when overridden.
314
+ Non-padding bytes after a fixed path field's NUL terminator reject. The core
315
+ enforces the 255-byte component ceiling under NFC and NFD, including Hangul
316
+ expansion. Every replay drains and validates physical EOF before publication or
317
+ returning selected bytes. Unrequested, filtered, and stripped members cannot
318
+ bypass validation. Decompression remains streaming; no complete decoded archive
319
+ is retained in memory or written to a decoded spool.
320
+
321
+ The WASM transport has a fixed 64 KiB input buffer, one pending member event,
322
+ and a 256 MiB maximum linear memory per isolated parser instance. Metadata is
323
+ bounded before allocation; allocation failure rejects. Stream backpressure
324
+ bounds queued chunks, and completion/error destroys the instance's parser
325
+ state. The manifest retains the existing charged budget below; linear memory
326
+ is an additional execution resource bound, not a new public limit option.
327
+
328
+ The raw meter enforces `maxEntries` before consuming each logical member's body,
329
+ including members later skipped by filtering or stripping. PAX/GNU metadata
330
+ headers do not count as members; their payloads use `maxMetaEntryBytes`.
331
+ The meter does not receive `maxEntryBytes` or `maxExtractedBytes`: those payload
332
+ budgets apply only after strip/filter acceptance, using declared effective
333
+ sizes and excluding block padding. JavaScript's entry checker and the native
334
+ accepted-plan builder retain this shared policy. Every TAR admission/parser
335
+ pass has a separate absolute decoded ceiling:
336
+ `maxExtractedBytes + maxArchiveBytes`, safely clamped to
337
+ `Number.MAX_SAFE_INTEGER` (768 MiB with defaults). It counts every admitted
338
+ decoded byte: headers, bodies, metadata, all block padding, both EOF blocks,
339
+ and zero padding after EOF. It bounds complete decoding before parser policy,
340
+ including all filtered/stripped content; cumulative metadata and zero tails
341
+ cannot bypass this bound. Exceeding this ceiling throws
342
+ `ArchiveLimitError("archive-decoded-size-exceeds-limit")`.
343
+
344
+ The same TypeScript helper derives the ceiling for JavaScript and every native
345
+ TAR pass. Before selecting a backend, it caps internal metadata/decoded limits at
346
+ `Number.MAX_SAFE_INTEGER` and logical entry counts at `2^32 - 1`. Larger finite
347
+ options such as `Number.MAX_VALUE` remain valid; high-level payload budgets keep
348
+ their large values. The decoded ceiling uses clamped `maxExtractedBytes` and
349
+ archive overhead with safe addition. Ordinary limits, including
350
+ zero and the existing defaulting/rounding rules, retain their behavior.
351
+ There is no new public option. This is an absolute decoded admission
352
+ cap, not a decompression-ratio policy; bounded stream/codec read-ahead remains.
353
+ There is no independent TAR parser decompression-ratio threshold.
354
+
355
+ ### Bounded local PAX support
356
+
357
+ Extraction and single-entry reads accept one nonempty local POSIX `x` header
358
+ (USTAR or GNU header format) immediately before one regular/contiguous file,
359
+ directory, symlink, or hardlink. `path`, `linkpath`, and `size` override that
360
+ member only. Effective paths still pass traversal validation before stripping,
361
+ then the output paths pass depth and collision checks. The filter receives the
362
+ canonical effective pre-strip path, followed by link policy checks. PAX never
363
+ permits link creation. Effective sizes drive framing, filters, and the existing output-byte
364
+ budgets; `maxEntries` still counts members, not their metadata headers.
365
+
366
+ Records must have exact byte lengths, ASCII keys, a final newline, and no
367
+ duplicate keys or unconsumed bytes. `path` and `linkpath` must be nonempty strict
368
+ UTF-8 without NUL. Unicode, a leading BOM, numeric-looking names, and embedded
369
+ newlines preserve their exact spelling; newlines inside a byte-counted value
370
+ are data. Windows filesystem filename restrictions still apply during creation.
371
+ Ownership names retain the existing nonempty printable-ASCII contract. Raw name,
372
+ USTAR prefix, and link fields still require strict UTF-8 and NUL padding even
373
+ when metadata overrides them. Raw link targets must be present only on links.
374
+ `size`, `uid`, and `gid`
375
+ must be canonical unsigned decimal safe integers (zero is valid; signs, leading
376
+ zeros, fractions, and exponents are not). Padded member sizes must also fit the
377
+ safe integer range. Raw and effective directory/link sizes must both be zero;
378
+ non-directory paths ending with a separator and `linkpath` on non-links are
379
+ rejected rather than allowing parser-specific type or framing changes.
380
+
381
+ The descriptive allowlist is `mtime`, `atime`, `ctime` (signed decimal seconds
382
+ with optional fractional digits, within JavaScript's Date range), `uid`, `gid`,
383
+ `uname`, and `gname`. These attributes are accepted but not restored to the
384
+ destination. `LIBARCHIVE.xattr.*` and `SCHILY.xattr.*` with nonempty ASCII
385
+ alphanumeric/dot/underscore/hyphen suffixes are also accepted as inert metadata,
386
+ never restored as extended attributes. Their values are byte-counted and may
387
+ contain NUL, non-UTF8 bytes, or newlines, including macOS provenance metadata.
388
+
389
+ Global `g`, old `X`, old GNU `N`, empty/dangling/repeated local headers, mixed
390
+ PAX/GNU extension chains, unknown keys, charset declarations, ACL extensions,
391
+ and all sparse extensions (including `GNU.sparse.*`, `SCHILY.filetype`,
392
+ `SCHILY.realsize`, and `SCHILY.size`) fail closed with
393
+ `ArchiveFormatError("archive-header-invalid")`. GNU sparse extension blocks are still
394
+ metered in 512-byte units before rejection, preserving metadata-limit errors
395
+ for excessive chains. The per-body `maxMetaEntryBytes` limit bounds PAX storage
396
+ and duplicate-key state; one local header per member prevents local metadata
397
+ chains without introducing a new limit or changing defaults.
398
+
399
+ ### Bounded GNU long names and links
400
+
401
+ The shared core buffers GNU long-name `L` and long-link `K` bodies within
402
+ `maxMetaEntryBytes`. A body must contain a nonempty
403
+ UTF-8 name, with either no NUL or exactly one terminal NUL. Embedded NULs,
404
+ additional terminal NULs, bytes after a NUL, and invalid UTF-8 reject with
405
+ `ArchiveFormatError("archive-header-invalid")`. The core preserves original
406
+ archive bytes, including the optional terminator and block padding.
407
+
408
+ One logical member may have at most one `L` and one `K`, in either order.
409
+ Repeated metadata of either kind, mixed PAX/GNU chains in either direction,
410
+ and GNU metadata without a following member reject with the same format error.
411
+ Pending metadata is cleared only when its described member is admitted;
412
+ metadata records do not count toward `maxEntries`.
413
+
414
+ An `L` name undergoes raw-path validation before parser normalization, stripping,
415
+ or filtering; unsafe paths reject with `ArchiveSecurityError("entry-path")`.
416
+ The validated name remains pending until its described header arrives. An
417
+ effective name ending in `/` or `\` requires raw directory type `5` or `D`;
418
+ other types reject with `ArchiveFormatError` before filtering, preventing the
419
+ parsers from disagreeing about a member's type.
420
+ `K` validates encoding and NUL structure without authorizing link creation.
421
+ Normal link/filter policy still governs the described member. Canonical
422
+ pre-strip filter paths, decoded-stream ceilings, and physical EOF checks apply
423
+ to plain/gzip TAR and native zstd/bzip2 alike.
424
+
425
+ ## `inspectTarArchive`
426
+
427
+ Inspect accepted TAR members without creating an extracted tree. This operation
428
+ uses the same complete Rust/WASM admission and TypeScript extraction planner as
429
+ `extractArchive`, with zero stripping. It detects plain TAR or gzip from the
430
+ input bytes; ZIP, zstd, and bzip2 are not part of this inspection API.
431
+
432
+ ```ts
433
+ import { inspectTarArchive } from "@openclaw/fs-safe/archive";
434
+
435
+ const entries = await inspectTarArchive({
436
+ archivePath: "/srv/uploads/tree.tar.gz",
437
+ timeoutMs: 30_000,
438
+ limits: {
439
+ maxArchiveBytes: 16 * 1024 * 1024,
440
+ maxEntries: 5_000,
441
+ maxEntryBytes: 16 * 1024 * 1024,
442
+ maxExtractedBytes: 64 * 1024 * 1024,
443
+ },
444
+ entryFilter: ({ kind }) => kind === "file" || kind === "directory" ? "extract" : "skip",
445
+ onFiltered: "reject-archive",
446
+ });
447
+ ```
448
+
449
+ `InspectTarArchiveOptions` accepts `archivePath`, `timeoutMs`, `limits`,
450
+ `entryFilter`, and `onFiltered`, with the same defaults and error classes as
451
+ extraction. The result is a frozen array of frozen `InspectedTarEntry` records:
452
+ `{ path: string; kind: "file" | "directory"; size: number }`, in archive order.
453
+ `size` is the effective declared payload length. `path` is extraction's
454
+ canonical pre-strip identity: case, Unicode spelling, BOM, and embedded LF are
455
+ preserved, while separators and dot components follow the existing archive path
456
+ contract. No human-readable tar listing or escape decoding is involved.
457
+
458
+ Full framing, gzip integrity, EOF, metadata, decoded-byte, and manifest-budget
459
+ validation finishes before the caller's filter runs. The shared planner then
460
+ applies traversal, collision, depth, blocked-type, and accepted-payload limits.
461
+ A failure returns no partial result. Filter callbacks are decisions, not admission
462
+ receipts: later policy, collision, or budget checks can still reject the archive.
463
+ Only the resolved Promise/result is authorization-worthy; do not perform
464
+ irreversible actions from a filter callback.
465
+
466
+ Root-only records count toward entry limits but produce no result; PAX/GNU metadata headers are not members. Only accepted
467
+ file/directory members appear, not implicit parent directories. Unsupported
468
+ records follow extraction's omission policy unless the filter rejects them, as
469
+ in the example. AppleDouble records encoded as ordinary files are ordinary
470
+ members, not hidden metadata.
471
+
472
+ Inspection pins and privately stages its input, then cleans up that copy. It
473
+ neither creates destination paths nor tests destination permissions or platform
474
+ filename restrictions. Its result is evidence about those inspected bytes, not
475
+ an extraction capability or a promise that a later file at `archivePath` is
476
+ unchanged. Callers making authorization decisions must retain the same private
477
+ immutable archive or verify byte identity before extracting with matching
478
+ filter/limit settings. Extraction always performs its own admission and guarded
479
+ publication. Native `off`, `auto`, and `require` retain their existing selection
480
+ and availability semantics; inspection does not fall back after native failure.
481
+
482
+ ## `resolveArchiveKind`
483
+
484
+ ```ts
485
+ import { resolveArchiveKind, type ArchiveKind } from "@openclaw/fs-safe/archive";
486
+
487
+ const kind = resolveArchiveKind("upload.zip"); // "zip"
488
+ const tar = resolveArchiveKind("upload.tar.gz"); // "tar"
489
+ const zstd = resolveArchiveKind("upload.tar.zst"); // "tar-zstd" when native is available
490
+ const unknown = resolveArchiveKind("upload.bin"); // null
491
+ ```
492
+
493
+ Recognizes:
494
+
495
+ - `*.zip` → `"zip"`
496
+ - `*.tar`, `*.tar.gz`, `*.tgz` → `"tar"`
497
+ - `*.tar.zst`, `*.tar.zstd`, `*.tzst` → `"tar-zstd"` (native only)
498
+ - `*.tar.bz2`, `*.tbz2`, `*.tbz` → `"tar-bzip2"` (native only)
499
+
500
+ Returns `null` for unknown extensions; check the result before calling
501
+ `extractArchive` if the filename is caller-controlled. A recognized zstd or
502
+ bzip2 TAR extension with no native binding throws the typed
503
+ `FsSafeError("helper-unavailable")` with installation guidance. This includes
504
+ `mode: "off"`; those two formats have no JavaScript fallback.
505
+
506
+ For a service whose input contract requires zstd, configure native mode before
507
+ the first archive call so a packaging mistake fails at the boundary:
508
+
509
+ ```ts
510
+ import { configureFsSafeNative } from "@openclaw/fs-safe/config";
511
+ import { extractArchive } from "@openclaw/fs-safe/archive";
512
+
513
+ configureFsSafeNative({ mode: "require" });
514
+ await extractArchive({
515
+ archivePath: "/srv/restore/snapshot.tar.zst",
516
+ destDir: "/srv/restore/staging",
517
+ kind: "tar-zstd",
518
+ timeoutMs: 60_000,
519
+ });
520
+ ```
521
+
522
+ ## `readArchiveEntry`
523
+
524
+ `readArchiveEntry(archivePath, entryPath, { maxBytes, kind? })` reads one
525
+ regular-file entry into a bounded `Buffer` without extracting a tree. It pins
526
+ and privately stages the archive input, rejects link, directory, and duplicate
527
+ entries, verifies ZIP CRC and declared size,
528
+ and throws `ArchiveLimitError` if the requested entry's output exceeds
529
+ `maxBytes`. ZIP output within that cap must match the declared uncompressed
530
+ size exactly; either a shorter or longer payload throws
531
+ `ArchiveFormatError("archive-header-invalid")` before bytes are returned on
532
+ both JavaScript and native backends.
533
+ For TAR, `maxBytes` applies only to that requested entry: a larger
534
+ unrequested member remains valid within the default archive admission limits.
535
+ TAR traversal uses default entry-count, compressed-input, and metadata limits,
536
+ plus the 768 MiB decoded ceiling derived from default extracted/archive byte
537
+ limits. It does not apply payload budgets to unrequested members. ZIP
538
+ inputs retain the archive subpath's 256 MiB compressed-input ceiling.
539
+ With a native binding it uses the same Rust decoders as extraction, including
540
+ zstd and bzip2 TAR. Without native it retains the JS ZIP/TAR/gzip implementation.
541
+
542
+ Requested paths and effective member names use extraction's canonical pre-strip
543
+ identity: backslashes become `/`, and repeated separators and `.` components
544
+ are removed after raw-path validation. For example, `./pkg//value` and
545
+ `pkg\value` both address `pkg/value`, including supported GNU/PAX and ZIP
546
+ Unicode Path names. Case and Unicode spelling are preserved. Requests ending
547
+ in `/` or `\` still reject as non-files. Canonical duplicate members reject
548
+ before an unrelated requested entry can be returned.
549
+
550
+ ```ts
551
+ const rawManifest = await readArchiveEntry(uploadPath, "package/manifest.json", {
552
+ maxBytes: 64 * 1024,
553
+ });
554
+ const manifest = JSON.parse(rawManifest.toString("utf8")) as PluginManifest;
555
+ validatePluginManifest(manifest);
556
+ ```
557
+
558
+ ## Lower-level building blocks
559
+
560
+ The archive subpath also exports the helpers `extractArchive` is built on. Most callers will not need them, but they are stable and documented:
561
+
562
+ | Function | Purpose |
563
+ |---|---|
564
+ | `withStagedArchiveDestination(opts)` | Creates a private staging dir outside the destination, calls your `run(stagingDir)`, then cleans it up. |
565
+ | `mergeExtractedTreeIntoDestination(opts)` | The merge step alone — staged tree → destination through boundary checks. |
566
+ | `prepareArchiveDestinationDir(destDir)` | Canonicalizes and asserts the destination directory. |
567
+ | `prepareArchiveOutputPath({ destinationDir, destinationRealDir, relPath, outPath, originalPath, isDirectory, deadline? })` | Validates and prepares parents for an already-resolved entry output path. |
568
+ | `loadZipArchiveWithPreflight(buffer, limits?)` | Loads a JSZip from a `Buffer` or `Uint8Array` with size/entry-count preflight before unzipping. |
569
+ | `readZipCentralDirectoryEntryCount(buffer)` | Returns the entry count from an already-loaded ZIP `Buffer` or `Uint8Array` without decoding payloads. |
570
+ | `createTarEntryPreflightChecker(opts)` | Returns a per-entry checker for use as a `tar.x` `onReadEntry` hook. |
571
+
572
+ These let you build custom extractors that share the same safety machinery — for example, a streaming uploader that wants to refuse archives with too many entries before reading any payloads.
573
+
574
+ ## Path helpers
575
+
576
+ `archive-entry` exports a handful of low-level helpers for entry-path normalization:
577
+
578
+ ```ts
579
+ import {
580
+ isWindowsDrivePath,
581
+ normalizeArchiveEntryPath,
582
+ resolveArchiveOutputPath,
583
+ stripArchivePath,
584
+ validateArchiveEntryPath,
585
+ } from "@openclaw/fs-safe/archive";
586
+ ```
587
+
588
+ - `validateArchiveEntryPath(raw, opts)` — throws `ArchiveSecurityError` for `..`, absolute, NUL-containing, drive-relative, or otherwise unsafe entry paths, including alternate data stream names on Windows.
589
+ - `normalizeArchiveEntryPath(raw)` — converts backslashes in the entry path to forward slashes.
590
+ - `stripArchivePath(entryPath, n)` — normalize separators, drop empty and `.` components, then strip the leading N components, returning `null` if none remain.
591
+ - `resolveArchiveOutputPath({ rootDir, relPath, originalPath, escapeLabel? })` — combines the validated relative path with the root and rejects escapes using the original archive path for diagnostics.
592
+ - `isWindowsDrivePath(value)` — detects drive-relative segments such as `C:secret` or `nested/C:secret` that should be rejected.
593
+
594
+ Validate attacker-controlled paths before calling normalization or stripping
595
+ helpers. After validation, `stripArchivePath(entryPath, 0)` returns the canonical
596
+ pre-strip identity used by extraction filters (or `null` for an empty path).
597
+
598
+ ## Common patterns
599
+
600
+ ### Extract an upload, surface budget violations
601
+
602
+ ```ts
603
+ import { extractArchive, ArchiveLimitError, ARCHIVE_LIMIT_ERROR_CODE } from "@openclaw/fs-safe/archive";
604
+
605
+ try {
606
+ await extractArchive({
607
+ archivePath: upload.path,
608
+ destDir: targetDir,
609
+ kind: "zip",
610
+ timeoutMs: 30_000,
611
+ limits: {
612
+ maxArchiveBytes: 100 * 1024 * 1024,
613
+ maxEntries: 10_000,
614
+ maxExtractedBytes: 200 * 1024 * 1024,
615
+ maxEntryBytes: 50 * 1024 * 1024,
616
+ },
617
+ });
618
+ } catch (err) {
619
+ if (err instanceof ArchiveLimitError) {
620
+ return reply(413, { code: err.code, message: err.message });
621
+ }
622
+ throw err;
623
+ }
624
+ ```
625
+
626
+ ### Decide kind from MIME, not filename
627
+
628
+ ```ts
629
+ const kind: ArchiveKind = mime === "application/zip" ? "zip" : "tar";
630
+ await extractArchive({ archivePath, destDir, kind, timeoutMs: 10_000 });
631
+ ```
632
+
633
+ ### Stage to private dir, then commit as a directory
634
+
635
+ ```ts
636
+ import { withTempWorkspace } from "@openclaw/fs-safe/temp";
637
+ import { replaceDirectoryAtomic } from "@openclaw/fs-safe/atomic";
638
+
639
+ await withTempWorkspace({ rootDir: "/srv/site/tmp", prefix: "extract-" }, async (ws) => {
640
+ await extractArchive({
641
+ archivePath: upload.path,
642
+ destDir: ws.dir,
643
+ timeoutMs: 30_000,
644
+ });
645
+ await replaceDirectoryAtomic({
646
+ stagedDir: ws.dir,
647
+ targetDir: "/srv/site/plugin",
648
+ });
649
+ });
650
+ ```
651
+
652
+ ## See also
653
+
654
+ - [Atomic writes](atomic.md) — `replaceDirectoryAtomic` for staged directory replacement.
655
+ - [Temp workspaces](temp.md) — extract into a private workspace and commit as one step.
656
+ - [Errors](errors.md) — `FsSafeError` codes the underlying writes can raise.
657
+ - [Migrating to 0.5](migrating-to-0.5.md) — clamp-default and native-format upgrade checklist.
658
+ - [`extractArchive` source](https://github.com/openclaw/fs-safe/blob/main/src/archive.ts).