@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.
- package/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
- package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
- package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
- package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
- package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
- package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
- package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
- package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
- package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
- package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
- package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
- package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
- package/dist/api.js +234 -297
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +2 -2
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/subagent-hooks-api.js +1 -1
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
- package/node_modules/@openclaw/fs-safe/README.md +70 -379
- package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
- package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
- package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
- package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
- package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
- package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
- package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
- package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
- package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
- package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
- package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
- package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
- package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
- package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
- package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
- package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
- package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
- package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
- package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
- package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
- package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
- package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
- package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
- package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
- package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
- package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
- package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
- package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
- package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
- package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
- package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
- package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
- package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
- package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
- package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
- package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
- package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
- package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
- package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
- package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
- package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
- package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
- package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
- package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
- package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
- package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
- package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
- package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
- package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
- package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
- package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
- package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
- package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
- package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
- package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
- package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
- package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
- package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
- package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
- package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
- package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
- package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
- package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
- package/node_modules/@openclaw/fs-safe/package.json +10 -10
- 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-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-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/package.json +5 -5
- package/skills/feishu-wiki/SKILL.md +1 -1
- package/dist/.setup/accounts-wRqItHug.mjs +0 -206
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
- package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
- package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
- package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
- package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
|
@@ -64,28 +64,50 @@ This does not promise delivery for a differently spelled alias that appears and
|
|
|
64
64
|
disappears entirely between scans: without an observed identity, it cannot be
|
|
65
65
|
admitted as the selected path. Observation is not a complete transient history.
|
|
66
66
|
Raw event names remain private: detail comes from guarded scans, prior guarded
|
|
67
|
-
snapshots, or explicitly configured targets.
|
|
68
|
-
|
|
67
|
+
snapshots, or explicitly configured targets. A non-target name absent from both
|
|
68
|
+
snapshots is ignored only when its parent directory has the same device/inode in
|
|
69
|
+
both guarded passes. Otherwise the unclassifiable selected hint loses detail.
|
|
70
|
+
Hints for excluded or unselected paths are
|
|
69
71
|
ignored. Exclusion callbacks are synchronous; excluded directories are recorded
|
|
70
72
|
without descent. Bounded exclusion records recognize late deletion hints.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
Pending hint pressure folds filenames into directory-level subtree hints, coarsening
|
|
74
|
+
toward the Root as needed. Unrelated folds are ignored after guarded alias admission;
|
|
75
|
+
relevant or uncertain folds trigger a full guarded pass and publish its snapshot
|
|
76
|
+
diff. The hint directory itself is never published without observation. Genuine
|
|
77
|
+
backend event loss and snapshot-diff budget exhaustion still invalidate every
|
|
78
|
+
scope, including when an excluded subtree caused genuine kernel loss.
|
|
79
|
+
|
|
80
|
+
On Linux and macOS, an undecodable child name triggers a structural hint for its
|
|
81
|
+
containing directory. If that directory's children are selected by a tree scope
|
|
82
|
+
with remaining depth, a guarded scan fails closed with `invalid-path` while the
|
|
83
|
+
name remains present. An undecodable sibling beside an ancestor or missing-scope
|
|
84
|
+
anchor is ignored: it cannot match a validated literal scope component. Raw or
|
|
85
|
+
lossily decoded names are never published or used for child I/O. Windows already
|
|
86
|
+
fails closed by reconciling after an undecodable UTF-16 notification.
|
|
87
|
+
|
|
88
|
+
Linux nameless self-events (including directory chmod, rename and deletion) retain
|
|
89
|
+
structural detail for the watched directory. Self-events for the Root itself,
|
|
90
|
+
kernel queue overflow and malformed transport buffers still lose detail.
|
|
73
91
|
|
|
74
92
|
## Transport and mode
|
|
75
93
|
|
|
76
94
|
| Platform/runtime | `auto` | Event transport / limitation |
|
|
77
95
|
| --- | --- | --- |
|
|
78
96
|
| Node.js on Linux with addon | `events` | One shared Rust thread and inotify instance; a nonrecursive watch per distinct directory inode. |
|
|
79
|
-
| Node.js on macOS with addon | `events` |
|
|
97
|
+
| Node.js on macOS with addon | `events` | Entry scopes use descriptor-bound kqueue watches; tree scopes share one FSEvents stream per subscription. Pathname activity after a swap remains advisory. |
|
|
80
98
|
| Node.js on Windows with addon | `events` | One recursive ReadDirectoryChangesW Root handle per subscription on the shared IOCP hub; the open handle prevents ordinary renames of the Root's ancestors. |
|
|
81
99
|
| Bun / other unsupported runtimes | `poll` | TSFN lifetime and shutdown have not been qualified; `events` rejects. |
|
|
82
100
|
| Missing/disabled addon | `poll` | `events` rejects with `FsSafeError("helper-unavailable")`. |
|
|
83
101
|
|
|
84
102
|
The shared hub sleeps until a filesystem event, command, or callback acknowledgement:
|
|
85
|
-
Linux blocks on inotify plus eventfd, Windows on IOCP, and macOS on
|
|
86
|
-
|
|
103
|
+
Linux blocks on inotify plus eventfd, Windows on IOCP, and macOS on kqueue with a
|
|
104
|
+
command wake (FSEvents wakes it from the serial dispatch queue). There is no native
|
|
87
105
|
polling timer; the independent JS reconciliation interval remains authoritative.
|
|
88
106
|
|
|
107
|
+
Linux discards queued events for watches retired by fs-safe, including their
|
|
108
|
+
`IN_IGNORED` echoes, without invalidating unrelated subscriptions. A genuinely
|
|
109
|
+
unknown descriptor or kernel queue overflow still invalidates every subscription.
|
|
110
|
+
|
|
89
111
|
`mode` is required. `poll` never starts or loads the watch hub; guarded scans
|
|
90
112
|
may still use the existing addon. Existing `FS_SAFE_NATIVE_MODE=off` and
|
|
91
113
|
`require` policies apply: `require` plus an unavailable event backend rejects
|
|
@@ -104,15 +126,29 @@ Nonblocking TSFN batches cannot block the hub on JavaScript, and per-owner
|
|
|
104
126
|
pending detail and queued batches are bounded. The last removal stops and joins
|
|
105
127
|
the native thread. No Worker threads, eval programs, or JS `fs.watch` are used.
|
|
106
128
|
|
|
107
|
-
macOS
|
|
108
|
-
|
|
129
|
+
macOS entry scopes use nonrecursive `EVFILT_VNODE` watches on the admitted parent
|
|
130
|
+
and, when present, the entry itself. Parent activity requests a guarded scan
|
|
131
|
+
without supplying filenames. The entry descriptor covers content and attribute
|
|
132
|
+
changes and is replaced when a guarded scan admits a new identity. Missing paths
|
|
133
|
+
use their nearest admitted ancestor. Descriptors are opened without following
|
|
134
|
+
symlinks; a symlink entry uses `O_SYMLINK` to observe the link itself. Every
|
|
135
|
+
descriptor's device/inode must match the guarded observation. There are at most
|
|
136
|
+
two retained descriptors per entry scope (128 scopes maximum), counted in health
|
|
137
|
+
`directories`; descriptor exhaustion fails registration with `EMFILE`. Removal
|
|
138
|
+
closes these descriptors on the hub before returning. Deep unselected traffic
|
|
139
|
+
does not reach an entry-only subscription.
|
|
140
|
+
|
|
141
|
+
Tree scopes of every depth retain FileEvents, NoDefer and WatchRoot with a 30 ms
|
|
142
|
+
FSEvents latency. After guarded admission, streams use only selected tree anchors,
|
|
109
143
|
falling back to the nearest admitted ancestor for missing paths. Nested anchors
|
|
110
144
|
are deduplicated, with at most 128 paths. The eight shallowest non-overlapping
|
|
111
145
|
excluded directories are also passed to `FSEventStreamSetExclusionPaths`.
|
|
112
146
|
When the stream paths change, the old stream is stopped, invalidated and released
|
|
113
147
|
on its dispatch queue, then its replacement starts before another guarded pass
|
|
114
148
|
covers the handover. Native exclusions reduce traffic but cannot eliminate real
|
|
115
|
-
FSEvents drops, including during recursive deletion.
|
|
149
|
+
FSEvents drops, including during recursive deletion. A shallow tree anchored above
|
|
150
|
+
a busy unselected subtree still receives recursive traffic; pending hints fold
|
|
151
|
+
under pressure, while genuine FSEvents drops can still overflow.
|
|
116
152
|
Absolute hints are reduced lexically against the admitted canonical Root;
|
|
117
153
|
outside paths never become detail. Dropped/wrapped streams, RootChanged and
|
|
118
154
|
Unmount trigger guarded reconciliation. Pathname hints can reflect activity
|
|
@@ -157,6 +193,20 @@ handles and delivery queues, and closing one does not retire another's observati
|
|
|
157
193
|
|
|
158
194
|
Periodic guarded reconciliation runs without needing an event. It catches
|
|
159
195
|
missed events and works on filesystems where native hints are incomplete.
|
|
196
|
+
Hint validation, scope relevance, and spelling-alias admission share the internal
|
|
197
|
+
hint module. Bounded change merging preserves structural precedence and insertion
|
|
198
|
+
order; nameless children require remaining tree depth, while folded subtrees also
|
|
199
|
+
cover ancestors of selected scopes.
|
|
200
|
+
Detailed native batches first pass guarded scope and spelling-alias admission;
|
|
201
|
+
proven unrelated activity does not schedule a scan. Relevant entry hints refresh
|
|
202
|
+
the entry scopes, while tree hints refresh the affected directory and its identity
|
|
203
|
+
chain, retaining unchanged sibling subtrees. Namespace changes, coarse directory
|
|
204
|
+
hints, vanished or hard-linked leaves, changed topology, uncertain identities,
|
|
205
|
+
backend loss, and batches without filenames still receive a full guarded pass.
|
|
206
|
+
`reconcile()`, initial admission, and scope replacement always reconcile every scope.
|
|
207
|
+
The periodic timer is independent of event traffic, so frequent hints cannot defer
|
|
208
|
+
the full missed-event check. Repeated passes reuse bounded pathname strings, never
|
|
209
|
+
cached metadata or filesystem authority.
|
|
160
210
|
When polling is selected, the interval is `pollIntervalMs`, then `intervalMs`,
|
|
161
211
|
then 1000 ms, in that order. This applies to explicit `mode: "poll"`, `auto`
|
|
162
212
|
selecting polling, and `auto` falling back after an unsupported event backend.
|
|
@@ -166,6 +216,11 @@ uses 25 ms polling when needed and retains the 30-second events reconciliation.
|
|
|
166
216
|
Scans are metadata comparisons: content changes preserving all compared
|
|
167
217
|
metadata may be missed in polling mode. No mode promises transactional
|
|
168
218
|
snapshots, complete history, or hard real-time delivery.
|
|
219
|
+
On Node.js, directory name reads avoid a thread-pool round trip per entry. Scans yield to
|
|
220
|
+
the event loop between bounded groups of at most 32 names so cancellation and
|
|
221
|
+
other work can progress; every entry still receives the same identity checks
|
|
222
|
+
and entry-budget admission before its metadata is read.
|
|
223
|
+
Bun and Deno retain asynchronous name reads.
|
|
169
224
|
|
|
170
225
|
`ready` resolves after the first complete guarded scan establishes the baseline,
|
|
171
226
|
even while writes continue. Events mode installs each directory registration
|
|
@@ -186,9 +241,9 @@ transient descendant scan errors produce structural invalidations, preserving th
|
|
|
186
241
|
Root identity checks. Events during a pass coalesce into one pending pass and
|
|
187
242
|
retain bounded detail regardless of how long the scan takes. The 25 ms hint
|
|
188
243
|
coalescing window does not impose a scan deadline. A full native callback queue
|
|
189
|
-
retains its bounded pending batch for retry.
|
|
190
|
-
|
|
191
|
-
detail. Sustained writes cannot exhaust a pass budget or disable observation.
|
|
244
|
+
retains its bounded pending batch for retry. Pending native and JavaScript hint queues degrade to coarse subtree hints when
|
|
245
|
+
full. Genuine backend loss, exhausted snapshot-diff capacity, or an unclassifiable
|
|
246
|
+
selected hint emits `overflow` without detail. Sustained writes cannot exhaust a pass budget or disable observation.
|
|
192
247
|
|
|
193
248
|
`reconcile()` resolves after a complete pass that **started after the call**. Calls
|
|
194
249
|
waiting for the same future pass coalesce; an earlier in-flight pass cannot satisfy
|
|
@@ -202,7 +257,8 @@ including during startup or reconciliation. Invalidations still arrive while
|
|
|
202
257
|
other work keeps the process alive. Persistent and non-persistent subscriptions
|
|
203
258
|
have independent lifetimes; closing the last persistent one lets Node exit.
|
|
204
259
|
Native environment cleanup retires any remaining event registrations and joins
|
|
205
|
-
the hub at exit. `signal` triggers close
|
|
260
|
+
the hub at exit. `signal` triggers close, including when a caller's abort
|
|
261
|
+
listener stops event propagation;
|
|
206
262
|
await `close()` or `[Symbol.asyncDispose]()` to join owned work.
|
|
207
263
|
|
|
208
264
|
`health()` returns `starting`, `ready`, `reconciling`, `unavailable`, or `closed`,
|
|
@@ -249,3 +305,29 @@ replay one with `--replay <failure.json>`. Event mode requires a working native
|
|
|
249
305
|
binding and never silently falls back to polling. The small
|
|
250
306
|
`test/watch-model.test.ts` corpus runs in ordinary CI; native-event cases also run
|
|
251
307
|
when `FS_SAFE_TEST_WATCH_EVENTS=1`. Keep fixtures on normal `os.tmpdir()` storage.
|
|
308
|
+
|
|
309
|
+
The nightly watch-stress workflow includes 500 seeds per mode with the finite
|
|
310
|
+
state corpus on all five runners. Add `--transitions` to exercise that corpus
|
|
311
|
+
before each random sequence in a local run.
|
|
312
|
+
It varies `maxPendingPaths` from 2 through 8, creates missing tree descendants
|
|
313
|
+
beside concurrent sibling churn, uses filesystem-proven case and Unicode aliases,
|
|
314
|
+
and covers atomic-save names, excluded subtrees, Linux undecodable siblings,
|
|
315
|
+
directory chmod/rename/deletion, scope replacement, and subscription retirement.
|
|
316
|
+
A separate quiet subscription must remain unaffected. The checker records genuine
|
|
317
|
+
backend loss and rejects overflow without that loss or a selected diff exceeding
|
|
318
|
+
the configured budget. Each checkpoint permits at most four guarded passes and
|
|
319
|
+
refreshes the consumer cache only in response to invalidation.
|
|
320
|
+
Volumes without Unicode-normalization aliases also receive a distinct spelling
|
|
321
|
+
beside the missing target. On Linux a selected undecodable child must fail its
|
|
322
|
+
owner closed with `invalid-path` while the other subscription stays healthy.
|
|
323
|
+
|
|
324
|
+
```sh
|
|
325
|
+
node scripts/watch-stress/model-runner.mjs --transitions --seeds 500 --mode both --output watch-transition-results.json
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
For transport diagnosis, add `--native-only`: event-mode checkpoints then wait up
|
|
329
|
+
to 400 intervals of 25 ms without calling `reconcile()`. This stronger diagnostic
|
|
330
|
+
depends on native event delivery; it is separate from the periodic-reconciliation
|
|
331
|
+
guarantee and must not turn an unavailable transport into a passing run. Transition
|
|
332
|
+
failures retain their seed and mode in the report; replay them with `--seed N
|
|
333
|
+
--seeds 1 --mode events --transitions` and the same diagnostic options.
|
|
@@ -64,6 +64,35 @@ Failed-write cleanup compares exact parent and file identities, including large
|
|
|
64
64
|
Windows file indexes. Replaced paths and paths whose ownership cannot be verified
|
|
65
65
|
are preserved.
|
|
66
66
|
|
|
67
|
+
### Windows link modes
|
|
68
|
+
|
|
69
|
+
With `mutationSymlinks` omitted, Windows buffered replacement `write()` and
|
|
70
|
+
`writeJson()` calls (`overwrite` omitted or `true`) currently differ by
|
|
71
|
+
implementation. The native pinned path rejects a final file
|
|
72
|
+
symlink with `path-alias`. The legacy JavaScript path can follow an unchanged
|
|
73
|
+
contained final link and replace its admitted target, preserving the link itself.
|
|
74
|
+
It reauthorizes the original link's target before staging and publication when
|
|
75
|
+
mutation policy is present, and retains its file/parent identity checks.
|
|
76
|
+
The legacy path is used by native mode `off`, by `auto` without a binding,
|
|
77
|
+
and by the explicit `renameIdentity: "verify-content-with-lock"` policy.
|
|
78
|
+
|
|
79
|
+
An omitted mutation policy also leaves parent-junction behavior implementation
|
|
80
|
+
dependent: native Windows parent admission refuses reparse traversal and can
|
|
81
|
+
report `invalid-path`, while the legacy writer can use the junction's admitted
|
|
82
|
+
contained target. This does not authorize an escaping target or bypass deny
|
|
83
|
+
policy. Set `mutationSymlinks: "reject"` explicitly for uniform link rejection
|
|
84
|
+
across these implementations, either in Root defaults or per call:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { root } from "@openclaw/fs-safe";
|
|
88
|
+
|
|
89
|
+
const files = await root("C:/workspace", { mutationSymlinks: "reject" });
|
|
90
|
+
await files.write("state.json", "{}");
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
These are existing compatibility differences; omitted policy does not currently
|
|
94
|
+
provide uniform Windows link handling. See [write-side link policy](security-model.md#symlinks-write-side).
|
|
95
|
+
|
|
67
96
|
## Denying mutations
|
|
68
97
|
|
|
69
98
|
All mutation verbs accept `denyMutations?: DenyMutationPolicy`, either as a root default or per-call option:
|
|
@@ -86,7 +115,10 @@ await fs.remove(".ssh/id_rsa"); // throws FsSafeError code "denied-path"
|
|
|
86
115
|
|
|
87
116
|
### `fs.write(rel, data, options?)`
|
|
88
117
|
|
|
89
|
-
Overwrite or create.
|
|
118
|
+
Overwrite or create. Replacement is atomic; buffered `overwrite: false` can
|
|
119
|
+
expose incomplete content on the JavaScript fallback. Use
|
|
120
|
+
[`create()` with `atomic: true`](#atomic-buffered-creation) when create-only
|
|
121
|
+
publication must wait for complete content.
|
|
90
122
|
|
|
91
123
|
```ts
|
|
92
124
|
await fs.write("state/last-run.json", JSON.stringify(run));
|
|
@@ -107,6 +139,11 @@ await fs.write("notes/today.txt", "hello\n", { encoding: "utf8" });
|
|
|
107
139
|
| `overwrite` | `boolean` | `true`; `false` is create-only. |
|
|
108
140
|
| `renameIdentity` | `RenameIdentityPolicy` | `"strict"`. |
|
|
109
141
|
|
|
142
|
+
`mkdir: false` requires existing parents and never creates a missing parent.
|
|
143
|
+
On POSIX, otherwise permitted relative in-root parent aliases remain available
|
|
144
|
+
to buffered and streamed writes and copies with native support enabled or
|
|
145
|
+
disabled. An explicit mutation symlink policy still applies.
|
|
146
|
+
|
|
110
147
|
`write`, `create`, `writeJson`, `createJson`, `append`, and `copyIn` accept `durable`.
|
|
111
148
|
Precedence is per-call option, then `Root.defaults.durable`, then `true`;
|
|
112
149
|
an explicitly `undefined` call option preserves the root default.
|
|
@@ -150,6 +187,10 @@ alone is never proof that the name still refers to the expected file.
|
|
|
150
187
|
### `fs.create(rel, data, options?)`
|
|
151
188
|
|
|
152
189
|
Don't-clobber variant of `write()`. Throws `already-exists` if the target is there.
|
|
190
|
+
An existing regular file or directory reports `already-exists` on every backend,
|
|
191
|
+
including buffered, atomic and streamed creation. Rejected directories and their
|
|
192
|
+
contents remain unchanged. Other non-regular types retain `not-file`; boundary,
|
|
193
|
+
explicit symlink policy, deny and hardlink failures retain their precedence.
|
|
153
194
|
Create-only preflight preserves boundary, alias, hardlink, and type checks without
|
|
154
195
|
opening an existing target to inherit its mode; a fresh file uses the requested
|
|
155
196
|
mode or the normal new-file default. When the native binding is in use
|
|
@@ -304,7 +345,12 @@ type RootWriteJsonOptions = RootWriteOptions & {
|
|
|
304
345
|
|
|
305
346
|
### `fs.append(rel, data, options?)`
|
|
306
347
|
|
|
307
|
-
Open in append mode, write, sync the file handle, and close. Honors `mkdir` for
|
|
348
|
+
Open in append mode, write, sync the file handle, and close. Honors `mkdir` for
|
|
349
|
+
the parent directory and syncs that directory when creating the file.
|
|
350
|
+
`durable: false` skips both syncs. Pass `prependNewlineIfNeeded: true` to separate
|
|
351
|
+
existing content from appended text when neither side supplies a newline. Strings use their `encoding` for the
|
|
352
|
+
newline check, including UTF-16LE; Buffers use a single LF byte. Empty strings
|
|
353
|
+
and Buffers add no separator; an empty append still creates a missing file.
|
|
308
354
|
|
|
309
355
|
`mode` selects the creation mode, defaulting to `0o600` when neither the call nor
|
|
310
356
|
the Root supplies it. On POSIX, the process umask can further restrict that mode;
|
|
@@ -317,23 +363,87 @@ await fs.append("logs/today.log", `[${ts}] ${line}\n`);
|
|
|
317
363
|
await fs.append("notes/scratch.md", "* new bullet", { prependNewlineIfNeeded: true });
|
|
318
364
|
```
|
|
319
365
|
|
|
320
|
-
For high-volume logging, consider [`openWritable`](#openwritable) and a long-lived append handle. Direct append-mode writes preserve kernel append semantics, but they are not atomic against external rotators that rename or unlink the target.
|
|
366
|
+
For high-volume logging, consider [`openWritable`](#openwritable-for-streaming) and a long-lived append handle. Direct append-mode writes preserve kernel append semantics, but they are not atomic against external rotators that rename or unlink the target.
|
|
321
367
|
|
|
322
368
|
### `fs.copyIn(rel, sourceAbsPath, options?)`
|
|
323
369
|
|
|
324
|
-
|
|
370
|
+
`copyIn` accepts a `RootCopySource`: a trusted absolute source path or a file
|
|
371
|
+
within another Root. The guarded form supplies `root` with only its `open` and
|
|
372
|
+
`stat` read capabilities, plus `relativePath`:
|
|
325
373
|
|
|
326
374
|
```ts
|
|
327
|
-
await
|
|
328
|
-
|
|
375
|
+
const source = await root("/srv/templates");
|
|
376
|
+
const destination = await root("/srv/workspace");
|
|
377
|
+
await destination.copyIn("config/settings.json", {
|
|
378
|
+
root: source,
|
|
379
|
+
relativePath: "config/settings.json",
|
|
380
|
+
}, {
|
|
381
|
+
overwrite: false,
|
|
382
|
+
clone: "auto",
|
|
383
|
+
mode: 0o600,
|
|
384
|
+
signal: AbortSignal.timeout(30_000),
|
|
329
385
|
});
|
|
330
386
|
```
|
|
331
387
|
|
|
332
|
-
|
|
333
|
-
`
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
388
|
+
The source Root applies its read policies, including confinement and symlink
|
|
389
|
+
handling. `sourceHardlinks` overrides its hardlink policy only when supplied;
|
|
390
|
+
otherwise the source Root default is retained. The admitted source
|
|
391
|
+
descriptor stays open through copying and source-identity verification; copying
|
|
392
|
+
does not consume its current file position. Both forms enforce `maxBytes` while
|
|
393
|
+
reading, including when a file grows after admission, and use bounded buffers.
|
|
394
|
+
Copies have independent file data; changing either file cannot change the other.
|
|
395
|
+
Set `preserveSourceMode: true` to select the mode from the admitted source
|
|
396
|
+
descriptor. An explicit numeric `mode`, including `Root.defaults.mode`, takes
|
|
397
|
+
precedence. By default, copying retains the existing destination-mode rules.
|
|
398
|
+
The operation verifies source identity, not a coherent snapshot of concurrent
|
|
399
|
+
in-place edits. Keep the source unchanged when snapshot consistency is required.
|
|
400
|
+
|
|
401
|
+
`overwrite` defaults to `true`, preserving the existing replacement behavior.
|
|
402
|
+
With `overwrite: false`, an existing destination produces `already-exists` and
|
|
403
|
+
is never altered. Copying prepares a private sibling file before publishing its
|
|
404
|
+
completed contents. Native mode uses no-replace rename. The guarded JavaScript
|
|
405
|
+
fallback links the completed stage and removes its temporary name in the same
|
|
406
|
+
JavaScript turn; the filesystem must support hardlinks. Other processes can
|
|
407
|
+
briefly observe both names. The source is never hardlinked to the destination.
|
|
408
|
+
|
|
409
|
+
`clone` chooses the file-data transfer strategy through `CopyCloneMode`, shared
|
|
410
|
+
with [`copyTree`](copy.md#api). File copies default to `"never"`; tree copies
|
|
411
|
+
default to `"auto"`:
|
|
412
|
+
|
|
413
|
+
| Value | Behavior |
|
|
414
|
+
| --- | --- |
|
|
415
|
+
| `never` | Copy regular file bytes using reads and writes, without explicit cloning or copy offload. |
|
|
416
|
+
| `auto` | Try native file cloning, then copy offload or ordinary byte copying when cloning is unavailable. |
|
|
417
|
+
| `always` | Require native cloning; fail when the binding or filesystem cannot provide it. |
|
|
418
|
+
|
|
419
|
+
Native file cloning supports APFS and supported Linux filesystems. Windows
|
|
420
|
+
currently uses byte copying for `never` and `auto`; `always` fails. Clone choice
|
|
421
|
+
does not change modes, durability, root confinement, or source and publication
|
|
422
|
+
identity checks. The shared strategy does not replace Root's guarded regular-file
|
|
423
|
+
contract with `copyTree`'s caller-owned immutable-tree and metadata contract.
|
|
424
|
+
|
|
425
|
+
An already aborted `signal` prevents I/O. Cancellation during copying waits for
|
|
426
|
+
admitted reads and native work to settle, then cleans only the owned unpublished
|
|
427
|
+
stage. The final authority check runs before publication. Once publication has
|
|
428
|
+
occurred, later cancellation or verification failure preserves the destination.
|
|
429
|
+
The synchronous optional `onDestinationPublished` callback receives a frozen
|
|
430
|
+
`RootCopyPublicationReceipt` containing `{ path, dev, ino }`, with exact bigint identity immediately after
|
|
431
|
+
publication, before later checks can fail. Callback errors also preserve the
|
|
432
|
+
published file and retain their original thrown value when cleanup succeeds,
|
|
433
|
+
including errors whose metadata cannot be inspected. Promise, thenable, and synchronous or asynchronous generator
|
|
434
|
+
results reject with `TypeError`; returned generators are never advanced. Other
|
|
435
|
+
synchronous return values are ignored. This receipt records an outcome; it does
|
|
436
|
+
not authorize removing a file that another actor may have edited. Application recovery and cooperative
|
|
437
|
+
locking remain caller-owned.
|
|
438
|
+
|
|
439
|
+
Existing `copyIn` callers must account for completed destinations retained after
|
|
440
|
+
a post-publication source-verification failure, even without the new options.
|
|
441
|
+
Recovery must inspect current destination state rather than assume a rejected
|
|
442
|
+
copy left no file.
|
|
443
|
+
|
|
444
|
+
`durable` follows the Root default (`true` when omitted at both levels);
|
|
445
|
+
`false` skips file and parent-directory syncs. Use `sourceHardlinks: "reject"`
|
|
446
|
+
to refuse hardlinked sources. There is no encoding option: copying preserves bytes.
|
|
337
447
|
|
|
338
448
|
### `fs.move(from, to, options?)`
|
|
339
449
|
|
|
@@ -370,7 +480,11 @@ paths are rechecked after the live mutation-authority callback and before
|
|
|
370
480
|
dispatch. The Root and retained parents are fenced again after any such callback.
|
|
371
481
|
These checks retain the documented final check-to-syscall race.
|
|
372
482
|
|
|
373
|
-
For `{ overwrite: true }`,
|
|
483
|
+
For `{ overwrite: true }`, `require` mode retains both parents, checks the source
|
|
484
|
+
identity relative to its parent, and renames through those descriptors (or the
|
|
485
|
+
corresponding Windows handles). It then rechecks the parents and destination
|
|
486
|
+
identity. `require` rejects with `helper-unavailable` if this operation's native
|
|
487
|
+
entry point is absent. In `off` and default `auto`, the JavaScript path checks both parent directories
|
|
374
488
|
before and after the rename. A failed post-operation check rejects even though
|
|
375
489
|
the rename may already have completed; rejection does not imply rollback.
|
|
376
490
|
|
|
@@ -481,15 +595,35 @@ reasons, are propagated without adding or changing their details. Context is
|
|
|
481
595
|
diagnostic; it is not permission to retry or mutate an entry.
|
|
482
596
|
|
|
483
597
|
Removal is incremental, not atomic. A later budget, cancellation, identity, or
|
|
484
|
-
filesystem failure does not restore already removed entries.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
598
|
+
filesystem failure does not restore already removed entries. Required-mode POSIX
|
|
599
|
+
removal checks the entry without following links and uses `unlinkat` relative
|
|
600
|
+
to a retained parent; empty directories use `AT_REMOVEDIR`. Recursive native
|
|
601
|
+
removal enumerates through retained directory descriptors and retains its
|
|
602
|
+
ordering, budgets, abort checks, and denied-descendant preflight. Linux descent
|
|
603
|
+
requires `openat2` with `RESOLVE_NO_XDEV`; macOS checks mount identity.
|
|
604
|
+
|
|
605
|
+
Windows nonrecursive removal checks the identity of the exact handle opened
|
|
606
|
+
relative to the parent and deletes that object with `FileDispositionInfoEx`,
|
|
607
|
+
without following a final reparse point. `require` rejects recursive removal
|
|
608
|
+
on Windows and on Linux without that mount-bounded capability. `off` and default
|
|
609
|
+
`auto` retain the JavaScript implementation even when the addon is loaded: its pathname checks are best-effort
|
|
610
|
+
against a hostile concurrent process and cannot prevent every outside side
|
|
611
|
+
effect. POSIX native deletion is also not an atomic expected-inode conditional
|
|
612
|
+
unlink. See the [platform matrix](security-model.md#native-root-mutation-capabilities)
|
|
613
|
+
for the precise parent-pinning guarantee and remaining same-call limitations.
|
|
488
614
|
|
|
489
615
|
### `fs.mkdir(rel)`
|
|
490
616
|
|
|
491
617
|
`mkdir -p`. Creates missing parents.
|
|
492
618
|
|
|
619
|
+
`require` mode creates each missing component relative to a retained directory,
|
|
620
|
+
opens the child without following a final symlink, and checks its identity before
|
|
621
|
+
continuing. Windows private creation retains its protected native creator,
|
|
622
|
+
which verifies the admitted parent identity and calls handle-relative
|
|
623
|
+
`NtCreateFile` with a protected security descriptor. `require` refuses a missing
|
|
624
|
+
native capability; default `auto` retains its existing best-effort path and does
|
|
625
|
+
not confine mkdir under hostile concurrency.
|
|
626
|
+
|
|
493
627
|
```ts
|
|
494
628
|
await fs.mkdir("snapshots/2026/05");
|
|
495
629
|
```
|
|
@@ -538,6 +672,28 @@ destination — there is no atomic-rename step. For exclusive publication of a
|
|
|
538
672
|
complete stream, use [`create()`](#streamed-creation). For streamed replacement,
|
|
539
673
|
the [`atomic`](atomic.md) helpers provide a staged writer.
|
|
540
674
|
|
|
675
|
+
When creating a missing file, `require` mode uses an exclusive no-follow open
|
|
676
|
+
beneath a retained parent and verifies the same inode during handoff to the
|
|
677
|
+
returned Node `FileHandle`. `append()` uses this path too. Required creation
|
|
678
|
+
fails with `helper-unavailable` with an incomplete addon, or when a
|
|
679
|
+
restrictive mode or umask prevents that handoff without widening initial permissions.
|
|
680
|
+
Default `auto` retains its existing JavaScript creation path even when the addon
|
|
681
|
+
is loaded, and does not confine creation under hostile concurrency. Required creation confinement
|
|
682
|
+
does not upgrade the returned `containment: "best-effort"` label or provide a
|
|
683
|
+
transaction around later caller writes.
|
|
684
|
+
|
|
685
|
+
On Windows, fallback creation rejects an observed dangling final symlink before
|
|
686
|
+
opening it, preserving the missing referent even when it is outside the Root.
|
|
687
|
+
Create-only writes and standalone exclusive creators apply the same preflight;
|
|
688
|
+
exclusive creation treats an existing symlink as a collision. This check does not close the race
|
|
689
|
+
between inspecting the leaf and opening it. An existing contained final symlink
|
|
690
|
+
still follows the omitted-policy behavior described above.
|
|
691
|
+
|
|
692
|
+
The native creation syscall applies the requested mode and inherited ACLs.
|
|
693
|
+
If those kernel-created permissions prevent the subsequent FileHandle handoff,
|
|
694
|
+
the operation fails and attempts identity-bound cleanup; it does not widen an
|
|
695
|
+
inherited ACL with a later `chmod`.
|
|
696
|
+
|
|
541
697
|
For all three write modes, `mode` only selects new-file creation permissions,
|
|
542
698
|
defaulting to `0o600` when neither the call nor the Root supplies it. POSIX
|
|
543
699
|
permissions remain subject to the process umask; existing files are not chmodded.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -173,20 +173,20 @@
|
|
|
173
173
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
174
174
|
},
|
|
175
175
|
"optionalDependencies": {
|
|
176
|
-
"@openclaw/fs-safe-darwin-arm64": "0.
|
|
177
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
178
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.
|
|
179
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.
|
|
180
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.
|
|
181
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.
|
|
182
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.
|
|
176
|
+
"@openclaw/fs-safe-darwin-arm64": "0.23.0",
|
|
177
|
+
"@openclaw/fs-safe-darwin-x64": "0.23.0",
|
|
178
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.23.0",
|
|
179
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.23.0",
|
|
180
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.23.0",
|
|
181
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.23.0",
|
|
182
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.23.0",
|
|
183
183
|
"jszip": "^3.10.2"
|
|
184
184
|
},
|
|
185
185
|
"devDependencies": {
|
|
186
186
|
"@emnapi/runtime": "2.0.0-alpha.5",
|
|
187
187
|
"@napi-rs/cli": "3.10.5",
|
|
188
188
|
"@types/node": "^26.6.1",
|
|
189
|
-
"@vitest/coverage-v8": "5.0.
|
|
189
|
+
"@vitest/coverage-v8": "5.0.2",
|
|
190
190
|
"fast-check": "^4.10.1",
|
|
191
191
|
"istanbul-lib-coverage": "3.2.2",
|
|
192
192
|
"istanbul-lib-report": "3.0.1",
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"sigstore": "5.0.0",
|
|
195
195
|
"tar": "7.5.22",
|
|
196
196
|
"typescript": "^7.0.2",
|
|
197
|
-
"vite": "8.3.
|
|
197
|
+
"vite": "8.3.1",
|
|
198
198
|
"vitest": "^5.0.1"
|
|
199
199
|
},
|
|
200
200
|
"engines": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/feishu",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.10.1-beta.1",
|
|
4
4
|
"description": "OpenClaw Feishu/Lark channel plugin for chats and workplace tools (community maintained by @m1heng).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@larksuiteoapi/node-sdk": "1.74.0",
|
|
12
|
-
"@openclaw/fs-safe": "0.
|
|
12
|
+
"@openclaw/fs-safe": "0.23.0",
|
|
13
13
|
"mdast-util-from-markdown": "2.0.3",
|
|
14
14
|
"mdast-util-gfm-table": "2.0.0",
|
|
15
15
|
"micromark-extension-gfm-table": "2.1.2",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"zod": "4.6.5"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"openclaw": ">=2026.
|
|
20
|
+
"openclaw": ">=2026.10.1-beta.1"
|
|
21
21
|
},
|
|
22
22
|
"peerDependenciesMeta": {
|
|
23
23
|
"openclaw": {
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
"minHostVersion": ">=2026.5.29"
|
|
65
65
|
},
|
|
66
66
|
"compat": {
|
|
67
|
-
"pluginApi": ">=2026.
|
|
67
|
+
"pluginApi": ">=2026.10.1-beta.1"
|
|
68
68
|
},
|
|
69
69
|
"build": {
|
|
70
70
|
"bundledDist": false,
|
|
71
|
-
"openclawVersion": "2026.
|
|
71
|
+
"openclawVersion": "2026.10.1-beta.1"
|
|
72
72
|
},
|
|
73
73
|
"release": {
|
|
74
74
|
"publishToClawHub": true,
|
|
@@ -14,7 +14,7 @@ From `https://example.feishu.cn/wiki/ABC123def`, use `ABC123def` as `token`. Tre
|
|
|
14
14
|
|
|
15
15
|
- Use `spaces` to enumerate accessible knowledge spaces and `nodes` for a space or parent node.
|
|
16
16
|
- Continue pagination with the returned `page_token` while `has_more` is true, keeping the same space and parent.
|
|
17
|
-
- Use `
|
|
17
|
+
- Search is unavailable. Use `nodes` to browse a known space or parent, or `get` when the user provides a wiki token.
|
|
18
18
|
- Use `get` to resolve a wiki token to its `node_token`, `obj_token`, and `obj_type`.
|
|
19
19
|
|
|
20
20
|
## Create and organize
|