@openclaw/feishu 2026.9.8 → 2026.10.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -36,7 +36,7 @@ export type RetainedRootWriteSelection = RootWritePathSelection & Readonly<{
36
36
  }>;
37
37
  export declare function createRootWriteSelectionForFd(selection: RootWritePathSelection, fd: number): RetainedRootWriteSelection;
38
38
  export declare function assertRootWritePathSelectionSync(root: RootContext, selection: RootWritePathSelection): void;
39
- export declare function assertRootWriteSelectionSync(root: RootContext, selection: RetainedRootWriteSelection, verifyBinding: boolean, fd?: number): void;
39
+ export declare function assertRootWriteSelectionSync(root: RootContext, selection: RetainedRootWriteSelection, fd?: number): void;
40
40
  export declare function prepareRootWritePathSelection(params: Omit<RootWritePathSelection, "parentGuard"> & Readonly<{
41
41
  preparedParent?: PreparedRootWriteParent;
42
42
  }>): Promise<RootWritePathSelection>;
@@ -49,8 +49,8 @@ export declare function refreshRootWritePathSelection(selection: RootWritePathSe
49
49
  * Re-authorize a retained shared-JavaScript destination without recapturing
50
50
  * either its pathname binding or its exact parent fence.
51
51
  */
52
- export declare function refreshRootWriteSelection(root: RootContext, selection: RetainedRootWriteSelection, verifyBinding: boolean, fd?: number): Promise<void>;
53
- export declare function refreshRetainedRootWriteAdmission(root: RootContext, selection: RetainedRootWriteSelection, verifyBinding: boolean, fd?: number): Promise<void>;
52
+ export declare function refreshRootWriteSelection(root: RootContext, selection: RetainedRootWriteSelection, fd?: number): Promise<void>;
53
+ export declare function refreshRetainedRootWriteAdmission(root: RootContext, selection: RetainedRootWriteSelection, fd?: number): Promise<void>;
54
54
  type GuardedWritePathOptions = {
55
55
  relativePath: string;
56
56
  denyMutations?: DenyMutationPolicy;
@@ -92,9 +92,8 @@ export function assertRootWritePathSelectionSync(root, selection) {
92
92
  }
93
93
  assertSyncDirectoryGuard(selection.parentGuard);
94
94
  }
95
- export function assertRootWriteSelectionSync(root, selection, verifyBinding, fd) {
96
- if (verifyBinding)
97
- inspectSelectionBindingSync(root, selection, fd);
95
+ export function assertRootWriteSelectionSync(root, selection, fd) {
96
+ inspectSelectionBindingSync(root, selection, fd);
98
97
  assertSyncDirectoryGuard(selection.parentGuard);
99
98
  }
100
99
  async function authorizeRootWritePathSelection(selection, afterAwait) {
@@ -154,15 +153,14 @@ export async function refreshRootWritePathSelection(selection) {
154
153
  * Re-authorize a retained shared-JavaScript destination without recapturing
155
154
  * either its pathname binding or its exact parent fence.
156
155
  */
157
- export async function refreshRootWriteSelection(root, selection, verifyBinding, fd) {
156
+ export async function refreshRootWriteSelection(root, selection, fd) {
158
157
  await authorizeRootWritePathSelection(selection);
159
- if (verifyBinding)
160
- inspectSelectionBindingSync(root, selection, fd);
158
+ inspectSelectionBindingSync(root, selection, fd);
161
159
  await assertAsyncDirectoryGuard(selection.parentGuard);
162
160
  }
163
- export async function refreshRetainedRootWriteAdmission(root, selection, verifyBinding, fd) {
161
+ export async function refreshRetainedRootWriteAdmission(root, selection, fd) {
164
162
  await getFsSafeTestHooks()?.beforePinnedWriteParentAdmission?.(selection.operationTargetPath);
165
- await refreshRootWriteSelection(root, selection, verifyBinding, fd);
163
+ await refreshRootWriteSelection(root, selection, fd);
166
164
  }
167
165
  export async function resolveGuardedWritePathInRoot(root, params) {
168
166
  const resolvedPath = await resolvePathInRoot(root, params.relativePath, {
@@ -263,9 +261,9 @@ export async function resolvePinnedWriteTargetInRoot(root, relativePath, request
263
261
  if (!overwrite) {
264
262
  try {
265
263
  const existing = fsSync.statSync(resolved);
266
- if (!existing.isFile())
264
+ if (!existing.isFile() && !existing.isDirectory())
267
265
  throw new FsSafeError("not-file", "not a file");
268
- if (existing.nlink > 1)
266
+ if (existing.isFile() && existing.nlink > 1)
269
267
  throw hardlinkedPathNotAllowedError();
270
268
  throw new FsSafeError("already-exists", "file already exists");
271
269
  }
@@ -8,5 +8,4 @@ export declare function trimHyphenEdges(value: string): string;
8
8
  export declare function isSafePathSegment(segment: string, options?: SafePathSegmentOptions): boolean;
9
9
  export declare function assertSafePathSegment(segment: string, options?: SafePathSegmentOptions): string;
10
10
  export declare function normalizeSafePathSegment(value: string): string;
11
- export declare function sanitizeSafePathSegment(value: string): string | undefined;
12
11
  export declare function assertSafePathPrefix(prefix: string, options?: SafePathSegmentOptions): string;
@@ -58,10 +58,6 @@ export function normalizeSafePathSegment(value) {
58
58
  .replace(/[^A-Za-z0-9._-]+/g, "-");
59
59
  return trimHyphenEdges(sanitized);
60
60
  }
61
- export function sanitizeSafePathSegment(value) {
62
- const trimmed = normalizeSafePathSegment(value);
63
- return isSafePathSegment(trimmed, { allowDotPrefix: true }) ? trimmed : undefined;
64
- }
65
61
  export function assertSafePathPrefix(prefix, options = {}) {
66
62
  // Prefixes are often derived from safe filenames. Normalize harmless
67
63
  // filename characters first, but still reject real path-control bytes.
@@ -7,6 +7,7 @@ import { readFileDescriptorBoundedSync } from "./bounded-read.js";
7
7
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard, inspectDirectoryIdentity } from "./directory-guard.js";
8
8
  import { pinNodeDirectoryForMode, assertOwnedDirectory } from "./directory-mode-node.js";
9
9
  import { FsSafeError } from "./errors.js";
10
+ import { hasNodeErrorCode } from "./path.js";
10
11
  import { openPinnedFileSync } from "./pinned-open.js";
11
12
  import { runPinnedWriteHelper } from "./pinned-write.js";
12
13
  import { ensureTrailingSep } from "./root-context.js";
@@ -14,7 +15,7 @@ import { verifyAtomicWriteResult } from "./root-write-verification.js";
14
15
  import { prepareSecretRead, secretReadError, trimSecretFileContent, } from "./secret-read-policy.js";
15
16
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
16
17
  import { serializePathWrite } from "./write-queue.js";
17
- import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
18
+ import { assertNoWindowsPathAlias, isForeignWindowsShareOrDevicePath, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
18
19
  export const PRIVATE_SECRET_DIR_MODE = 0o700;
19
20
  export const PRIVATE_SECRET_FILE_MODE = 0o600;
20
21
  export function readSecretFileSync(filePath, label, options = {}) {
@@ -62,7 +63,8 @@ function isRelativeEscape(relativePath) {
62
63
  }
63
64
  function assertPathWithinRoot(rootDir, targetPath) {
64
65
  const relative = path.relative(rootDir, targetPath);
65
- if (!relative || isRelativeEscape(relative)) {
66
+ // path.relative folds Unicode case on Windows, so it alone could admit another host.
67
+ if (!relative || isRelativeEscape(relative) || isForeignWindowsShareOrDevicePath(targetPath, [rootDir])) {
66
68
  throw new Error(`Private secret path must stay under ${rootDir}.`);
67
69
  }
68
70
  }
@@ -172,9 +174,8 @@ async function ensurePrivateDirectory(rootDir, targetDir, mode) {
172
174
  break;
173
175
  }
174
176
  catch (error) {
175
- if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
177
+ if (!hasNodeErrorCode(error, "ENOENT"))
176
178
  throw error;
177
- }
178
179
  await assertAsyncDirectoryGuard(parentGuard);
179
180
  created = await createPrivateDirectory(current, mode);
180
181
  // EEXIST grants no initialization authority; both outcomes need fresh type checks.
@@ -212,8 +213,11 @@ function snapshotSecretFileWriteParams(params) {
212
213
  durable: params.durable,
213
214
  };
214
215
  }
215
- async function secretFileWriteQueueKey(filePath) {
216
+ async function secretFileWriteQueueKey(rootDir, filePath) {
217
+ assertNoWindowsPathAlias(rootDir, "filesystem", "private secret root uses a Windows filesystem namespace alias");
216
218
  assertNoWindowsPathAlias(filePath, "filesystem", "private secret path uses a Windows filesystem namespace alias");
219
+ // Canonicalizing an outside path would probe it first; a foreign share contacts its host.
220
+ assertPathWithinRoot(resolvePathPreservingWindowsRoot(rootDir), path.resolve(filePath));
217
221
  try {
218
222
  return await canonicalPathFromExistingAncestor(filePath);
219
223
  }
@@ -271,9 +275,8 @@ async function materializeSecretFileAtomic(params, createOnly) {
271
275
  }
272
276
  }
273
277
  catch (error) {
274
- if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
278
+ if (!hasNodeErrorCode(error, "ENOENT"))
275
279
  throw error;
276
- }
277
280
  }
278
281
  await assertAsyncDirectoryGuard(rootGuard);
279
282
  await assertAsyncDirectoryGuard(parentGuard);
@@ -310,7 +313,7 @@ async function materializeSecretFileAtomic(params, createOnly) {
310
313
  }
311
314
  export async function writeSecretFileAtomic(params) {
312
315
  const ownedParams = snapshotSecretFileWriteParams(params);
313
- const canonicalPath = await secretFileWriteQueueKey(ownedParams.filePath);
316
+ const canonicalPath = await secretFileWriteQueueKey(ownedParams.rootDir, ownedParams.filePath);
314
317
  await serializePathWrite(canonicalPath, async () => {
315
318
  await materializeSecretFileAtomic(ownedParams, false);
316
319
  });
@@ -318,7 +321,7 @@ export async function writeSecretFileAtomic(params) {
318
321
  export async function createSecretFileAtomic(params) {
319
322
  try {
320
323
  const ownedParams = snapshotSecretFileWriteParams(params);
321
- const canonicalPath = await secretFileWriteQueueKey(ownedParams.filePath);
324
+ const canonicalPath = await secretFileWriteQueueKey(ownedParams.rootDir, ownedParams.filePath);
322
325
  await serializePathWrite(canonicalPath, async () => {
323
326
  await materializeSecretFileAtomic(ownedParams, true);
324
327
  });
@@ -8,8 +8,6 @@ type SecureDirStat = {
8
8
  };
9
9
  export type ResolveSecureTempRootOptions = {
10
10
  accessSync?: (path: string, mode?: number) => void;
11
- /** @deprecated Unused. Repairs require descriptor-bound chmod. */
12
- chmodSync?: (path: string, mode: number) => void;
13
11
  descriptor?: SecureTempRootDescriptorAdapter;
14
12
  fallbackPrefix: string;
15
13
  getuid?: () => number | undefined;
@@ -2,6 +2,7 @@ import { syncFileBestEffort } from "./file-sync.js";
2
2
  import fsSync, {} 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 { assertDirectoryIdentitySync, createAsyncDirectoryGuard, } from "./directory-guard.js";
6
7
  import { syncDirectoryBestEffort } from "./directory-durability.js";
7
8
  import { isWindowsReservedDeviceName } from "./device-path.js";
@@ -184,6 +185,7 @@ export async function writeCallbackSibling(params) {
184
185
  }
185
186
  }
186
187
  else {
188
+ assertExclusiveCreateLeaf(tempPath);
187
189
  result = await Reflect.apply(write, params, [tempPath]);
188
190
  }
189
191
  assertParent();
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard } from "./directory-guard.js";
5
5
  import { fitFileNameToPortableComponent, sanitizeUntrustedFileName } from "./filename.js";
6
6
  import { applyDirectoryMode } from "./replace-file-descriptor.js";
7
+ import { AtomicIo, runAsync } from "./atomic-io.js";
7
8
  import { realpathSync } from "./realpath.js";
8
9
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
9
10
  import { root } from "./root.js";
@@ -12,7 +13,7 @@ import { resolveSecureTempRoot } from "./secure-temp-dir.js";
12
13
  import { resolveCallbackTempPath, writeCallbackSibling } from "./sibling-staged-file.js";
13
14
  import { tempFile } from "./temp-target.js";
14
15
  import { getFsSafeTestHooks } from "./test-hooks.js";
15
- import { assertNoWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
16
+ import { assertNoWindowsPathAlias, isForeignWindowsShareOrDevicePath, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
16
17
  function buildTempName(tempPrefix) {
17
18
  const safePrefix = assertSafePathPrefix(tempPrefix ?? ".fs-safe-stream", {
18
19
  label: "sibling temp prefix",
@@ -35,12 +36,11 @@ export async function writeSiblingTempFile(options) {
35
36
  const syncParentDir = options.syncParentDir === true;
36
37
  await fs.mkdir(recursiveMkdirPath(dir), { recursive: true, mode: dirMode });
37
38
  if (chmodDir) {
38
- await applyDirectoryMode({
39
- fsModule: fs,
39
+ await runAsync(applyDirectoryMode(AtomicIo.async(fs), {
40
40
  dirPath: dir,
41
41
  mode: dirMode,
42
42
  ignoreChmodError: true,
43
- });
43
+ }));
44
44
  }
45
45
  return await writeCallbackSibling({
46
46
  tempDir: dir,
@@ -83,6 +83,9 @@ export async function writeViaSiblingTempPath(params) {
83
83
  assertNoWindowsPathAlias(rootDir, "filesystem", "sibling temp root uses a Windows filesystem namespace alias");
84
84
  const requestedTargetPath = path.resolve(targetPathInput);
85
85
  assertNoWindowsPathAlias(requestedTargetPath, "filesystem", "sibling temp target uses a Windows filesystem namespace alias");
86
+ if (isForeignWindowsShareOrDevicePath(requestedTargetPath, [resolvePathPreservingWindowsRoot(rootDirInput), rootDir])) {
87
+ throw new Error("Target path is outside the allowed root");
88
+ }
86
89
  let targetPath;
87
90
  try {
88
91
  const realDir = realpathSync.native(path.dirname(requestedTargetPath));
@@ -5,6 +5,7 @@ import { fileObservation } from "./file-observation.js";
5
5
  import { readFileHandleBounded } from "./bounded-read.js";
6
6
  import { openSidecarRoot } from "./sidecar-lock-root.js";
7
7
  import { createNativeExclusiveFile } from "./native-operations.js";
8
+ import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
8
9
  import { sidecarLockRetryDelay, sidecarLockTimeout, isTransientLockFileDenial, maxTransientLockDenials, validateSidecarLockStaleMs, validateSidecarLockRetryOptions, validateSidecarLockCompromiseCheckIntervalMs, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
9
10
  import { relativeSidecarLockPath, removeSidecarLockIfUnchanged, serializeSidecarLockPayload, sidecarReclaimGuardExists, } from "./sidecar-lock-reclaim.js";
10
11
  import { awaitSidecarAdmissionBoundary, conditionalSidecarLockParser, observeHeldSidecarParser, observeSidecarLockParser, runSidecarAdmissionBoundary, } from "./sidecar-lock-admission-parser.js";
@@ -278,15 +279,21 @@ export async function acquireSidecarLock(options, context) {
278
279
  handle = opened.handle;
279
280
  }
280
281
  else {
281
- try {
282
- handle =
283
- (await createNativeExclusiveFile(lockPath, 0o600)) ??
284
- (await fs.open(lockPath, "wx", 0o600));
285
- }
286
- catch (createError) {
282
+ const failedOpen = (createError) => {
287
283
  lockFileCreateDenied = isTransientLockFileDenial(createError, lockPath);
288
284
  exclusiveCreateConflict = createError.code === "EEXIST";
289
285
  throw createError;
286
+ };
287
+ handle = await createNativeExclusiveFile(lockPath, 0o600).catch(failedOpen) ?? null;
288
+ if (!handle) {
289
+ try {
290
+ assertExclusiveCreateLeaf(lockPath);
291
+ }
292
+ catch (error) {
293
+ exclusiveCreateConflict = error.code === "EEXIST";
294
+ throw error;
295
+ }
296
+ handle = await fs.open(lockPath, "wx", 0o600).catch(failedOpen);
290
297
  }
291
298
  await handle.writeFile(raw, "utf8");
292
299
  }
@@ -23,9 +23,6 @@ export declare function serializeSidecarLockPayload(payload: Record<string, unkn
23
23
  };
24
24
  export declare function relativeSidecarLockPath(lockRoot: Root, lockPath: string): string;
25
25
  export declare function parseSidecarLockPayload(raw: string, parser?: (raw: string) => unknown): unknown;
26
- export declare function readSidecarLockSnapshot(lockPath: string, options?: Parameters<typeof readSidecarLockRawSnapshot>[1] & {
27
- parsePayload?: (raw: string) => unknown;
28
- }): Promise<SidecarLockSnapshot | null>;
29
26
  export declare function readSidecarLockRawSnapshot(lockPath: string, options?: {
30
27
  lockRoot?: Root;
31
28
  rejectNonFile?: boolean;
@@ -63,14 +63,6 @@ export function parseSidecarLockPayload(raw, parser) {
63
63
  return null;
64
64
  }
65
65
  }
66
- function missingSnapshotPath(error) {
67
- if (error.code === "ENOENT")
68
- return null;
69
- throw error;
70
- }
71
- export async function readSidecarLockSnapshot(lockPath, options = {}) {
72
- return parseSidecarLockSnapshot(await readSidecarLockRawSnapshot(lockPath, options), options.parsePayload);
73
- }
74
66
  async function readSidecarLockComparisonSnapshot(lockPath, options) {
75
67
  const snapshot = await readSidecarLockRawSnapshot(lockPath, options);
76
68
  const parsePayload = options?.parsePayload;
@@ -97,13 +89,7 @@ export async function readSidecarLockRawSnapshot(lockPath, options = {}) {
97
89
  await opened.handle.close().catch(() => undefined);
98
90
  }
99
91
  }
100
- let before;
101
- try {
102
- before = fsSync.lstatSync(lockPath, { bigint: true });
103
- }
104
- catch (error) {
105
- before = missingSnapshotPath(error);
106
- }
92
+ const before = lstatSidecarLockSync(lockPath);
107
93
  if (!before)
108
94
  return null;
109
95
  if (!before.isFile() || before.isSymbolicLink()) {
@@ -142,13 +128,7 @@ export async function readSidecarLockRawSnapshot(lockPath, options = {}) {
142
128
  if (!options.allowDescriptorIdentityDrift && !sameFileIdentity(before, opened))
143
129
  return null;
144
130
  const raw = (await readFileHandleBounded(handle, MAX_LOCK_PAYLOAD_BYTES)).toString("utf8");
145
- let after;
146
- try {
147
- after = fsSync.lstatSync(lockPath, { bigint: true });
148
- }
149
- catch (error) {
150
- after = missingSnapshotPath(error);
151
- }
131
+ const after = lstatSidecarLockSync(lockPath);
152
132
  if (!after || !after.isFile() || !sameFileIdentity(before, after))
153
133
  return null;
154
134
  return { raw, stat: after };
@@ -162,7 +142,9 @@ function lstatSidecarLockSync(lockPath) {
162
142
  return fsSync.lstatSync(lockPath, { bigint: true });
163
143
  }
164
144
  catch (error) {
165
- return missingSnapshotPath(error);
145
+ if (error.code === "ENOENT")
146
+ return null;
147
+ throw error;
166
148
  }
167
149
  }
168
150
  export function readSidecarLockSnapshotSync(lockPath, parsePayload, options = {}) {
@@ -1,8 +1,9 @@
1
1
  import type { BigIntStats } from "node:fs";
2
2
  import { FsSafeError } from "./errors.js";
3
+ import type { StagedSymlinkFailureDetails } from "./staged-symlink-types.js";
3
4
  import type { StagedFileCleanupReceipt, StagedFileFailureDetails, StagedFilePublication, StagedFileReceipt } from "./staged-file-types.js";
4
5
  export declare function createStagedFileReceipt(directory: StagedFileReceipt["directory"], temporaryBasename: string, stat: BigIntStats): StagedFileReceipt;
5
- export declare function stagedFileFailure(error: unknown, details: StagedFileFailureDetails): FsSafeError;
6
+ export declare function stagedFailure(kind: "file" | "symlink", error: unknown, details: StagedFileFailureDetails | StagedSymlinkFailureDetails): FsSafeError;
6
7
  /** Finish owned cleanup and every close before surfacing publication evidence. */
7
8
  export declare function settleStagedFile(params: {
8
9
  temporaryBasename: string;
@@ -10,11 +10,16 @@ export function createStagedFileReceipt(directory, temporaryBasename, stat) {
10
10
  }),
11
11
  });
12
12
  }
13
- export function stagedFileFailure(error, details) {
14
- const code = error instanceof FsSafeError
15
- ? error.code
16
- : error?.code === "EEXIST" ? "already-exists" : "helper-failed";
17
- return new FsSafeError(code, `staged file ${details.phase} failed`, { cause: error, details });
13
+ export function stagedFailure(kind, error, details) {
14
+ let code = "helper-failed";
15
+ try {
16
+ code = error instanceof FsSafeError ? error.code
17
+ : error?.code === "EEXIST" ? "already-exists" : "helper-failed";
18
+ }
19
+ catch {
20
+ // Uninspectable error metadata must not interrupt terminal settlement.
21
+ }
22
+ return new FsSafeError(code, `staged ${kind} ${details.phase} failed`, { cause: error, details });
18
23
  }
19
24
  /** Finish owned cleanup and every close before surfacing publication evidence. */
20
25
  export async function settleStagedFile(params) {
@@ -49,7 +54,7 @@ export async function settleStagedFile(params) {
49
54
  status,
50
55
  resources,
51
56
  });
52
- throw stagedFileFailure(error, {
57
+ throw stagedFailure("file", error, {
53
58
  phase: params.failure ? params.phase : "cleanup",
54
59
  publication: params.publication,
55
60
  cleanup,
@@ -53,14 +53,7 @@ export function registerTempPathForExit(tempPath, options) {
53
53
  singleLinkFile: options?.singleLinkFile,
54
54
  cleanupSync: options?.cleanupSync,
55
55
  };
56
- if (!entry.identity && !entry.cleanupSync) {
57
- try {
58
- entry.identity = fsSync.lstatSync(tempPath, { bigint: true });
59
- }
60
- catch {
61
- // Callers that register before creation set the identity after opening.
62
- }
63
- }
56
+ // Only the caller's receipt or cleanup callback grants removal authority.
64
57
  tempCleanupEntries.set(tempPath, entry);
65
58
  const unregister = (() => {
66
59
  if (tempCleanupEntries.get(tempPath) === entry)
@@ -20,4 +20,4 @@ export declare function validateInitialTempWorkspaceChild(stat: BigIntStats, own
20
20
  export declare function childHasRequestedMode(stat: BigIntStats | Stats, mode: number): boolean;
21
21
  export declare function admitTempWorkspaceChild(dir: string, expected: BigIntStats, parent: TempWorkspaceRootAdmission, mode: number): Promise<void> | undefined;
22
22
  export declare function admitTempWorkspaceChildSync(dir: string, expected: BigIntStats, parent: TempWorkspaceRootAdmission, mode: number): void;
23
- export declare function validateAdmittedTempWorkspaceChild(current: TempWorkspaceIdentityStat, ownerUid: number | undefined, mode: number): TempWorkspaceIdentityStat;
23
+ export declare function validateAdmittedTempWorkspaceChild(current: TempWorkspaceIdentityStat, ownerUid: number | undefined, mode: number): void;
@@ -105,10 +105,7 @@ async function initializeTempWorkspaceChildMode(dir, expected, parent, mode) {
105
105
  try {
106
106
  await owner.apply(mode, { check: parent.assertCurrent });
107
107
  const current = inspectDirectoryIdentitySync(dir, expected);
108
- assertTempWorkspaceChildState(current, parent.ownerUid);
109
- if (!childHasRequestedMode(current, mode)) {
110
- throw new FsSafeError("path-mismatch", "temp workspace final mode could not be verified");
111
- }
108
+ validateAdmittedTempWorkspaceChild(current, parent.ownerUid, mode);
112
109
  parent.assertCurrent();
113
110
  }
114
111
  finally {
@@ -131,10 +128,7 @@ export function admitTempWorkspaceChildSync(dir, expected, parent, mode) {
131
128
  try {
132
129
  owner.apply(mode, parent.assertCurrent);
133
130
  const current = inspectDirectoryIdentitySync(dir, expected);
134
- assertTempWorkspaceChildState(current, parent.ownerUid);
135
- if (!childHasRequestedMode(current, mode)) {
136
- throw new FsSafeError("path-mismatch", "temp workspace final mode could not be verified");
137
- }
131
+ validateAdmittedTempWorkspaceChild(current, parent.ownerUid, mode);
138
132
  parent.assertCurrent();
139
133
  }
140
134
  finally {
@@ -146,5 +140,4 @@ export function validateAdmittedTempWorkspaceChild(current, ownerUid, mode) {
146
140
  if (!childHasRequestedMode(current, mode)) {
147
141
  throw new FsSafeError("path-mismatch", "temp workspace final mode could not be verified");
148
142
  }
149
- return current;
150
143
  }
@@ -3,12 +3,7 @@ export type WriteTextAtomicOptions = Pick<ReplaceFileAtomicOptions, "beforeRenam
3
3
  mode?: number;
4
4
  dirMode?: number;
5
5
  trailingNewline?: boolean;
6
- /**
7
- * When false, skip the temp-file and parent-directory fsync calls while
8
- * preserving the temp-file replace/rename behavior.
9
- *
10
- * Defaults to true.
11
- */
6
+ /** Defaults to true; false skips file and parent fsync without changing replacement. */
12
7
  durable?: boolean;
13
8
  };
14
9
  export declare function writeTextAtomic(filePath: string, content: string, options?: WriteTextAtomicOptions): Promise<void>;
@@ -7,7 +7,7 @@ import { guardedRenameSync, guardedRmSync } from "./guarded-mutation.js";
7
7
  import { realpathSync } from "./realpath.js";
8
8
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
9
9
  import { getFsSafeTestHooks } from "./test-hooks.js";
10
- import { hasWindowsPathAlias, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
10
+ import { hasWindowsPathAlias, isForeignWindowsShareOrDevicePath, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
11
11
  const TRASH_DESTINATION_COLLISION_CODES = new Set(["EEXIST", "ENOTEMPTY", "ERR_FS_CP_EEXIST"]);
12
12
  const TRASH_DESTINATION_RETRY_LIMIT = 4;
13
13
  function getFsErrorCode(error) {
@@ -93,6 +93,10 @@ function assertAllowedTrashTarget(targetPath, allowedRoots) {
93
93
  assertNoTrashPathAlias(targetPath, "target path");
94
94
  const lexicalTarget = path.resolve(targetPath);
95
95
  assertNoTrashPathAlias(lexicalTarget, "target path");
96
+ const resolvedRoots = resolveAllowedTrashRoots(allowedRoots);
97
+ if (isForeignWindowsShareOrDevicePath(lexicalTarget, [...allowedRoots, ...resolvedRoots])) {
98
+ throw new Error(`Refusing to trash path outside allowed roots: ${targetPath}`);
99
+ }
96
100
  const stat = fs.lstatSync(lexicalTarget);
97
101
  const resolvedTarget = resolveTrashTargetPath(targetPath);
98
102
  const resolvedTargetPath = resolvedTarget.path;
@@ -100,7 +104,7 @@ function assertAllowedTrashTarget(targetPath, allowedRoots) {
100
104
  // Admit the directory entry only when its parent really stays inside an
101
105
  // allowed root. Do not admit it because the symlink target is inside.
102
106
  const resolvedParent = resolveTrashEntryParent(lexicalTarget, targetPath);
103
- const isAllowed = resolveAllowedTrashRoots(allowedRoots).some((root) => isSameOrChildPath(resolvedParent, root));
107
+ const isAllowed = resolvedRoots.some((root) => isSameOrChildPath(resolvedParent, root));
104
108
  if (!isAllowed) {
105
109
  throw new Error(`Refusing to trash path outside allowed roots: ${targetPath}`);
106
110
  }