@openclaw/feishu 2026.9.8 → 2026.10.1-beta.2

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 (225) hide show
  1. package/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
  2. package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
  3. package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
  4. package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
  5. package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
  6. package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
  7. package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
  8. package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
  9. package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
  10. package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
  11. package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
  12. package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
  13. package/dist/api.js +234 -297
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/doctor-contract-api.js +2 -2
  16. package/dist/session-binding-contract-api.js +1 -1
  17. package/dist/setup-api.js +1 -1
  18. package/dist/setup-entry.js +1 -1
  19. package/dist/subagent-hooks-api.js +1 -1
  20. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
  21. package/node_modules/@openclaw/fs-safe/README.md +70 -379
  22. package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
  23. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
  24. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  25. package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
  26. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  27. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
  28. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
  29. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
  30. package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
  31. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
  32. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
  33. package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
  34. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
  35. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
  36. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
  37. package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
  38. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
  39. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
  40. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
  41. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
  42. package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
  43. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
  44. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
  45. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
  46. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
  47. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
  48. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
  49. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
  50. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
  51. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
  52. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
  53. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
  54. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
  55. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
  56. package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
  57. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
  58. package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
  59. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
  60. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
  61. package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
  62. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
  63. package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
  64. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
  65. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
  66. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
  67. package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
  68. package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
  69. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
  70. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
  71. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
  72. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
  73. package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
  74. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
  75. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
  76. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
  77. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
  78. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
  79. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
  80. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
  81. package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
  82. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
  83. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
  84. package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
  85. package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
  86. package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
  87. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
  88. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
  89. package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
  90. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
  91. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
  92. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
  93. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
  94. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
  95. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
  96. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
  97. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
  98. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
  99. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
  100. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
  101. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
  102. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
  103. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
  104. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
  105. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
  106. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
  107. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
  108. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
  109. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
  110. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
  111. package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
  112. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
  113. package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
  114. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
  115. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
  116. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
  117. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
  118. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
  119. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
  120. package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
  121. package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
  122. package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
  123. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
  124. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
  125. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
  126. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
  127. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
  128. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
  129. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
  130. package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
  131. package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
  132. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
  133. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
  134. package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
  135. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
  136. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
  137. package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
  139. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
  140. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
  141. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
  142. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
  143. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
  144. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
  145. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
  146. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
  147. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
  148. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
  149. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
  150. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
  151. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
  152. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
  153. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
  154. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
  155. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
  156. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
  157. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
  158. package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
  159. package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
  160. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
  161. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
  162. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
  163. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
  164. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
  165. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
  166. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
  167. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
  168. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
  169. package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
  170. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
  171. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
  172. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
  173. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
  174. package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
  175. package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
  176. package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
  177. package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
  178. package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
  179. package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
  180. package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
  181. package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
  182. package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
  183. package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
  184. package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
  185. package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
  186. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
  187. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
  188. package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
  189. package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
  190. package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
  191. package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
  192. package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
  193. package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
  194. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
  195. package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
  196. package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
  197. package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
  198. package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
  199. package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
  200. package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
  201. package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
  202. package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
  203. package/node_modules/@openclaw/fs-safe/package.json +10 -10
  204. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  205. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  206. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  207. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  208. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  209. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  210. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  211. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  212. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  213. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  214. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  215. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  216. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  217. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  218. package/package.json +5 -5
  219. package/skills/feishu-wiki/SKILL.md +1 -1
  220. package/dist/.setup/accounts-wRqItHug.mjs +0 -206
  221. package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
  222. package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
  223. package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
  224. package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
  225. package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
