@openclaw/matrix 2026.10.5-beta.3 → 2026.10.10-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.setup/{approval-handler.runtime-rxLn8CbP.mjs → approval-handler.runtime-CsqMdj7h.mjs} +9 -9
- package/dist/.setup/{approval-reactions-DhB6gNPU.mjs → approval-reactions-D2pEUrFS.mjs} +1 -1
- package/dist/.setup/{channel-BcwGspPw.mjs → channel-Dw4_imdd.mjs} +132 -111
- package/dist/.setup/{channel.runtime-CUc6rbu1.mjs → channel.runtime-2EEtIlyu.mjs} +6 -6
- package/dist/.setup/{channel.setup-CwuYD_CT.mjs → channel.setup-Be8TYKMA.mjs} +8 -3
- package/dist/.setup/{cli-mbL9jCro.mjs → cli-CEmWh7wh.mjs} +99 -37
- package/dist/.setup/{cli-metadata-6MOGuyHq.mjs → cli-metadata-CBLGwJT6.mjs} +1 -1
- package/dist/.setup/{client-BlTvpEWC.mjs → client-BKiiI29H.mjs} +3 -3
- package/dist/.setup/{client-vPPbaxNp.mjs → client-C4Gibhzl.mjs} +94 -120
- package/dist/.setup/{client-bootstrap-D05xy1HB.mjs → client-bootstrap-kOIDV4z_.mjs} +3 -3
- package/dist/.setup/{create-client-CroC4BVc.mjs → create-client-BS8UYx6N.mjs} +23 -15
- package/dist/.setup/{credentials-Bf9PQ7xs.mjs → credentials-CPnJZaZB.mjs} +3 -5
- package/dist/.setup/{credentials-read-CqRiEAQa.mjs → credentials-read-D2ULV55A.mjs} +12 -4
- package/dist/.setup/{crypto-runtime-CpJ0b7vY.mjs → crypto-runtime-D8yY7kjD.mjs} +168 -290
- package/dist/.setup/crypto-state-store-D4wrCvFs.mjs +185 -0
- package/dist/.setup/crypto-store-ownership-Z_Y5WRKr.mjs +174 -0
- package/dist/.setup/crypto-unsafe-state-doctor-C8szCYmf.mjs +54 -0
- package/dist/.setup/{directory-live-B1WbfSHo.mjs → directory-live-BpSz-FQB.mjs} +1 -1
- package/dist/.setup/{draft-stream-Bdrh7dRt.mjs → draft-stream-DYf0th1n.mjs} +1 -1
- package/dist/.setup/{exec-approvals-BgNWWJg1.mjs → exec-approvals-DaM-5OA5.mjs} +33 -43
- package/dist/.setup/{file-sync-store-BdKx_gMi.mjs → file-sync-store-CUH8EAJq.mjs} +3 -3
- package/dist/.setup/idb-persistence-BHTYPRe_.mjs +233 -0
- package/dist/.setup/idb-persistence-lock-Bh3ZLIsE.mjs +14 -0
- package/dist/.setup/{messages-B1CZqU8u.mjs → messages-_XHVg3do.mjs} +3 -18
- package/dist/.setup/{monitor-Dk_fQL1k.mjs → monitor-DRUSJ_Ev.mjs} +31 -39
- package/dist/.setup/{onboarding-CrSexor_.mjs → onboarding-CK9Ksgio.mjs} +3 -3
- package/dist/.setup/{outbound-LR3SqPIq.mjs → outbound-D5Z_4FBg.mjs} +28 -36
- package/dist/.setup/{profile-update-C7bjaGR6.mjs → profile-update--Pf3jCXD.mjs} +2 -2
- package/dist/.setup/{reaction-events-CTSvOlXO.mjs → reaction-events-BG9spedG.mjs} +6 -6
- package/dist/.setup/{resolve-targets-BVVbbvP6.mjs → resolve-targets-BJ5sIKeM.mjs} +1 -1
- package/dist/.setup/{retired-state-DW5pLH05.mjs → retired-state-DOCruzWd.mjs} +10 -10
- package/dist/.setup/{room-info-9Uqlpt1V.mjs → room-info-BE7bbX_c.mjs} +3 -3
- package/dist/.setup/runtime-setter-api-i2MOXoT3.mjs +2 -0
- package/dist/.setup/{sdk-Cfc6m7fY.mjs → sdk-ByU8So5S.mjs} +242 -139
- package/dist/.setup/secret-contract-DxyzKSzf.mjs +90 -0
- package/dist/.setup/secret-contract-api-Dukgvike.mjs +2 -0
- package/dist/.setup/{send-LCOuTVc1.mjs → send-BprM-Wmx.mjs} +19 -42
- package/dist/.setup/{setup-bootstrap-CV-hHBXu.mjs → setup-bootstrap-DmznRqYe.mjs} +2 -6
- package/dist/.setup/{setup-core-CKnNTfgD.mjs → setup-core-DcCk1_Tv.mjs} +4 -7
- package/dist/.setup/setup-plugin-api-BU6UGksW.mjs +2 -0
- package/dist/.setup/{startup-verification-CgfUb4yJ.mjs → startup-verification-UU8DUvi4.mjs} +5 -13
- package/dist/.setup/state-layout-walk-BQMgMykU.mjs +27 -0
- package/dist/.setup/{storage-DkC6bHgA.mjs → storage-l8V0XsGM.mjs} +7 -166
- package/dist/.setup/{subagent-hooks-D8jyeQsO.mjs → subagent-hooks-CSY4iwa_.mjs} +7 -6
- package/dist/.setup/{sync-cache-state-D4G-fi3t.mjs → sync-cache-state-Cnm4TCEI.mjs} +1 -1
- package/dist/.setup/thread-bindings-rrwQAS6R.mjs +620 -0
- package/dist/.setup/{thread-bindings-shared-CbsFNbAY.mjs → thread-bindings-shared-CPjUA0Ib.mjs} +26 -5
- package/dist/.setup/{tool-actions-DAp6_XSJ.mjs → tool-actions-DBrk6Yei.mjs} +9 -11
- package/dist/.setup/{verification-B3VswneT.mjs → verification--qUbmRd-.mjs} +2 -2
- package/dist/api.js +6 -6
- package/dist/auth-presence.js +16 -2
- package/dist/channel-plugin-api.js +1 -2
- package/dist/cli-metadata.js +1 -2
- package/dist/contract-api.js +2 -2
- package/dist/doctor-contract-api.js +20 -27
- package/dist/index.js +38 -12
- package/dist/plugin-entry.handlers.runtime.js +32 -5
- package/dist/runtime-api.js +3 -3
- package/dist/runtime-setter-api.js +1 -2
- package/dist/secret-contract-api.js +1 -2
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-entry.js +3 -3
- package/dist/setup-plugin-api.js +1 -2
- package/dist/subagent-hooks-api.js +1 -1
- package/dist/thread-binding-api.js +1 -2
- package/dist/thread-bindings-runtime.js +1 -2
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +148 -0
- package/node_modules/@openclaw/fs-safe/README.md +20 -4
- package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
- package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/bounded-read.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/bounded-read.js +9 -2
- package/node_modules/@openclaw/fs-safe/dist/clone-metadata.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +2 -1
- package/node_modules/@openclaw/fs-safe/dist/config.d.ts +6 -0
- package/node_modules/@openclaw/fs-safe/dist/config.js +5 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +3 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +8 -7
- package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-link.js +170 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-path-admission.js +11 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.d.ts +2 -4
- package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
- package/node_modules/@openclaw/fs-safe/dist/create.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/creation-boundary.js +7 -4
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-durability.d.ts +7 -4
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +20 -5
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
- package/node_modules/@openclaw/fs-safe/dist/directory-receipt.d.ts +7 -7
- package/node_modules/@openclaw/fs-safe/dist/directory-receipt.js +9 -4
- package/node_modules/@openclaw/fs-safe/dist/durability.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/exact-identity.d.ts +17 -0
- package/node_modules/@openclaw/fs-safe/dist/exact-identity.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/file-hash.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-hash.js +11 -9
- package/node_modules/@openclaw/fs-safe/dist/file-identity.d.ts +7 -1
- package/node_modules/@openclaw/fs-safe/dist/file-identity.js +27 -5
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-admission.js +1 -2
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +22 -15
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
- package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/index.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
- package/node_modules/@openclaw/fs-safe/dist/local-file-descriptor.js +24 -13
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +85 -18
- package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +5 -0
- package/node_modules/@openclaw/fs-safe/dist/native-config.js +11 -0
- package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +6 -4
- package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/native-fallback-warning.js +26 -2
- package/node_modules/@openclaw/fs-safe/dist/native-guidance.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-guidance.js +21 -0
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-operations.js +8 -6
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +44 -4
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +85 -15
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +3 -3
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +33 -6
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +22 -3
- package/node_modules/@openclaw/fs-safe/dist/native-policy-directory-observation.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file-metadata.js +16 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +15 -5
- package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-stat-identity.js +7 -0
- package/node_modules/@openclaw/fs-safe/dist/native-status.d.ts +17 -0
- package/node_modules/@openclaw/fs-safe/dist/native-status.js +30 -0
- package/node_modules/@openclaw/fs-safe/dist/native.d.ts +31 -4
- package/node_modules/@openclaw/fs-safe/dist/native.js +107 -9
- package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/owner-dacl-batch.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/owner-dacl.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/permission-exec.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +83 -47
- package/node_modules/@openclaw/fs-safe/dist/pinned-open.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-open.js +8 -3
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +13 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +10 -8
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +7 -5
- package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
- package/node_modules/@openclaw/fs-safe/dist/private-directory.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.d.ts +3 -2
- package/node_modules/@openclaw/fs-safe/dist/private-temp-workspace.js +38 -10
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
- package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/publish-file-failure.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +5 -3
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +26 -20
- package/node_modules/@openclaw/fs-safe/dist/read-opened-file.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/read-opened-file.js +3 -4
- package/node_modules/@openclaw/fs-safe/dist/realpath.js +32 -0
- package/node_modules/@openclaw/fs-safe/dist/regular-file.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/regular-file.js +11 -15
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +3 -2
- package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
- package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +8 -13
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +252 -27
- package/node_modules/@openclaw/fs-safe/dist/root-directory-creation.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +11 -8
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +35 -13
- package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file-open-batch-sync.js +108 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.d.ts +7 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.js +15 -0
- package/node_modules/@openclaw/fs-safe/dist/root-impl.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +154 -175
- package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-mkdir-native.js +239 -0
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +37 -15
- package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.d.ts +32 -0
- package/node_modules/@openclaw/fs-safe/dist/root-native-creation-path.js +56 -0
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-public-types.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +95 -16
- package/node_modules/@openclaw/fs-safe/dist/root-remove.js +6 -4
- package/node_modules/@openclaw/fs-safe/dist/root-walk.js +23 -19
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -6
- package/node_modules/@openclaw/fs-safe/dist/root-write-publication.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/secure-file-windows.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/secure-file.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/secure-file.js +10 -4
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/staged-directory.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/staged-directory.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +5 -3
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-descriptor.js +13 -2
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-owner.js +71 -3
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-ownership.js +22 -0
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-types.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/trash.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/types.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.d.ts +5 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-appcontainer.js +39 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-owner.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-owner.js +25 -36
- package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +24 -5
- package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/windows-security-facts.js +4 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.exe +0 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-security-helper.js +7 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
- package/node_modules/@openclaw/fs-safe/dist/write-file-handle.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/write-file-handle.js +12 -10
- package/node_modules/@openclaw/fs-safe/docs/advanced.md +2 -1
- package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/config.md +71 -1
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +45 -5
- package/node_modules/@openclaw/fs-safe/docs/copy.md +45 -5
- package/node_modules/@openclaw/fs-safe/docs/durability.md +34 -7
- package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
- package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
- package/node_modules/@openclaw/fs-safe/docs/install.md +47 -27
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +6 -6
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.6.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +79 -6
- package/node_modules/@openclaw/fs-safe/docs/native.md +279 -8
- package/node_modules/@openclaw/fs-safe/docs/permissions.md +64 -15
- package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
- package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
- package/node_modules/@openclaw/fs-safe/docs/public-api.md +10 -0
- package/node_modules/@openclaw/fs-safe/docs/queue-link-free-design.md +237 -0
- package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
- package/node_modules/@openclaw/fs-safe/docs/reading.md +56 -2
- package/node_modules/@openclaw/fs-safe/docs/regular-file.md +2 -0
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
- package/node_modules/@openclaw/fs-safe/docs/secure-file.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/security-model.md +35 -0
- package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
- package/node_modules/@openclaw/fs-safe/docs/staged-file.md +18 -0
- package/node_modules/@openclaw/fs-safe/docs/store.md +15 -0
- package/node_modules/@openclaw/fs-safe/docs/temp.md +39 -0
- package/node_modules/@openclaw/fs-safe/docs/testing.md +13 -0
- package/node_modules/@openclaw/fs-safe/docs/types.md +51 -81
- package/node_modules/@openclaw/fs-safe/docs/walk.md +4 -1
- package/node_modules/@openclaw/fs-safe/docs/watch.md +3 -2
- package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
- package/node_modules/@openclaw/fs-safe/package.json +21 -12
- package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-freebsd-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/node_modules/typebox/build/value/clean/from_object.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/openclaw.plugin.json +3 -0
- package/package.json +7 -6
- package/dist/.setup/runtime-setter-api-DtxDAM-x.mjs +0 -3
- package/dist/.setup/secret-contract-DSBRvhdZ.mjs +0 -95
- package/dist/.setup/secret-contract-api-DzEJ-X7n.mjs +0 -3
- package/dist/.setup/setup-plugin-api-BDAYk1a5.mjs +0 -3
- package/dist/.setup/thread-bindings-CT4P-OSZ.mjs +0 -365
- package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +0 -336
- package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.ps1 +0 -90
|
@@ -5,6 +5,7 @@ import { FsSafeError } from "./errors.js";
|
|
|
5
5
|
import { sameFileIdentity } from "./file-identity.js";
|
|
6
6
|
import { realpathSync } from "./realpath.js";
|
|
7
7
|
import { resolveRootPathSync, ROOT_PATH_ALIAS_POLICIES } from "./root-path.js";
|
|
8
|
+
import { hasWindowsPathAlias } from "./windows-path-alias.js";
|
|
8
9
|
function admitUncachedCopyPath(input, create = false, admittedRoot) {
|
|
9
10
|
if (!path.isAbsolute(input.rootPath) || !path.isAbsolute(input.absolutePath)) {
|
|
10
11
|
throw new FsSafeError("invalid-path", "copy roots and paths must be absolute");
|
|
@@ -50,15 +51,23 @@ export function createCopyPathAdmissionCache() {
|
|
|
50
51
|
const assertParent = ({ root, parent, chain }) => {
|
|
51
52
|
assertSyncDirectoryGuard(root);
|
|
52
53
|
for (const guard of chain) {
|
|
54
|
+
// The full parent guard immediately below includes this exact identity.
|
|
55
|
+
if (guard === parent)
|
|
56
|
+
continue;
|
|
53
57
|
inspectDirectoryIdentitySync(guard.dir, { dev: BigInt(guard.stat.dev), ino: BigInt(guard.stat.ino) });
|
|
54
58
|
}
|
|
55
|
-
|
|
59
|
+
if (parent !== root)
|
|
60
|
+
assertSyncDirectoryGuard(parent);
|
|
56
61
|
assertSyncDirectoryGuard(root);
|
|
57
62
|
};
|
|
58
63
|
const admit = (input, create = false) => {
|
|
59
64
|
// Keep the public resolver for Windows aliases and noncanonical spellings,
|
|
60
65
|
// including raw dot segments whose traversal must not be normalized away.
|
|
61
|
-
|
|
66
|
+
const ordinaryWindowsPath = process.platform !== "win32" || (/^[a-z]:\\/iu.test(input.rootPath) &&
|
|
67
|
+
!hasWindowsPathAlias(input.rootPath, "filesystem") &&
|
|
68
|
+
!hasWindowsPathAlias(input.absolutePath, "filesystem") &&
|
|
69
|
+
!input.absolutePath.slice(3).split("\\").some(segment => /[. ]$/u.test(segment) || segment.includes("~")));
|
|
70
|
+
if (!ordinaryWindowsPath || !path.isAbsolute(input.rootPath) ||
|
|
62
71
|
!path.isAbsolute(input.absolutePath) || input.rootPath.includes("\0") || input.absolutePath.includes("\0") ||
|
|
63
72
|
path.resolve(input.rootPath) !== input.rootPath || path.resolve(input.absolutePath) !== input.absolutePath ||
|
|
64
73
|
!input.absolutePath.startsWith(input.rootPath.endsWith(path.sep) ? input.rootPath : `${input.rootPath}${path.sep}`)) {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
import { type ExactIdentityReceipt, type ExactFileIdentity } from "./file-identity.js";
|
|
1
2
|
import { type CopyPath } from "./copy-path-admission.js";
|
|
2
3
|
import { type CopyCloneMode } from "./copy-policy.js";
|
|
3
4
|
import { type OwnedFileDescriptorSync } from "./create-owned-file.js";
|
|
4
5
|
export type CopyRootFileSyncOptions = {
|
|
5
6
|
source: CopyPath;
|
|
6
7
|
destination: CopyPath;
|
|
7
|
-
expectedSourceIdentity?:
|
|
8
|
-
dev: bigint;
|
|
9
|
-
ino: bigint;
|
|
10
|
-
};
|
|
8
|
+
expectedSourceIdentity?: ExactFileIdentity | ExactIdentityReceipt;
|
|
11
9
|
clone?: CopyCloneMode;
|
|
12
10
|
maxBytes?: number;
|
|
13
11
|
mode?: number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { identityFromReceipt } from "./file-identity.js";
|
|
1
2
|
import fs, {} from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { normalizeMaxBytes } from "./byte-budget.js";
|
|
@@ -50,7 +51,8 @@ function copyRootFileWithAdmissionSync(options, cache) {
|
|
|
50
51
|
const targetInput = options.destination;
|
|
51
52
|
const targetPath = { rootPath: targetInput.rootPath, absolutePath: targetInput.absolutePath };
|
|
52
53
|
const expectedInput = options.expectedSourceIdentity;
|
|
53
|
-
const
|
|
54
|
+
const expected = expectedInput === undefined ? undefined : identityFromReceipt(expectedInput);
|
|
55
|
+
const expectedSourceIdentity = expected && { dev: expected.dev, ino: expected.ino };
|
|
54
56
|
const clone = resolveCopyCloneMode(options.clone, "never");
|
|
55
57
|
const maxBytes = normalizeMaxBytes(options.maxBytes);
|
|
56
58
|
const mode = options.mode;
|
|
@@ -68,7 +70,7 @@ function copyRootFileWithAdmissionSync(options, cache) {
|
|
|
68
70
|
let parentOpen = false;
|
|
69
71
|
let target;
|
|
70
72
|
let identity;
|
|
71
|
-
const native = getNativeBinding();
|
|
73
|
+
const native = process.platform !== "win32" ? getNativeBinding("copyFileExclusiveSync", "removeStagedFile") : getNativeBinding();
|
|
72
74
|
const nativeCopy = process.platform !== "win32" && native?.copyFileExclusiveSync && native.removeStagedFile
|
|
73
75
|
? native.copyFileExclusiveSync.bind(native) : undefined;
|
|
74
76
|
try {
|
|
@@ -4,12 +4,9 @@ import fsp, {} from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { assertExclusiveCreateLeaf } from "./exclusive-create.js";
|
|
7
|
+
import { timestampSeconds } from "./copy-metadata.js";
|
|
8
|
+
import { readCopyLinkTarget } from "./copy-link.js";
|
|
7
9
|
import { assertStagedDirectoryCurrent, exactIdentityMatches, openStagedDirectory, } from "./staged-directory.js";
|
|
8
|
-
function timestampSeconds(nanoseconds) {
|
|
9
|
-
// Dates discard sub-millisecond precision; negative numbers mean "now" in
|
|
10
|
-
// Node's utimes API. Numeric strings retain fractional, pre-epoch timestamps.
|
|
11
|
-
return String(Number(nanoseconds / 1000000000n) + Number(nanoseconds % 1000000000n) / 1e9);
|
|
12
|
-
}
|
|
13
10
|
/** Byte-copy adapter for the shared immutable-source, caller-owned namespace contract. */
|
|
14
11
|
export async function copyOwnedTree(source, destination, options) {
|
|
15
12
|
const callerSignal = options.signal ? AbortSignal.any([options.signal]) : undefined;
|
|
@@ -145,7 +142,7 @@ export async function copyOwnedTree(source, destination, options) {
|
|
|
145
142
|
else if (child.isSymbolicLink()) {
|
|
146
143
|
const link = process.platform === "win32"
|
|
147
144
|
? await fsp.readlink(childSource)
|
|
148
|
-
: await
|
|
145
|
+
: await readCopyLinkTarget(childSource);
|
|
149
146
|
let type;
|
|
150
147
|
if (process.platform === "win32") {
|
|
151
148
|
// Older Node releases infer link type from the destination, where a
|
|
@@ -21,7 +21,7 @@ function closeDescriptor(fd, failure) {
|
|
|
21
21
|
}
|
|
22
22
|
/** Inspect an existing real directory without creating probe files. */
|
|
23
23
|
export function probeTreeClone(parentPath) {
|
|
24
|
-
const native = getNativeBinding();
|
|
24
|
+
const native = getNativeBinding("probeTreeClone");
|
|
25
25
|
if (!native)
|
|
26
26
|
return undefined;
|
|
27
27
|
const parent = openStagedDirectory(assertAbsolutePathInput(parentPath));
|
|
@@ -64,9 +64,9 @@ async function materializeTree(source, destination, options, policy) {
|
|
|
64
64
|
throw new FsSafeError("invalid-path", "clone concurrency must be an integer between 1 and 32");
|
|
65
65
|
}
|
|
66
66
|
const native = policy === "always"
|
|
67
|
-
? requireNativeBinding()
|
|
67
|
+
? requireNativeBinding("probeTreeClone", "cloneTree")
|
|
68
68
|
: policy === "auto"
|
|
69
|
-
? getNativeBinding()
|
|
69
|
+
? getNativeBinding("probeTreeClone", "cloneTree")
|
|
70
70
|
: undefined;
|
|
71
71
|
const target = assertAbsolutePathInput(destination);
|
|
72
72
|
const name = path.basename(target);
|
|
@@ -204,7 +204,7 @@ function privateDirectoryBackend() {
|
|
|
204
204
|
if (getFsSafeNativeConfig().mode === "require") {
|
|
205
205
|
throw new FsSafeError("helper-unavailable", "private directory creation requires an up-to-date native parent identity capability");
|
|
206
206
|
}
|
|
207
|
-
warnNativeFallback("windows-private-directory", "Private Windows directory creation
|
|
207
|
+
warnNativeFallback("windows-private-directory", "Private Windows directory creation launches the packaged .NET helper for each check or creation.", "createPrivateDirectoryWithParentIdentity");
|
|
208
208
|
return undefined;
|
|
209
209
|
}
|
|
210
210
|
export async function createDirectory(targetPath, options = {}) {
|
|
@@ -53,17 +53,17 @@ export function assertPrivateFileCreationAvailable() {
|
|
|
53
53
|
typeof native.verifyPrivateWindowsFile !== "function")
|
|
54
54
|
unavailable();
|
|
55
55
|
}
|
|
56
|
-
function commandFallback() {
|
|
56
|
+
function commandFallback(capability) {
|
|
57
57
|
if (getFsSafeNativeConfig().mode === "require")
|
|
58
58
|
unavailable();
|
|
59
|
-
warnNativeFallback("windows-private-creation", "Private Windows creation
|
|
59
|
+
warnNativeFallback("windows-private-creation", "Private Windows creation launches the packaged .NET helper for each check or creation.", capability);
|
|
60
60
|
}
|
|
61
61
|
function creationOperation(name) {
|
|
62
62
|
const native = getNativeBinding();
|
|
63
63
|
const operation = native?.[name];
|
|
64
64
|
if (typeof operation === "function")
|
|
65
65
|
return operation.bind(native);
|
|
66
|
-
commandFallback();
|
|
66
|
+
commandFallback(name);
|
|
67
67
|
return undefined;
|
|
68
68
|
}
|
|
69
69
|
/** Full Windows identity, with security validation when requested; no repairs. */
|
|
@@ -300,7 +300,10 @@ export function assertBeforeCreation(selected, permissions, assertion, kind) {
|
|
|
300
300
|
if (permissions.private)
|
|
301
301
|
assertDarwinCreationDirectoryAcl(selected.parent.dir, selected.parent.stat, kind);
|
|
302
302
|
assertSynchronousCallbackResult(assertion?.(), "assertBeforeMutation");
|
|
303
|
-
|
|
303
|
+
// Ordinary callers enter immediately after prepareCreationPath's identical fence.
|
|
304
|
+
// A callback or private ACL inspection needs a fresh observation afterward.
|
|
305
|
+
if (assertion !== undefined || permissions.private)
|
|
306
|
+
selected.assertParent();
|
|
304
307
|
if (permissions.private && kind === "directory")
|
|
305
308
|
assertDarwinPrivateDirectoryMode(permissions.mode);
|
|
306
309
|
if (permissions.private)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DarwinAclInspection = {
|
|
2
|
+
kind: "none";
|
|
3
|
+
} | {
|
|
4
|
+
kind: "present";
|
|
5
|
+
inheritsToFiles: boolean;
|
|
6
|
+
inheritsToDirectories: boolean;
|
|
7
|
+
} | {
|
|
8
|
+
kind: "unknown";
|
|
9
|
+
reason: string;
|
|
10
|
+
};
|
|
11
|
+
/** Inspect one no-follow descriptor. Unknown is never evidence of no ACL. */
|
|
12
|
+
export declare function inspectDarwinAcl(path: string): DarwinAclInspection;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { getNativeBinding } from "./native.js";
|
|
3
|
+
/** Inspect one no-follow descriptor. Unknown is never evidence of no ACL. */
|
|
4
|
+
export function inspectDarwinAcl(path) {
|
|
5
|
+
if (process.platform !== "darwin")
|
|
6
|
+
return { kind: "unknown", reason: "unsupported-platform" };
|
|
7
|
+
let fd;
|
|
8
|
+
try {
|
|
9
|
+
const native = getNativeBinding();
|
|
10
|
+
if (!native?.inspectDarwinAcl)
|
|
11
|
+
return { kind: "unknown", reason: "helper-unavailable" };
|
|
12
|
+
fd = fs.openSync(path, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
13
|
+
const facts = native.inspectDarwinAcl(fd);
|
|
14
|
+
if (facts.state === "absent" || facts.state === "empty")
|
|
15
|
+
return { kind: "none" };
|
|
16
|
+
if (facts.state === "present" && typeof facts.inheritsToFiles === "boolean" &&
|
|
17
|
+
typeof facts.inheritsToDirectories === "boolean") {
|
|
18
|
+
return { kind: "present", inheritsToFiles: facts.inheritsToFiles, inheritsToDirectories: facts.inheritsToDirectories };
|
|
19
|
+
}
|
|
20
|
+
return { kind: "unknown", reason: "incomplete-acl-facts" };
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return { kind: "unknown", reason: error instanceof Error ? error.message : String(error) };
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
if (fd !== undefined)
|
|
27
|
+
fs.closeSync(fd);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { ExactFileIdentity, ExactIdentityReceipt } from "./exact-identity.js";
|
|
1
2
|
import { type BigIntStats, type Stats } from "node:fs";
|
|
2
|
-
import type {
|
|
3
|
+
import type { FileIdentityInput } from "./file-identity.js";
|
|
3
4
|
export type DirectorySyncOutcome = {
|
|
4
5
|
status: "synced";
|
|
5
6
|
} | {
|
|
@@ -10,14 +11,16 @@ export type DirectoryReceipt<T extends Stats | BigIntStats = Stats> = {
|
|
|
10
11
|
path: string;
|
|
11
12
|
realPath: string;
|
|
12
13
|
identity: T;
|
|
14
|
+
exactIdentity?: ExactFileIdentity;
|
|
13
15
|
};
|
|
16
|
+
export type ExactDirectoryReceipt = DirectoryReceipt & ExactIdentityReceipt;
|
|
14
17
|
export type DurableDirectoryReceipt = DirectoryReceipt & {
|
|
15
18
|
parentSync: DirectorySyncOutcome | {
|
|
16
19
|
status: "not-needed";
|
|
17
20
|
};
|
|
18
21
|
};
|
|
19
22
|
export type PinnedDirectory = {
|
|
20
|
-
readonly receipt:
|
|
23
|
+
readonly receipt: ExactDirectoryReceipt;
|
|
21
24
|
assertCurrent(): Promise<void>;
|
|
22
25
|
sync(): Promise<DirectorySyncOutcome>;
|
|
23
26
|
close(): Promise<void>;
|
|
@@ -26,7 +29,7 @@ export type EnsureDurableDirectoryOptions = {
|
|
|
26
29
|
directoryPath: string;
|
|
27
30
|
label?: string;
|
|
28
31
|
mode?: number;
|
|
29
|
-
expectedExistingIdentity?:
|
|
32
|
+
expectedExistingIdentity?: FileIdentityInput;
|
|
30
33
|
create?: (directoryPath: string) => Promise<void>;
|
|
31
34
|
};
|
|
32
35
|
export declare function pinDirectory(directory: string | DirectoryReceipt<Stats | BigIntStats>, options?: {
|
|
@@ -40,4 +43,4 @@ export declare function syncDirectorySync(directory: string | DirectoryReceipt<S
|
|
|
40
43
|
}): DirectorySyncOutcome;
|
|
41
44
|
export declare function syncDirectoryBestEffort(directoryPath: string): Promise<void>;
|
|
42
45
|
export declare function syncDirectoryBestEffortSync(directoryPath: string): void;
|
|
43
|
-
export declare function ensureDurableDirectory(options: EnsureDurableDirectoryOptions): Promise<DurableDirectoryReceipt>;
|
|
46
|
+
export declare function ensureDurableDirectory(options: EnsureDurableDirectoryOptions): Promise<DurableDirectoryReceipt & ExactIdentityReceipt>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
-
import { sameFileIdentity } from "./file-identity.js";
|
|
4
|
+
import { hasSafeFileIdentity, sameFileIdentity } from "./file-identity.js";
|
|
5
5
|
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { isNotFoundPathError } from "./path.js";
|
|
7
7
|
import { directoryComponentNotDirectoryError } from "./root-errors.js";
|
|
@@ -9,6 +9,9 @@ import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservi
|
|
|
9
9
|
import { realpathSync } from "./realpath.js";
|
|
10
10
|
import { directoryEntryPath } from "./directory-entry-path.js";
|
|
11
11
|
import { assertStatObservationSync, inspectStatObservationSync, } from "./stat-observation.js";
|
|
12
|
+
// Numeric metadata is retained for existing internal consumers; rounded IDs
|
|
13
|
+
// must never become authority for a later comparison.
|
|
14
|
+
const exactGuardIdentities = new WeakMap();
|
|
12
15
|
export async function readDirectoryIdentity(dir) {
|
|
13
16
|
const guard = await createAsyncDirectoryGuard(dir, { bigint: true });
|
|
14
17
|
return Object.freeze({ dev: guard.stat.dev, ino: guard.stat.ino, realPath: guard.realPath });
|
|
@@ -43,9 +46,19 @@ export function captureDirectoryGuard(dir, mode, options) {
|
|
|
43
46
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
44
47
|
throw directoryComponentNotDirectoryError();
|
|
45
48
|
}
|
|
49
|
+
let exactIdentity;
|
|
50
|
+
if (!hasSafeFileIdentity(stat)) {
|
|
51
|
+
const exact = fsSync.lstatSync(operationPath, { bigint: true });
|
|
52
|
+
if (exact.isSymbolicLink() || !exact.isDirectory())
|
|
53
|
+
throw directoryComponentNotDirectoryError();
|
|
54
|
+
exactIdentity = { dev: exact.dev, ino: exact.ino };
|
|
55
|
+
}
|
|
46
56
|
const realPath = mode === "native" ? realpathSync.native(operationPath) : realpathSync(operationPath);
|
|
47
57
|
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
48
|
-
|
|
58
|
+
const guard = { dir, realPath, stat };
|
|
59
|
+
if (exactIdentity)
|
|
60
|
+
exactGuardIdentities.set(guard, exactIdentity);
|
|
61
|
+
return guard;
|
|
49
62
|
}
|
|
50
63
|
export async function assertAsyncDirectoryGuard(guard) {
|
|
51
64
|
assertDirectoryGuard(guard, "native");
|
|
@@ -59,9 +72,11 @@ export function assertDirectoryGuard(guard, mode) {
|
|
|
59
72
|
const expectedRealPath = guard.realPath;
|
|
60
73
|
assertNoWindowsPathAlias(expectedRealPath, "filesystem");
|
|
61
74
|
const expectedStat = guard.stat;
|
|
62
|
-
const
|
|
75
|
+
const suppliedIdentity = { dev: expectedStat.dev, ino: expectedStat.ino };
|
|
76
|
+
const retainedIdentity = exactGuardIdentities.get(guard);
|
|
77
|
+
const expectedIdentity = retainedIdentity ?? suppliedIdentity;
|
|
63
78
|
const stat = typeof expectedIdentity.dev === "bigint" && typeof expectedIdentity.ino === "bigint"
|
|
64
|
-
? inspectDirectoryIdentityAtPathSync(operationPath, {
|
|
79
|
+
? retainedIdentity ? fsSync.lstatSync(operationPath, { bigint: true }) : inspectDirectoryIdentityAtPathSync(operationPath, {
|
|
65
80
|
dev: expectedIdentity.dev,
|
|
66
81
|
ino: expectedIdentity.ino,
|
|
67
82
|
})
|
|
@@ -74,7 +89,7 @@ export function assertDirectoryGuard(guard, mode) {
|
|
|
74
89
|
if (mode === "native" && !sameFileIdentity(stat, expectedIdentity)) {
|
|
75
90
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
76
91
|
}
|
|
77
|
-
const realPath = mode === "native" || typeof
|
|
92
|
+
const realPath = mode === "native" || typeof suppliedIdentity.ino === "bigint"
|
|
78
93
|
? realpathSync.native(operationPath) : realpathSync(operationPath);
|
|
79
94
|
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
80
95
|
if ((mode === "normalized" && !sameFileIdentity(stat, expectedIdentity)) || realPath !== expectedRealPath) {
|
|
@@ -5,6 +5,7 @@ export type DirectoryModeChecks = {
|
|
|
5
5
|
};
|
|
6
6
|
export type DirectoryModeOwner = {
|
|
7
7
|
verify(check?: () => void): Promise<void>;
|
|
8
|
+
verifyCanonical?(): Promise<boolean>;
|
|
8
9
|
apply(mode: number, checks?: DirectoryModeChecks): Promise<void>;
|
|
9
10
|
close(): Promise<void>;
|
|
10
11
|
};
|
|
@@ -12,6 +13,7 @@ export declare function assertOwnedDirectory(expected: Stats | BigIntStats, actu
|
|
|
12
13
|
/** Serializes use and close: even a queued path-based fd operation retains its descriptor. */
|
|
13
14
|
export declare function ownDirectoryMode(params: {
|
|
14
15
|
inspect: () => Promise<number>;
|
|
16
|
+
inspectCanonical?: () => number | undefined;
|
|
15
17
|
chmod: (mode: number) => Promise<void>;
|
|
16
18
|
prepareChmod?: () => Promise<void>;
|
|
17
19
|
verifyChmod?: () => Promise<void>;
|
|
@@ -28,6 +30,7 @@ export declare function nodeDirectorySearchOnlyFlags(): {
|
|
|
28
30
|
export declare function pinNodeDirectoryForMode(dirPath: string, options?: {
|
|
29
31
|
expectedIdentity?: BigIntStats;
|
|
30
32
|
ownerUid?: number;
|
|
33
|
+
canonicalPath?: string;
|
|
31
34
|
}): Promise<DirectoryModeOwner>;
|
|
32
35
|
/** Synchronous counterpart with the same no-follow, identity, and owner checks. */
|
|
33
36
|
export declare function pinNodeDirectoryForModeSync(dirPath: string, options?: {
|
|
@@ -4,6 +4,8 @@ import { FsSafeError } from "./errors.js";
|
|
|
4
4
|
import { inspectDirectoryIdentity, inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
5
5
|
import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
6
6
|
import { sameFileIdentityForCleanup } from "./file-identity.js";
|
|
7
|
+
import { getNativeBinding } from "./native.js";
|
|
8
|
+
import { nativePolicyDirectoryObserver } from "./native-policy-directory-observation.js";
|
|
7
9
|
export function assertOwnedDirectory(expected, actual) {
|
|
8
10
|
if (actual.isSymbolicLink() || !actual.isDirectory()) {
|
|
9
11
|
throw new FsSafeError("not-file", "directory mode target must be a real directory");
|
|
@@ -23,7 +25,7 @@ export function ownDirectoryMode(params) {
|
|
|
23
25
|
pending = operation.catch(() => undefined);
|
|
24
26
|
return operation;
|
|
25
27
|
};
|
|
26
|
-
|
|
28
|
+
const owner = {
|
|
27
29
|
verify: (check) => enqueue(async () => {
|
|
28
30
|
check?.();
|
|
29
31
|
await params.inspect();
|
|
@@ -79,6 +81,14 @@ export function ownDirectoryMode(params) {
|
|
|
79
81
|
return closing;
|
|
80
82
|
},
|
|
81
83
|
};
|
|
84
|
+
if (params.inspectCanonical) {
|
|
85
|
+
owner.verifyCanonical = async () => {
|
|
86
|
+
let verified = false;
|
|
87
|
+
await enqueue(async () => { verified = params.inspectCanonical() !== undefined; });
|
|
88
|
+
return verified;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return owner;
|
|
82
92
|
}
|
|
83
93
|
/** Darwin descriptor inspection avoids requesting directory-content reads. */
|
|
84
94
|
export function nodeDarwinDirectoryMetadataFlags() {
|
|
@@ -131,7 +141,11 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
131
141
|
proc = route.proc;
|
|
132
142
|
return await fs.open(dirPath, route.flags | flags);
|
|
133
143
|
});
|
|
144
|
+
let observer;
|
|
134
145
|
try {
|
|
146
|
+
observer = options.canonicalPath === dirPath && ownerUid === undefined
|
|
147
|
+
? nativePolicyDirectoryObserver(getNativeBinding(), handle.fd, dirPath)
|
|
148
|
+
: undefined;
|
|
135
149
|
const inspect = async () => {
|
|
136
150
|
const opened = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), expected);
|
|
137
151
|
assertOwnedDirectory(expected, opened);
|
|
@@ -153,8 +167,18 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
153
167
|
assertOwner(opened);
|
|
154
168
|
assertOwner(followed);
|
|
155
169
|
};
|
|
170
|
+
const observeCanonical = observer;
|
|
156
171
|
const owner = ownDirectoryMode({
|
|
157
172
|
inspect,
|
|
173
|
+
inspectCanonical: observeCanonical ? () => {
|
|
174
|
+
const observed = observeCanonical();
|
|
175
|
+
if (!observed)
|
|
176
|
+
return undefined;
|
|
177
|
+
if (observed.identity.dev !== expected.dev || observed.identity.ino !== expected.ino) {
|
|
178
|
+
throw new FsSafeError("path-mismatch", "directory changed before its mode could be applied");
|
|
179
|
+
}
|
|
180
|
+
return Number(observed.identity.mode & 4095n);
|
|
181
|
+
} : undefined,
|
|
158
182
|
prepareChmod: proc ? assertProcAuthority : undefined,
|
|
159
183
|
verifyChmod: proc ? assertProcAuthority : undefined,
|
|
160
184
|
async chmod(mode) {
|
|
@@ -165,12 +189,13 @@ export async function pinNodeDirectoryForMode(dirPath, options = {}) {
|
|
|
165
189
|
await handle.chmod(mode);
|
|
166
190
|
}
|
|
167
191
|
},
|
|
168
|
-
close: () => handle.close(),
|
|
192
|
+
close: () => { observer?.dispose(); return handle.close(); },
|
|
169
193
|
});
|
|
170
194
|
await owner.verify();
|
|
171
195
|
return owner;
|
|
172
196
|
}
|
|
173
197
|
catch (error) {
|
|
198
|
+
observer?.dispose();
|
|
174
199
|
try {
|
|
175
200
|
await handle.close();
|
|
176
201
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BigIntStats, type Stats } from "node:fs";
|
|
2
|
-
import type { DirectoryReceipt } from "./directory-durability.js";
|
|
3
|
-
import type
|
|
2
|
+
import type { DirectoryReceipt, ExactDirectoryReceipt } from "./directory-durability.js";
|
|
3
|
+
import { type FileIdentityInput } from "./file-identity.js";
|
|
4
4
|
type ExactIdentity = Readonly<Pick<BigIntStats, "dev" | "ino">>;
|
|
5
5
|
declare const numericFields: readonly ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
6
6
|
declare const timeFields: readonly ["atime", "mtime", "ctime", "birthtime"];
|
|
@@ -13,11 +13,11 @@ type DirectoryAuthority = DirectoryProvenance & Readonly<{
|
|
|
13
13
|
path: string;
|
|
14
14
|
realPath: string;
|
|
15
15
|
}>;
|
|
16
|
-
export declare function directoryReceiptIdentity(
|
|
16
|
+
export declare function directoryReceiptIdentity(input: FileIdentityInput): ExactIdentity;
|
|
17
17
|
export declare function directoryReceiptAuthority(receipt: DirectoryReceipt<Stats | BigIntStats>): DirectoryAuthority;
|
|
18
|
-
export declare function ownDirectoryReceipt(receipt: DirectoryReceipt<Stats | BigIntStats>):
|
|
19
|
-
export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt):
|
|
20
|
-
export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats):
|
|
21
|
-
export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string):
|
|
18
|
+
export declare function ownDirectoryReceipt(receipt: DirectoryReceipt<Stats | BigIntStats>): ExactDirectoryReceipt;
|
|
19
|
+
export declare function copyRetainedDirectoryReceipt(receipt: DirectoryReceipt): ExactDirectoryReceipt;
|
|
20
|
+
export declare function createDirectoryReceiptFromIdentity(pathname: string, realPath: string, exactStat: BigIntStats): ExactDirectoryReceipt;
|
|
21
|
+
export declare function createDirectoryReceiptSync(directoryPath: string, label: string, canonicalize?: (pathname: string) => string): ExactDirectoryReceipt;
|
|
22
22
|
export declare function assertDirectoryReceiptCurrentSync(receipt: DirectoryReceipt, label: string, canonicalize?: (pathname: string) => string): void;
|
|
23
23
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs, {} from "node:fs";
|
|
2
2
|
import { inspectDirectoryIdentitySync } from "./directory-guard.js";
|
|
3
3
|
import { FsSafeError } from "./errors.js";
|
|
4
|
+
import { identityFromReceipt } from "./file-identity.js";
|
|
4
5
|
import { realpathSync } from "./realpath.js";
|
|
5
6
|
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
6
7
|
const numericFields = ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"];
|
|
@@ -9,10 +10,11 @@ const timeFields = ["atime", "mtime", "ctime", "birthtime"];
|
|
|
9
10
|
// fields and accept exact provenance only while its public identity is intact.
|
|
10
11
|
const authorities = new WeakMap();
|
|
11
12
|
const identities = new WeakMap();
|
|
12
|
-
export function directoryReceiptIdentity(
|
|
13
|
-
if (!
|
|
13
|
+
export function directoryReceiptIdentity(input) {
|
|
14
|
+
if (!input) {
|
|
14
15
|
throw new FsSafeError("path-mismatch", "directory receipt identity is missing");
|
|
15
16
|
}
|
|
17
|
+
const identity = identityFromReceipt(input);
|
|
16
18
|
const known = identities.get(identity);
|
|
17
19
|
const dev = identity.dev;
|
|
18
20
|
const ino = identity.ino;
|
|
@@ -41,9 +43,11 @@ function snapshotDirectoryReceipt(receipt) {
|
|
|
41
43
|
}
|
|
42
44
|
assertNoWindowsPathAlias(pathname, "filesystem");
|
|
43
45
|
assertNoWindowsPathAlias(realPath, "filesystem");
|
|
44
|
-
const identity = directoryReceiptIdentity(stat);
|
|
46
|
+
const identity = directoryReceiptIdentity(receipt.exactIdentity ?? stat);
|
|
45
47
|
const known = authorities.get(receipt);
|
|
46
|
-
|
|
48
|
+
const matches = (value, expected) => typeof value === "bigint" ? value === expected : value === Number(expected);
|
|
49
|
+
if (known && (!matches(stat.dev, known.identity.dev) || !matches(stat.ino, known.identity.ino) ||
|
|
50
|
+
pathname !== known.path || realPath !== known.realPath ||
|
|
47
51
|
identity.dev !== known.identity.dev || identity.ino !== known.identity.ino)) {
|
|
48
52
|
throw new FsSafeError("path-mismatch", "directory receipt changed before admission");
|
|
49
53
|
}
|
|
@@ -79,6 +83,7 @@ export function copyRetainedDirectoryReceipt(receipt) {
|
|
|
79
83
|
}
|
|
80
84
|
function receiptFromAuthority(authority) {
|
|
81
85
|
const receipt = {
|
|
86
|
+
exactIdentity: authority.identity,
|
|
82
87
|
path: authority.path,
|
|
83
88
|
realPath: authority.realPath,
|
|
84
89
|
identity: Object.assign(Object.create(fs.Stats.prototype), authority.metadata),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, type DirectoryReceipt, type DirectorySyncOutcome, type DurableDirectoryReceipt, type EnsureDurableDirectoryOptions, type PinnedDirectory, } from "./directory-durability.js";
|
|
1
|
+
export { ensureDurableDirectory, pinDirectory, syncDirectory, syncDirectoryBestEffort, syncDirectoryBestEffortSync, syncDirectorySync, type DirectoryReceipt, type ExactDirectoryReceipt, type DirectorySyncOutcome, type DurableDirectoryReceipt, type EnsureDurableDirectoryOptions, type PinnedDirectory, } from "./directory-durability.js";
|
|
2
2
|
export { isHardlinkFallbackError, publishFileExclusive, type PublishFileExclusiveResult, type PublishFileExclusiveStrategy, } from "./publish-file.js";
|
|
3
3
|
export type { PublishFileExclusiveCleanup, PublishFileExclusiveDirectorySyncFailure, PublishFileExclusiveFailureDetails, PublishFileExclusiveFailurePhase, PublishFileExclusiveSyncFailurePolicy, } from "./publish-file-failure.js";
|
|
4
4
|
export { sha256File, sha256FileSync, type Sha256FileInput, type Sha256FileSyncInput, type Sha256FileOptions, type Sha256FileResult, } from "./file-hash.js";
|
|
5
|
+
export type { ExactFileIdentity, ExactIdentityReceipt } from "./exact-identity.js";
|
|
@@ -18,7 +18,7 @@ function basename(name) {
|
|
|
18
18
|
}
|
|
19
19
|
function identity(value) {
|
|
20
20
|
const { dev, ino } = value;
|
|
21
|
-
if (typeof dev !== "bigint" || typeof ino !== "bigint" || dev <
|
|
21
|
+
if (typeof dev !== "bigint" || typeof ino !== "bigint" || dev < -(1n << 63n) || ino < -(1n << 63n) || ino === 0n ||
|
|
22
22
|
dev > 0xffffffffffffffffn || ino > 0xffffffffffffffffn) {
|
|
23
23
|
throw new FsSafeError("path-mismatch", "publication requires an exact known identity");
|
|
24
24
|
}
|
|
@@ -230,7 +230,7 @@ export function retainEntryForPublication(options) {
|
|
|
230
230
|
inspectEntry(sourcePath, source.expected);
|
|
231
231
|
const sourceFd = kind === "symlink"
|
|
232
232
|
? binding.openStagedSymlink(sourceParent.fd, source.basename)
|
|
233
|
-
:
|
|
233
|
+
: native.openBeneath(sourceParent.fd, source.basename, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK |
|
|
234
234
|
(kind === "directory" ? fs.constants.O_DIRECTORY : 0))?.fd;
|
|
235
235
|
if (!Number.isInteger(sourceFd) || sourceFd < 0) {
|
|
236
236
|
throw new FsSafeError("helper-unavailable", "publication source descriptor is unavailable");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type BigIntStats, type Stats } from "node:fs";
|
|
2
|
+
export type ExactFileIdentity = Readonly<{
|
|
3
|
+
dev: bigint;
|
|
4
|
+
ino: bigint;
|
|
5
|
+
}>;
|
|
6
|
+
export type ExactIdentityReceipt = {
|
|
7
|
+
readonly exactIdentity: ExactFileIdentity;
|
|
8
|
+
};
|
|
9
|
+
/** Copy scalars from one observation; never try to recover rounded identity bits. */
|
|
10
|
+
export declare function exactFileIdentity(stat: {
|
|
11
|
+
dev: number | bigint;
|
|
12
|
+
ino: number | bigint;
|
|
13
|
+
}): ExactFileIdentity;
|
|
14
|
+
/** Node stores unsigned libuv fields in a signed BigInt64Array. */
|
|
15
|
+
export declare function numberStatScalar(value: number | bigint): number;
|
|
16
|
+
/** Legacy metadata view of the very same bigint observation. */
|
|
17
|
+
export declare function numberStats(stat: BigIntStats): Stats;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import fs, {} from "node:fs";
|
|
2
|
+
import { FsSafeError } from "./errors.js";
|
|
3
|
+
/** Copy scalars from one observation; never try to recover rounded identity bits. */
|
|
4
|
+
export function exactFileIdentity(stat) {
|
|
5
|
+
const { dev, ino } = stat;
|
|
6
|
+
if ((typeof dev !== "bigint" && !Number.isSafeInteger(dev)) ||
|
|
7
|
+
(typeof ino !== "bigint" && !Number.isSafeInteger(ino))) {
|
|
8
|
+
throw new FsSafeError("path-mismatch", "file identity is not exact");
|
|
9
|
+
}
|
|
10
|
+
return Object.freeze({ dev: BigInt(dev), ino: BigInt(ino) });
|
|
11
|
+
}
|
|
12
|
+
/** Node stores unsigned libuv fields in a signed BigInt64Array. */
|
|
13
|
+
export function numberStatScalar(value) {
|
|
14
|
+
return typeof value === "bigint" ? Number(BigInt.asUintN(64, value)) : Number(value);
|
|
15
|
+
}
|
|
16
|
+
/** Legacy metadata view of the very same bigint observation. */
|
|
17
|
+
export function numberStats(stat) {
|
|
18
|
+
const result = Object.create(fs.Stats.prototype);
|
|
19
|
+
for (const field of ["dev", "mode", "nlink", "uid", "gid", "rdev", "blksize", "ino", "size", "blocks"]) {
|
|
20
|
+
result[field] = numberStatScalar(stat[field]);
|
|
21
|
+
}
|
|
22
|
+
for (const field of ["atime", "mtime", "ctime", "birthtime"]) {
|
|
23
|
+
const ns = stat[`${field}Ns`];
|
|
24
|
+
const remainder = ((ns % 1000000000n) + 1000000000n) % 1000000000n;
|
|
25
|
+
result[`${field}Ms`] = Number((ns - remainder) / 1000000000n) * 1_000 + Number(remainder) / 1_000_000;
|
|
26
|
+
// Stats.prototype supplies Node's lazy, correctly rounded Date getters.
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
@@ -113,7 +113,8 @@ export async function copyFileHandle(source, target, options = {}) {
|
|
|
113
113
|
const assertBeforeMutation = composeMutationAssertions(undefined, options.assertBeforeMutation);
|
|
114
114
|
const sourceStat = await inspectFileIdentity(() => {
|
|
115
115
|
signal?.throwIfAborted();
|
|
116
|
-
|
|
116
|
+
const fd = source.fd;
|
|
117
|
+
return fd < 0 ? source.stat({ bigint: true }) : fs.fstatSync(fd, { bigint: true });
|
|
117
118
|
});
|
|
118
119
|
signal?.throwIfAborted();
|
|
119
120
|
if (!sourceStat.isFile())
|
|
@@ -123,7 +124,8 @@ export async function copyFileHandle(source, target, options = {}) {
|
|
|
123
124
|
}
|
|
124
125
|
const targetStat = await inspectFileIdentity(() => {
|
|
125
126
|
signal?.throwIfAborted();
|
|
126
|
-
|
|
127
|
+
const fd = target.fd;
|
|
128
|
+
return fd < 0 ? target.stat({ bigint: true }) : fs.fstatSync(fd, { bigint: true });
|
|
127
129
|
});
|
|
128
130
|
signal?.throwIfAborted();
|
|
129
131
|
if (!targetStat.isFile())
|
|
@@ -10,7 +10,7 @@ export type Sha256FileResult = {
|
|
|
10
10
|
bytes: number;
|
|
11
11
|
digest: string;
|
|
12
12
|
};
|
|
13
|
-
export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions): Promise<Sha256FileResult>;
|
|
13
|
+
export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions, ownsPosition?: boolean): Promise<Sha256FileResult>;
|
|
14
14
|
export declare function sha256File(input: Sha256FileInput, options?: Sha256FileOptions): Promise<Sha256FileResult>;
|
|
15
15
|
/** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
|
|
16
16
|
export declare function sha256FileSync(input: Sha256FileSyncInput, options?: Sha256FileOptions): Sha256FileResult;
|