@@ -22,7 +22,7 @@ export function handleSyncStaleAdmission(acquisition, params) {
22
22
  try {
23
23
  rawSnapshot = readSidecarLockRawSnapshotSync(acquisition.lockPath, {
24
24
  rejectNonFile: true,
25
- onOpenFailure: (error) => {
25
+ onOpenFailure: () => {
26
26
  lockFileOpenDenied = true;
27
27
  assertUnheld();
28
28
  },
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
2
3
  import { acquireFileLockSyncWithRoot } from "./file-lock-sync-root-acquire.js";
3
4
  import { withSyncHeldLockHandle } from "./file-lock-sync-root-held.js";
4
5
  import path from "node:path";
@@ -227,6 +228,13 @@ export function acquireFileLockSync(targetPath, options) {
227
228
  const noFollow = process.platform !== "win32" && typeof fs.constants.O_NOFOLLOW === "number"
228
229
  ? fs.constants.O_NOFOLLOW
229
230
  : 0;
231
+ try {
232
+ assertExclusiveCreateLeaf(lockPath);
233
+ }
234
+ catch (error) {
235
+ exclusiveCreateConflict = error.code === "EEXIST";
236
+ throw error;
237
+ }
230
238
  try {
231
239
  fd = fs.openSync(lockPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | noFollow, 0o600);
232
240
  }
@@ -12,6 +12,7 @@ import { prepareSecretFileWrite } from "./secret-file.js";
12
12
  import { resolveSecureTempRoot } from "./secure-temp-dir.js";
13
13
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
14
14
  import { readRegularFile } from "./regular-file.js";
15
+ import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
15
16
  import { errorCauseOptions } from "./root-errors.js";
16
17
  import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
17
18
  export async function ensureParentInRoot(scopedRoot, relativePath, mode) {
@@ -67,6 +68,7 @@ export async function writeStreamToTempSource(params) {
67
68
  let handle = null;
68
69
  let handleClosedByStream = false;
69
70
  try {
71
+ assertExclusiveCreateLeaf(filePath);
70
72
  handle = await fs.open(filePath, "wx", params.mode);
71
73
  const writable = handle.createWriteStream();
72
74
  writable.once("close", () => {
@@ -8,8 +8,9 @@ import { ensureParentSync, ensureStoreDirectorySync, } from "./file-store-bounda
8
8
  import { assertSyncStoreDirectoryReceipt } from "./file-store-sync-directory.js";
9
9
  import { isPathInside } from "./path.js";
10
10
  import { resolveReadOpenFlags } from "./read-open-flags.js";
11
- import { writeTempFileSync } from "./replace-file-descriptor.js";
12
- import { SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
11
+ import { writeTempFile } from "./replace-file-descriptor.js";
12
+ import { AtomicIo, runSync } from "./atomic-io.js";
13
+ import { AtomicTempOwner } from "./replace-file-temp-owner.js";
13
14
  import { errorCauseOptions } from "./root-errors.js";
14
15
  import { inspectFileIdentitySync } from "./strict-file-identity.js";
15
16
  import { getFsSafeTestHooks } from "./test-hooks.js";
@@ -75,35 +76,36 @@ export function writeFileSyncAtomic(params) {
75
76
  });
76
77
  }
77
78
  const tempPath = path.join(parentGuard.dir, `.fs-safe-${process.pid}-${randomUUID()}.tmp`);
78
- const owner = new SyncAtomicTempOwner(tempPath);
79
+ const io = AtomicIo.sync(fs);
80
+ const owner = new AtomicTempOwner(tempPath, io);
79
81
  let originalFailure;
80
82
  try {
81
83
  getFsSafeTestHooks()?.beforeFileStoreSyncPrivateWrite?.(filePath);
82
84
  assertSyncStoreDirectoryReceipt(parentGuard);
83
85
  owner.start();
84
- const temp = writeTempFileSync({
85
- fsModule: fs, tempPath, content: params.content, mode: params.mode, sync: false,
86
+ io.fchmodSync = (descriptor, mode) => {
87
+ try {
88
+ fs.fchmodSync(descriptor, mode);
89
+ }
90
+ catch {
91
+ // Best-effort on platforms that do not enforce POSIX modes.
92
+ }
93
+ };
94
+ const temp = runSync(writeTempFile(io, {
95
+ tempPath, content: params.content, mode: params.mode, sync: false,
86
96
  onIdentity: owner.onIdentity,
87
- fchmodSync: (descriptor, mode) => {
88
- try {
89
- fs.fchmodSync(descriptor, mode);
90
- }
91
- catch {
92
- // Best-effort on platforms that do not enforce POSIX modes.
93
- }
94
- },
95
- });
97
+ }));
96
98
  owner.adopt(temp);
97
99
  // Preserve the store's strict fsync errors; the generic temp helper tolerates EPERM.
98
100
  if (params.durable)
99
- fs.fsyncSync(temp.fd);
100
- verifyStoreFile(temp.fd, owner.identity, tempPath);
101
+ fs.fsyncSync(temp.file.fd);
102
+ verifyStoreFile(temp.file.fd, owner.identity, tempPath);
101
103
  assertSyncStoreDirectoryReceipt(parentGuard);
102
104
  fs.renameSync(tempPath, filePath);
103
105
  owner.markRenamed();
104
106
  assertSyncStoreDirectoryReceipt(parentGuard);
105
107
  try {
106
- verifyStoreFile(temp.fd, owner.identity, filePath);
108
+ verifyStoreFile(temp.file.fd, owner.identity, filePath);
107
109
  }
108
110
  catch (error) {
109
111
  if (error instanceof FsSafeError) {
@@ -123,6 +125,6 @@ export function writeFileSyncAtomic(params) {
123
125
  throw error;
124
126
  }
125
127
  finally {
126
- owner.finish({ fsModule: fs, originalFailure, throwOnCleanupError: false });
128
+ runSync(owner.finish({ originalFailure, throwOnCleanupError: false }));
127
129
  }
128
130
  }
@@ -8,7 +8,7 @@ import { writeFileSyncAtomic } from "./file-store-sync-write.js";
8
8
  import { createJsonStore } from "./json-document-store.js";
9
9
  import { stringifyJsonDocument } from "./json-stringify.js";
10
10
  import { isNotFoundPathError } from "./path.js";
11
- import { throwFsSafeReadError } from "./root-errors.js";
11
+ import { errorCauseOptions, throwFsSafeReadError } from "./root-errors.js";
12
12
  import { root } from "./root.js";
13
13
  import { DEFAULT_ROOT_MAX_BYTES } from "./root-impl.js";
14
14
  import { matchRootFileOpenFailure, openRootFileSync } from "./root-file.js";
@@ -38,9 +38,7 @@ function handleSyncStoreReadOpenFailure(opened) {
38
38
  if (isNotFound(failure.error)) {
39
39
  return null;
40
40
  }
41
- throw new FsSafeError("path-mismatch", "store target changed during read", {
42
- cause: failure.error instanceof Error ? failure.error : undefined,
43
- });
41
+ throw new FsSafeError("path-mismatch", "store target changed during read", errorCauseOptions(failure.error));
44
42
  },
45
43
  validation: (failure) => {
46
44
  if (failure.error instanceof FsSafeError) {
@@ -48,15 +46,11 @@ function handleSyncStoreReadOpenFailure(opened) {
48
46
  }
49
47
  // Validation failures mean the path existed but violated store policy
50
48
  // (directory, hardlink, symlink race). Do not report them as missing.
51
- throw new FsSafeError("path-mismatch", "store target failed read validation", {
52
- cause: failure.error instanceof Error ? failure.error : undefined,
53
- });
49
+ throw new FsSafeError("path-mismatch", "store target failed read validation", errorCauseOptions(failure.error));
54
50
  },
55
51
  io: (failure) => throwFsSafeReadError(failure.error, "store"),
56
52
  fallback: (failure) => {
57
- throw new FsSafeError("path-mismatch", "store target changed during read", {
58
- cause: failure.error instanceof Error ? failure.error : undefined,
59
- });
53
+ throw new FsSafeError("path-mismatch", "store target changed during read", errorCauseOptions(failure.error));
60
54
  },
61
55
  });
62
56
  }
@@ -1,12 +1,4 @@
1
- /**
2
- * Returns true when `fs.stat()` can stat the path.
3
- *
4
- * This follows stat semantics: broken symlinks return false, while symlinks to
5
- * existing targets return true.
6
- */
1
+ /** True if stat succeeds; follows symlinks, so broken links return false. */
7
2
  export declare function pathExists(filePath: string): Promise<boolean>;
8
- /**
9
- * Synchronous counterpart to `pathExists()`, with the same `fs.statSync()`
10
- * semantics.
11
- */
3
+ /** Synchronous {@link pathExists}. */
12
4
  export declare function pathExistsSync(filePath: string): boolean;
@@ -1,11 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import { pathForWindowsFilesystem } from "./windows-path-alias.js";
3
- /**
4
- * Returns true when `fs.stat()` can stat the path.
5
- *
6
- * This follows stat semantics: broken symlinks return false, while symlinks to
7
- * existing targets return true.
8
- */
3
+ /** True if stat succeeds; follows symlinks, so broken links return false. */
9
4
  export async function pathExists(filePath) {
10
5
  try {
11
6
  fs.statSync(pathForWindowsFilesystem(filePath));
@@ -15,10 +10,7 @@ export async function pathExists(filePath) {
15
10
  return false;
16
11
  }
17
12
  }
18
- /**
19
- * Synchronous counterpart to `pathExists()`, with the same `fs.statSync()`
20
- * semantics.
21
- */
13
+ /** Synchronous {@link pathExists}. */
22
14
  export function pathExistsSync(filePath) {
23
15
  try {
24
16
  fs.statSync(pathForWindowsFilesystem(filePath));
@@ -2,10 +2,8 @@ import { type RootBoundaryIdentity } from "./root-boundary.js";
2
2
  import { type MutationDirectoryObservation } from "./pinned-mutation-observation.js";
3
3
  import type { PinnedCreatedDirectoryReceipt, PinnedMutationAdmissionReceipt, PinnedMutationAuthorizationToken } from "./pinned-write-types.js";
4
4
  /**
5
- * Creates each missing path component from `rootReal` down to `targetPath`,
6
- * guarding every step. Returns the real (symlink-resolved) path of the final
7
- * component so callers can guard/use that path directly instead of
8
- * re-deriving it from the original, possibly-symlinked, lexical path.
5
+ * Guard each component from rootReal to targetPath; optionally create missing ones.
6
+ * Returns the resolved path: use it, rather than the lexical input, for later guards.
9
7
  */
10
8
  export declare function mkdirPathComponentsWithGuards(params: {
11
9
  rootReal: string;
@@ -15,6 +13,7 @@ export declare function mkdirPathComponentsWithGuards(params: {
15
13
  beforeUseComponent?: (componentPath: string, prospectiveTargetPath: string, retainedTargetPath: string | undefined) => Promise<void> | void;
16
14
  afterCreateComponent?: (receipt: PinnedCreatedDirectoryReceipt) => PinnedMutationAuthorizationToken | undefined;
17
15
  assertBeforeMutation?: () => void;
16
+ createMissing?: boolean;
18
17
  mode?: number;
19
18
  private?: boolean;
20
19
  rejectSymlinks?: boolean;
@@ -10,7 +10,7 @@ import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservi
10
10
  import { realpathSync } from "./realpath.js";
11
11
  import { admitPathInsideRoot } from "./root-boundary.js";
12
12
  import { checkedMutationDirectory } from "./pinned-mutation-observation.js";
13
- import { createPathSegmentRoute, joinPathSegmentRoute, } from "./path-segment-route.js";
13
+ import { createPathSegmentRoute, joinPathSegmentRoute, sameAbsolutePath, } from "./path-segment-route.js";
14
14
  function sameDirectoryFacts(left, right) {
15
15
  return left.dev === right.dev && left.ino === right.ino && left.mode === right.mode &&
16
16
  left.nlink === right.nlink;
@@ -55,9 +55,6 @@ function createdDirectoryEvidence(admission, parent, childPath) {
55
55
  return undefined;
56
56
  }
57
57
  }
58
- function sameAbsolutePath(left, right) {
59
- return path.relative(path.resolve(left), path.resolve(right)) === "";
60
- }
61
58
  function suppliedExactRootIdentity(identity) {
62
59
  return typeof identity?.dev === "bigint" && typeof identity.ino === "bigint"
63
60
  ? { dev: identity.dev, ino: identity.ino }
@@ -87,10 +84,8 @@ async function realpathOrThrowNotFile(target) {
87
84
  }
88
85
  }
89
86
  /**
90
- * Creates each missing path component from `rootReal` down to `targetPath`,
91
- * guarding every step. Returns the real (symlink-resolved) path of the final
92
- * component so callers can guard/use that path directly instead of
93
- * re-deriving it from the original, possibly-symlinked, lexical path.
87
+ * Guard each component from rootReal to targetPath; optionally create missing ones.
88
+ * Returns the resolved path: use it, rather than the lexical input, for later guards.
94
89
  */
95
90
  export async function mkdirPathComponentsWithGuards(params) {
96
91
  const rawRootReal = params.rootReal;
@@ -181,7 +176,7 @@ export async function mkdirPathComponentsWithGuards(params) {
181
176
  }
182
177
  }
183
178
  }
184
- if (shouldCreate) {
179
+ if (shouldCreate && params.createMissing !== false) {
185
180
  params.assertBeforeMutation?.();
186
181
  // Both policy and ordinary mkdir must renew the parent after callbacks.
187
182
  inspectGuardCurrent(parentGuard);
@@ -244,15 +239,8 @@ export async function mkdirPathComponentsWithGuards(params) {
244
239
  }
245
240
  const admittedNextPath = admittedNextReal.path;
246
241
  if (stat?.isSymbolicLink()) {
247
- // An existing path component may legitimately be a symlink to a real
248
- // directory inside the root (e.g. a skill-bank layout). We already
249
- // verified above that it resolves inside the root, so treat the
250
- // resolved real path as the directory for the rest of this walk
251
- // instead of rejecting it outright. Guard checks from here on operate
252
- // on the real (non-symlink) path, preserving TOCTOU protection for
253
- // every subsequent segment. Callers that need the final directory
254
- // (e.g. to guard it themselves after this function returns) must use
255
- // the returned resolved path, not their own lexical parent path.
242
+ // Containment was checked above. Continue through the symlink's real directory
243
+ // so subsequent guards bind that directory.
256
244
  const targetStat = fsSync.statSync(admittedNextPath);
257
245
  if (!targetStat.isDirectory()) {
258
246
  throw directoryComponentNotDirectoryError();
@@ -33,7 +33,7 @@ export const GUEST_FILESYSTEM_DISPATCH_PYTHON = [
33
33
  " sys.exit(SANDBOX_CREATE_EXISTS_EXIT_CODE)",
34
34
  " elif operation == 'read':",
35
35
  " if len(sys.argv) > 5:",
36
- " read_file_bounded(parent_fd, sys.argv[4], int(sys.argv[5]))",
36
+ " read_file(parent_fd, sys.argv[4], int(sys.argv[5]))",
37
37
  " else:",
38
38
  " read_file(parent_fd, sys.argv[4])",
39
39
  " elif operation == 'readdir':",
@@ -42,7 +42,6 @@ export const GUEST_FILESYSTEM_RENAME_NO_REPLACE_PYTHON = [
42
42
  " flags = 0x00000004 # RENAME_EXCL",
43
43
  " else:",
44
44
  " rename_fn = None",
45
- " flags = 0",
46
45
  " if rename_fn is not None:",
47
46
  " rename_fn.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint]",
48
47
  " rename_fn.restype = ctypes.c_int",
@@ -77,7 +76,6 @@ export const GUEST_FILESYSTEM_CREATE_EXCLUSIVE_PYTHON = [
77
76
  " staging_name = None",
78
77
  " temp_fd = None",
79
78
  " temp_name = 'payload'",
80
- " temp_inode = None",
81
79
  " try:",
82
80
  " try:",
83
81
  " os.lstat(basename, dir_fd=parent_fd)",
@@ -204,7 +204,9 @@ export const GUEST_FILESYSTEM_PYTHON = [
204
204
  "",
205
205
  GUEST_FILESYSTEM_CREATE_EXCLUSIVE_PYTHON,
206
206
  "",
207
- "def read_file_impl(parent_fd, basename, max_bytes):",
207
+ "def read_file(parent_fd, basename, max_bytes=None):",
208
+ " if max_bytes is not None and max_bytes < 0:",
209
+ " raise OSError(errno.EINVAL, 'read limit must be non-negative', basename)",
208
210
  " file_fd = os.open(basename, READ_FLAGS, dir_fd=parent_fd)",
209
211
  " try:",
210
212
  " file_stat = os.fstat(file_fd)",
@@ -227,14 +229,6 @@ export const GUEST_FILESYSTEM_PYTHON = [
227
229
  " finally:",
228
230
  " os.close(file_fd)",
229
231
  "",
230
- "def read_file(parent_fd, basename):",
231
- " read_file_impl(parent_fd, basename, None)",
232
- "",
233
- "def read_file_bounded(parent_fd, basename, max_bytes):",
234
- " if max_bytes < 0:",
235
- " raise OSError(errno.EINVAL, 'read limit must be non-negative', basename)",
236
- " read_file_impl(parent_fd, basename, max_bytes)",
237
- "",
238
232
  "def remove_tree(parent_fd, basename):",
239
233
  " entry_stat = os.lstat(basename, dir_fd=parent_fd)",
240
234
  " if not stat.S_ISDIR(entry_stat.st_mode) or stat.S_ISLNK(entry_stat.st_mode):",
@@ -2,7 +2,7 @@ export { FsSafeError, categorizeFsSafeError, } from "./errors.js";
2
2
  export type * from "./errors.js";
3
3
  export { DEFAULT_ROOT_MAX_BYTES, root, type Root, } from "./root.js";
4
4
  export type * from "./root-public-types.js";
5
- export { configureFsSafePython, configureFsSafeNative, getFsSafeNativeConfig, } from "./native-config.js";
5
+ export { configureFsSafeNative, getFsSafeNativeConfig, } from "./native-config.js";
6
6
  export type * from "./config.js";
7
7
  export { writeExternalFileWithinRoot, } from "./output.js";
8
8
  export type * from "./output.js";
@@ -1,5 +1,5 @@
1
1
  export { FsSafeError, categorizeFsSafeError, } from "./errors.js";
2
2
  export { DEFAULT_ROOT_MAX_BYTES, root, } from "./root.js";
3
- export { configureFsSafePython, configureFsSafeNative, getFsSafeNativeConfig, } from "./native-config.js";
3
+ export { configureFsSafeNative, getFsSafeNativeConfig, } from "./native-config.js";
4
4
  export { writeExternalFileWithinRoot, } from "./output.js";
5
5
  export { configureFsSafeLocks, getFsSafeLockConfig, } from "./lock-config.js";
@@ -45,11 +45,7 @@ class QueueDirectory {
45
45
  throw new Error(`durable queue path is not a directory: ${dir}`);
46
46
  }
47
47
  const segments = path.relative(base, target).split(path.sep).filter(Boolean);
48
- for (let index = 0; index < segments.length; index++) {
49
- const segment = segments[index];
50
- if (segment === undefined) {
51
- continue;
52
- }
48
+ for (const segment of segments) {
53
49
  current = path.join(current, segment);
54
50
  let stat;
55
51
  try {
@@ -7,8 +7,9 @@ import { stringifyJsonDocument } from "./json-stringify.js";
7
7
  import { readRegularFile, readRegularFileSync, statRegularFile } from "./regular-file.js";
8
8
  import { openRootFileSync } from "./root-file.js";
9
9
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
10
- import { writeTempFileSync } from "./replace-file-descriptor.js";
11
- import { SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
10
+ import { writeTempFile, syncDirectoryBestEffort } from "./replace-file-descriptor.js";
11
+ import { AtomicIo, runSync } from "./atomic-io.js";
12
+ import { AtomicTempOwner } from "./replace-file-temp-owner.js";
12
13
  import { admitStandalonePublicationPath, assertNoWindowsPathAlias } from "./windows-path-alias.js";
13
14
  import { writeTextAtomic } from "./text-atomic.js";
14
15
  import { sleep } from "./timing.js";
@@ -57,28 +58,8 @@ function trySetSecureMode(fd) {
57
58
  // Best-effort mode application stays on the retained staging descriptor.
58
59
  }
59
60
  }
60
- function trySyncDirectory(pathname) {
61
- let fd;
62
- try {
63
- fd = fsSync.openSync(path.dirname(pathname), "r");
64
- fsSync.fsyncSync(fd);
65
- }
66
- catch {
67
- // best-effort; some platforms/filesystems do not support syncing directories.
68
- }
69
- finally {
70
- if (fd !== undefined) {
71
- try {
72
- fsSync.closeSync(fd);
73
- }
74
- catch {
75
- // best-effort cleanup
76
- }
77
- }
78
- }
79
- }
80
61
  function renameJsonFileWithFallback(owner, pathname) {
81
- owner.assertCurrent(fsSync);
62
+ runSync(owner.assertCurrent());
82
63
  try {
83
64
  fsSync.renameSync(owner.pathname, pathname);
84
65
  return;
@@ -86,9 +67,9 @@ function renameJsonFileWithFallback(owner, pathname) {
86
67
  catch (error) {
87
68
  const code = error?.code;
88
69
  if (code === "EPERM" || code === "EEXIST") {
89
- owner.assertCurrent(fsSync);
70
+ runSync(owner.assertCurrent());
90
71
  fsSync.rmSync(pathname, { force: true });
91
- owner.assertCurrent(fsSync);
72
+ runSync(owner.assertCurrent());
92
73
  fsSync.renameSync(owner.pathname, pathname);
93
74
  return;
94
75
  }
@@ -113,31 +94,32 @@ export function writeJsonSync(pathname, data) {
113
94
  const tmpPath = path.format({ ...path.parse(filePath), base: `.fs-safe-${randomUUID()}.tmp` });
114
95
  const payload = `${stringifyJsonDocument(data, null, 2)}\n`;
115
96
  fsSync.mkdirSync(recursiveMkdirPath(path.dirname(filePath)), { recursive: true, mode: JSON_DIR_MODE });
116
- const owner = new SyncAtomicTempOwner(tmpPath);
97
+ const io = AtomicIo.sync(fsSync);
98
+ const owner = new AtomicTempOwner(tmpPath, io);
117
99
  let originalFailure;
118
100
  try {
119
101
  owner.start();
120
- const temp = writeTempFileSync({
121
- fsModule: fsSync, tempPath: tmpPath, content: payload, mode: JSON_FILE_MODE,
102
+ const temp = runSync(writeTempFile(io, {
103
+ tempPath: tmpPath, content: payload, mode: JSON_FILE_MODE,
122
104
  sync: false, onIdentity: owner.onIdentity,
123
- });
105
+ }));
124
106
  owner.adopt(temp);
125
- owner.assertCurrent(fsSync);
126
- trySetSecureMode(temp.fd);
127
- fsSync.fsyncSync(temp.fd);
107
+ runSync(owner.assertCurrent());
108
+ trySetSecureMode(temp.file.fd);
109
+ fsSync.fsyncSync(temp.file.fd);
128
110
  renameJsonFileWithFallback(owner, filePath);
129
111
  owner.markRenamed();
130
- owner.assertPublished(fsSync, filePath);
131
- trySetSecureMode(temp.fd);
132
- trySyncDirectory(filePath);
133
- owner.assertPublished(fsSync, filePath);
112
+ runSync(owner.assertPublished(filePath));
113
+ trySetSecureMode(temp.file.fd);
114
+ runSync(syncDirectoryBestEffort(io, path.dirname(filePath)));
115
+ runSync(owner.assertPublished(filePath));
134
116
  }
135
117
  catch (error) {
136
118
  originalFailure = { error };
137
119
  throw error;
138
120
  }
139
121
  finally {
140
- owner.finish({ fsModule: fsSync, originalFailure, throwOnCleanupError: false });
122
+ runSync(owner.finish({ originalFailure, throwOnCleanupError: false }));
141
123
  }
142
124
  }
143
125
  export class JsonFileReadError extends Error {
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import fsSync, { constants as fsConstants } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
+ import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
5
6
  import { assertSyncDirectoryGuard, createAsyncDirectoryGuard } from "./directory-guard.js";
6
7
  import { FsSafeError } from "./errors.js";
7
8
  import { guardedRename } from "./guarded-mutation.js";
@@ -142,6 +143,7 @@ async function copyRegularFilePinned(params) {
142
143
  }
143
144
  await assertSourceStillMatches(params.from, openedIdentity);
144
145
  const parentGuard = await createAsyncDirectoryGuard(path.dirname(params.to), { bigint: true });
146
+ assertExclusiveCreateLeaf(params.to);
145
147
  const destinationHandle = await fs.open(params.to, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, modeBits(params.mode) || 0o666);
146
148
  let destinationIdentity;
147
149
  try {
@@ -3,6 +3,7 @@ export declare class MutationAuthorityError extends FsSafeError {
3
3
  readonly rejection: unknown;
4
4
  constructor(rejection: unknown);
5
5
  }
6
+ export declare function isMutationAuthorityError(error: unknown): error is MutationAuthorityError;
6
7
  export declare function assertSynchronousCallbackResult(returned: unknown, name: string): void;
7
8
  export declare function composeMutationAssertions(defaultAssertion?: () => void, callAssertion?: () => void): (() => void) | undefined;
8
9
  export declare function rethrowMutationAuthorityError(error: unknown): never;
@@ -7,6 +7,14 @@ export class MutationAuthorityError extends FsSafeError {
7
7
  this.rejection = rejection;
8
8
  }
9
9
  }
10
+ export function isMutationAuthorityError(error) {
11
+ try {
12
+ return error instanceof MutationAuthorityError;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
10
18
  export function assertSynchronousCallbackResult(returned, name) {
11
19
  if (returned !== null &&
12
20
  (typeof returned === "object" || typeof returned === "function") &&
@@ -34,7 +42,7 @@ export function composeMutationAssertions(defaultAssertion, callAssertion) {
34
42
  };
35
43
  }
36
44
  export function rethrowMutationAuthorityError(error) {
37
- if (error instanceof MutationAuthorityError)
45
+ if (isMutationAuthorityError(error))
38
46
  throw error.rejection;
39
47
  // Cleanup failures and indeterminate publication receipts must retain their context.
40
48
  throw error;
@@ -1,4 +1,22 @@
1
1
  import type { RetainedFileResult } from "./retained-file-types.js";
2
+ export type NativePublicationTransition = {
3
+ outcome: "committed" | "not-published" | "indeterminate";
4
+ errorCode?: string;
5
+ errorMessage?: string;
6
+ };
7
+ export interface NativeWindowsEntryPublication {
8
+ readonly admission: {
9
+ outcome: string;
10
+ errorCode?: string;
11
+ errorMessage?: string;
12
+ };
13
+ current(published: boolean): void;
14
+ publish(): NativePublicationTransition;
15
+ close(): {
16
+ code: string;
17
+ message: string;
18
+ }[];
19
+ }
2
20
  export interface NativeRetainedFile {
3
21
  readonly admission: RetainedFileResult | {
4
22
  status: "retained";
@@ -63,6 +81,17 @@ export interface NativeOwnedTreeRemovalResult {
63
81
  errorCode?: string;
64
82
  errorMessage?: string;
65
83
  }
84
+ export interface NativeRootRemovalEntry {
85
+ dev: bigint;
86
+ ino: bigint;
87
+ directory: boolean;
88
+ symlink: boolean;
89
+ }
90
+ export interface NativeRootRemovalDirectory {
91
+ readonly fd: number;
92
+ read(): string | null;
93
+ close(): void;
94
+ }
66
95
  export interface NativeWindowsAccessControlEntry {
67
96
  sid: string;
68
97
  mask: number;
@@ -103,10 +132,19 @@ type NativeTwoPathArgs = [
103
132
  targetRelPath: string
104
133
  ];
105
134
  export interface NativeBinding {
135
+ rootRemovalStat?(parent: number, name: string): NativeRootRemovalEntry;
136
+ rootRemovalUnlink?(parent: number, name: string, dev: bigint, ino: bigint, directory: boolean): void;
137
+ openRootRemovalDirectory?(parent: number, name: string, dev: bigint, ino: bigint): NativeRootRemovalDirectory;
138
+ openCreateBeneath?(parentFd: number, basename: string, flags: number, mode: number): number;
139
+ renameReplaceWithIdentity?(sourceParent: number, source: string, targetParent: number, target: string, dev: bigint, ino: bigint): void;
106
140
  /** Windows-only, private handle custody; no borrowed/runtime descriptors. */
107
141
  retainWindowsFile?(directory: string, basename: string, parentDev: bigint, parentIno: bigint, dev: bigint, ino: bigint, size: bigint, mtimeNs: bigint, ctimeNs: bigint, sha256: string, maxBytes: number): NativeRetainedFile;
108
142
  watchRegister?(root: string, limit: number, callback: (batch: import("./watch-native.js").NativeWatchWireBatch) => void, persistent: boolean): number;
109
143
  watchConfigure?(id: number, anchors: string[], exclusions: string[]): void;
144
+ watchEntries?(id: number, entries: import("./watch-native.js").NativeWatchEntry[]): {
145
+ directories: number;
146
+ changed: boolean;
147
+ };
110
148
  watchAdd?(id: number, directory: {
111
149
  root: string;
112
150
  relative: string;
@@ -164,7 +202,7 @@ export interface NativeBinding {
164
202
  createStagedFile?(parentFd: number, basename: string): number;
165
203
  stagedFileMatches?(parentFd: number, basename: string, fileFd: number): boolean;
166
204
  removeStagedFile?(parentFd: number, basename: string, fileFd: number): "removed" | "name-absent" | "preserved";
167
- copyFileExclusive?(sourceFd: number, parentFd: number, basename: string, clone: CopyCloneMode, maxBytes: number | undefined, signal: AbortSignal | undefined, sync: boolean): Promise<NativeFileCopyResult>;
205
+ copyFileExclusive?(sourceFd: number, parentFd: number, basename: string, clone: CopyCloneMode, maxBytes: number | undefined, signal: AbortSignal | undefined): Promise<NativeFileCopyResult>;
168
206
  cloneFileExclusive(sourceFd: number, targetRootFd: number, targetRelPath: string): number;
169
207
  copyFileRangeExclusive(sourceFd: number, targetRootFd: number, targetRelPath: string): Promise<NativeCopyResult>;
170
208
  createPrivateDirectory(path: string): void;
@@ -180,15 +218,22 @@ export interface NativeBinding {
180
218
  linkBeneath(...args: NativeTwoPathArgs): void;
181
219
  /** Direct-child mkdir; true is receipt provenance only, never cleanup ownership. */
182
220
  mkdirChildBeneath?(parentFd: number, basename: string, mode: number): boolean;
221
+ mkdirOpenChildBeneath?(parentFd: number, basename: string, mode: number, flags: number): {
222
+ fd: number;
223
+ created: boolean;
224
+ };
183
225
  mkdirBeneath(rootFd: number, relPath: string, mode: number): void;
184
226
  openBeneath(rootFd: number, relPath: string, flags: number): NativeOpenBeneathResult;
185
- readArchiveEntryNative(path: string, kind: string, requested: string, maxBytes: number, limits: TarMeterLimits, signal: AbortSignal): Promise<Buffer>;
186
227
  readOwnerAndDacl(path: string): NativeWindowsSecurityFacts;
187
228
  /** Internal Windows-only inspection of the exact borrowed Node descriptor. */
188
229
  inspectWindowsSecureFileHandle?(fd: number): NativeWindowsDescriptorSecurityFacts;
189
230
  ownedTreeRemovalAvailable?(parentFd: number): boolean;
190
231
  removeOwnedTree?(parentFd: number, basename: string, directoryFd: number): Promise<NativeOwnedTreeRemovalResult>;
191
232
  removeOwnedTreeSync?(parentFd: number, basename: string, directoryFd: number): NativeOwnedTreeRemovalResult;
233
+ /** Local filesystem admission for one-way cooperative entry publication. */
234
+ retainWindowsEntryPublication?(sourcePath: string, name: string, sourceDev: bigint, sourceIno: bigint, targetPath: string, targetName: string, targetDev: bigint, targetIno: bigint, dev: bigint, ino: bigint, kind: "file" | "directory" | "symlink"): NativeWindowsEntryPublication;
235
+ entryPublicationFilesystem?(parentFd: number): string;
236
+ publishRetainedEntryNoReplace?(sourceParentFd: number, sourceBasename: string, sourceFd: number, targetParentFd: number, targetBasename: string): NativePublicationTransition;
192
237
  renameNoReplace(...args: NativeTwoPathArgs): void;
193
238
  /** Identity-fenced retained-directory rename capability. */
194
239
  renameNoReplaceWithIdentity?(...args: [...paths: NativeTwoPathArgs, expectedSourceDev: bigint, expectedSourceIno: bigint]): void;
@@ -2,15 +2,7 @@ export type FsSafeNativeMode = "auto" | "off" | "require";
2
2
  export type FsSafeNativeConfig = {
3
3
  mode: FsSafeNativeMode;
4
4
  };
5
- /** @deprecated Compatibility bridge for 0.4 upgrades. Use {@link FsSafeNativeConfig}. */
6
- export type FsSafePythonConfig = {
7
- mode: FsSafeNativeMode;
8
- pythonPath?: string;
9
- };
10
5
  export declare function configureFsSafeNative(config: Partial<FsSafeNativeConfig>): void;
11
- /**
12
- * @deprecated Compatibility bridge for 0.4 upgrades. Use configureFsSafeNative.
13
- */
14
- export declare function configureFsSafePython(config: Partial<FsSafePythonConfig>): void;
15
6
  export declare function getFsSafeNativeConfig(): FsSafeNativeConfig;
7
+ export declare function isFsSafeNativeRequired(): boolean;
16
8
  export declare function __resetFsSafeNativeConfigForTest(): void;