@openclaw/feishu 2026.9.8 → 2026.10.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -29,25 +29,15 @@ The equivalent environment variables are `FS_SAFE_NATIVE_MODE` and `OPENCLAW_FS_
|
|
|
29
29
|
| `off` | Do not load a native package. Use supported fallbacks and reject native-only operations deterministically. |
|
|
30
30
|
| `require` | Throw `FsSafeError("helper-unavailable")` instead of falling back when an operation needs the native binding and it cannot load. |
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Windows security operations can use the package's readable PowerShell/C# scripts
|
|
43
|
-
in `auto` and `off`, subject to the [Windows security fallback prerequisites](install.md#windows-security-fallback).
|
|
44
|
-
|
|
45
|
-
On Bun macOS/Linux, the [runtime path adapter](install.md#bun-runtime) uses the
|
|
46
|
-
same Rust addon for system canonicalization in `auto` and `require`. No JIT is
|
|
47
|
-
needed. With `off` or a missing addon in `auto`, Bun's own resolver retains its
|
|
48
|
-
path and permission limitations. Canonicalization in `require` fails with
|
|
49
|
-
`helper-unavailable` if the addon or its canonicalizer is missing, including
|
|
50
|
-
when admitting a temp workspace. Containment and identity checks stay intact.
|
|
32
|
+
See [Archive extraction](archive.md) for native, bundled WASM, and ZIP backends.
|
|
33
|
+
Native archive-operation failures are terminal; `auto` does not retry them through a
|
|
34
|
+
fallback. `require` rejects missing bindings or required capabilities.
|
|
35
|
+
|
|
36
|
+
Windows owner/DACL inspection, private-directory creation, and secure reads can
|
|
37
|
+
use [PowerShell fallbacks](install.md#windows-security-fallback) in `auto` and
|
|
38
|
+
`off`; `require` stays strict, and native operation failures remain terminal.
|
|
39
|
+
Bun macOS/Linux canonicalization uses the addon under its
|
|
40
|
+
[runtime requirements](install.md#bun-runtime).
|
|
51
41
|
|
|
52
42
|
Configure the mode once during startup. Loading is lazy and cached; changing from `auto` to `require` after a failed load changes failure policy but does not repeatedly probe the binary.
|
|
53
43
|
|
|
@@ -64,22 +54,12 @@ with Node's automatic worker-exit cleanup; `Worker.terminate()` can leave them
|
|
|
64
54
|
open until process exit. The native close operation handles explicit cleanup,
|
|
65
55
|
not forced worker termination.
|
|
66
56
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`RESOLVE_NO_XDEV`, at runtime. An unavailable or denied probe selects compatible
|
|
74
|
-
JavaScript cleanup even in global `require` mode; `require-bounded` rejects before
|
|
75
|
-
child creation.
|
|
76
|
-
Already-created strict workspaces retain their binding
|
|
77
|
-
and descriptors across later mode changes.
|
|
78
|
-
|
|
79
|
-
[`stageFileInDirectory()`](staged-file.md) always requires native support on
|
|
80
|
-
Linux/macOS and rejects before creation when off, unavailable, or missing the
|
|
81
|
-
required capability. Windows is unsupported for this lifecycle. This does not
|
|
82
|
-
change the mode policy of existing fallback-capable APIs.
|
|
57
|
+
Temp workspaces have an independent `cleanupSafety` policy: `"compatible"`
|
|
58
|
+
can use guarded JavaScript cleanup even in native `require` mode;
|
|
59
|
+
`"require-bounded"` rejects before child creation without the required cleanup
|
|
60
|
+
capabilities. See the [temp workspace contract](temp.md#private-temp-workspaces).
|
|
61
|
+
[Retained-directory staging](staged-file.md) requires native support on Linux/macOS
|
|
62
|
+
and is unsupported on Windows.
|
|
83
63
|
|
|
84
64
|
## Native boundary
|
|
85
65
|
|
|
@@ -87,15 +67,9 @@ The internal Darwin descriptor ACL inspector requires its matching native
|
|
|
87
67
|
capability in both `auto` and `require`; `off`, a missing package, or an older
|
|
88
68
|
binding without `inspectDarwinAcl` rejects with `helper-unavailable`. Inspection
|
|
89
69
|
failure or malformed facts reject with `permission-unverified`; there is no
|
|
90
|
-
mode-bit or pathname fallback for this capability.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
state. The payload ACL-clear readback is part of that fused observation. Once
|
|
94
|
-
a clone payload exists, normalization and verification failures become terminal
|
|
95
|
-
`EIO` errors (with the underlying status and detail retained), not capability
|
|
96
|
-
signals that permit an ordinary-copy retry. Checked cleanup cannot undo that
|
|
97
|
-
terminal classification.
|
|
98
|
-
This addition does not change other APIs' native-mode or permission contracts.
|
|
70
|
+
mode-bit or pathname fallback for this capability. See
|
|
71
|
+
[Darwin clone normalization](native.md#the-beneath-model) for descriptor-bound
|
|
72
|
+
admission and terminal errors after a clone payload exists.
|
|
99
73
|
|
|
100
74
|
The native layer exposes policy-free filesystem mechanisms: beneath-root
|
|
101
75
|
open/mkdir/link, replace and no-replace rename, identity reads, archive decode/execution,
|
|
@@ -103,9 +77,9 @@ clone/copy/hash workers, POSIX canonicalization, and Windows security descriptor
|
|
|
103
77
|
layer owns policy, retries, filters, budgets, modes, cleanup, error
|
|
104
78
|
normalization, and the decision to fall back.
|
|
105
79
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
80
|
+
Platform mechanisms and containment limits are documented in the
|
|
81
|
+
[security model](security-model.md#containment-guarantees-by-platform) and
|
|
82
|
+
[native architecture](native.md#the-beneath-model).
|
|
109
83
|
|
|
110
84
|
Linux root lookups reject negative descriptor sentinels before borrowing a handle or resolving a relative path; they never substitute the process working directory for an admitted root. Public Root operations already supply retained, admitted handles.
|
|
111
85
|
|
|
@@ -120,58 +94,23 @@ through the retained parents without another receipt, duplicate, reopen, or
|
|
|
120
94
|
macOS `F_GETPATH`; the documented final source-name substitution window remains
|
|
121
95
|
there. Deeper names retain guarded parent traversal.
|
|
122
96
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
binding or capability can use a packaged PowerShell script that inspects the
|
|
128
|
-
borrowed handle. Raw owner/DACL inspection and private-directory creation also support
|
|
129
|
-
this fallback, subject to the [Windows security fallback prerequisites](install.md#windows-security-fallback).
|
|
130
|
-
Each capability emits a path-free warning once per process and adds PowerShell
|
|
131
|
-
startup and compilation overhead per call. Native `require` rejects missing
|
|
132
|
-
capabilities, and native operation failures remain terminal. No-clobber moves fail with
|
|
133
|
-
`helper-unavailable` when descriptor-relative parent admission or the atomic
|
|
134
|
-
no-replace rename is unavailable; they never use a check followed by a replacing
|
|
135
|
-
rename. Equivalent JavaScript paths remain available for documented
|
|
136
|
-
fallback-capable features. See [Native architecture](native.md#javascript-fallback-guarantees-and-delta)
|
|
137
|
-
for the exact difference.
|
|
138
|
-
|
|
139
|
-
The guarded JavaScript mutation path is detection-based, not containment-atomic.
|
|
140
|
-
If a same-privilege peer can replace a writable parent after its identity guard
|
|
141
|
-
but before Node resolves a pathname mutation, the mutation can land outside the
|
|
142
|
-
intended root before the post-operation guard throws. Native `require` ensures the addon is present, but does not require a
|
|
143
|
-
`kernel-atomic` resolver; inspect containment and use OS isolation when that
|
|
144
|
-
concurrent attacker is part of the threat model.
|
|
145
|
-
|
|
146
|
-
`openBeneath()` returns `{ fd, containment }`. `containment` is
|
|
147
|
-
`"kernel-atomic"` for Linux `openat2` and `"best-effort"` for the Linux fallback, macOS and
|
|
148
|
-
Windows. Public JavaScript root open/read/writable results also expose the
|
|
149
|
-
field and report `"best-effort"`; the label reports mechanism, not policy.
|
|
97
|
+
No-clobber `Root.move()` fails with `helper-unavailable` if descriptor-relative
|
|
98
|
+
parent admission or atomic no-replace rename is unavailable; it never substitutes
|
|
99
|
+
a check followed by a replacing rename. See the
|
|
100
|
+
[fallback contract](native.md#javascript-fallback-guarantees-and-delta).
|
|
150
101
|
|
|
151
|
-
|
|
102
|
+
Guarded JavaScript mutations can have out-of-root effects before a post-check
|
|
103
|
+
detects a hostile parent swap. Native `require` does not require a kernel-atomic
|
|
104
|
+
resolver: inspect the operation's `containment` and use OS isolation for hostile
|
|
105
|
+
concurrent actors. The [security model](security-model.md#native-root-mutation-capabilities)
|
|
106
|
+
is authoritative for operation and platform guarantees.
|
|
152
107
|
|
|
153
|
-
|
|
154
|
-
contract is unchanged, so migrate startup configuration directly:
|
|
108
|
+
## Migration from the Python helper
|
|
155
109
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
| `configureFsSafePython({ mode: "require" })` | `configureFsSafeNative({ mode: "require" })` |
|
|
161
|
-
| `FS_SAFE_PYTHON_MODE` | `FS_SAFE_NATIVE_MODE` |
|
|
162
|
-
| `OPENCLAW_FS_SAFE_PYTHON_MODE` | `OPENCLAW_FS_SAFE_NATIVE_MODE` |
|
|
163
|
-
| `pythonPath`, `FS_SAFE_PYTHON`, and the OpenClaw interpreter-path aliases | Remove; prebuilt bindings do not use an interpreter path |
|
|
164
|
-
|
|
165
|
-
In 0.5, `configureFsSafePython` and the legacy Python environment names
|
|
166
|
-
remain only as an upgrade bridge. On the first config read they emit one
|
|
167
|
-
`DeprecationWarning` with code `FS_SAFE_PYTHON_DEPRECATED`, state the mapped
|
|
168
|
-
native mode, and then apply that mode. A legacy interpreter path without an
|
|
169
|
-
explicit mode maps to `auto` and the path itself is ignored. Native config has
|
|
170
|
-
the normal precedence over legacy environment config.
|
|
171
|
-
|
|
172
|
-
There is no silent alias and no Python execution fallback. The bridge exists
|
|
173
|
-
only to make shipped 0.4 configuration visible and predictable while the
|
|
174
|
-
consumer performs its 0.5 upgrade.
|
|
110
|
+
The deprecated configuration bridge maps Python settings to native modes and
|
|
111
|
+
warns once; it never executes Python. Follow the
|
|
112
|
+
[0.5 migration checklist](migrating-to-0.5.md#2-replace-python-helper-configuration)
|
|
113
|
+
for aliases, warning behavior, and precedence.
|
|
175
114
|
|
|
176
115
|
## Related pages
|
|
177
116
|
|
|
@@ -17,7 +17,7 @@ guarded JavaScript path. Native loading is lazy; installs do not compile Rust,
|
|
|
17
17
|
run postinstall code, or fetch binaries at runtime. Seven exact-version optional
|
|
18
18
|
packages are filtered by OS, CPU, and Linux libc, so an installation receives
|
|
19
19
|
only its matching prebuilt binding.
|
|
20
|
-
Native-only
|
|
20
|
+
Native-only operations fail explicitly
|
|
21
21
|
instead of substituting a weaker implementation.
|
|
22
22
|
|
|
23
23
|
## The beneath model
|
|
@@ -235,11 +235,8 @@ not bypass the byte limit.
|
|
|
235
235
|
|
|
236
236
|
## Mode semantics
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
| `auto` | Try once, cache the result | Use guarded JavaScript when safe; reject native-only operations |
|
|
241
|
-
| `require` | Try once, cache the result | Throw `FsSafeError("helper-unavailable")` |
|
|
242
|
-
| `off` | Never attempt a binding load | Use guarded JavaScript when safe; reject native-only operations |
|
|
238
|
+
See [Native helper policy](native-helper.md#modes) for the `auto`, `require`, and
|
|
239
|
+
`off` contracts and cached loader behavior.
|
|
243
240
|
|
|
244
241
|
`sha256FileSync()` is a synchronous Node implementation in all three modes and
|
|
245
242
|
does not load the binding. Use asynchronous `sha256File()` for native hashing
|
|
@@ -251,19 +248,10 @@ Features without a safe fallback, including no-clobber
|
|
|
251
248
|
when native support is absent or off. Staging is currently Linux/macOS only and
|
|
252
249
|
rejects Windows with `unsupported-platform`.
|
|
253
250
|
|
|
254
|
-
Windows
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
paths remain data, with no runtime-generated helper script or encoded launcher.
|
|
259
|
-
The [Windows security fallback prerequisites](install.md#windows-security-fallback)
|
|
260
|
-
apply, and unsupported or disallowed command execution fails closed. This route
|
|
261
|
-
preserves raw ACL facts, private DACLs at creation, and descriptor-bound secure
|
|
262
|
-
reads, and emits a path-free `FS_SAFE_NATIVE_FALLBACK` warning once per capability
|
|
263
|
-
per process. Each call adds PowerShell startup and compilation overhead.
|
|
264
|
-
`require` rejects missing capabilities without a command, and an available native operation's
|
|
265
|
-
failure never triggers this fallback. See [Permissions](permissions.md) and
|
|
266
|
-
[Secure file reads](secure-file.md) for error and platform contracts.
|
|
251
|
+
Windows owner/DACL inspection, private-directory creation, and secure-file
|
|
252
|
+
inspection support [PowerShell fallbacks](install.md#windows-security-fallback)
|
|
253
|
+
in `auto` and `off`. `require` rejects missing capabilities, and native operation
|
|
254
|
+
failures are terminal.
|
|
267
255
|
|
|
268
256
|
The staged-file owner also serves POSIX native pinned writes, including streaming.
|
|
269
257
|
Unpublished files remain at `0600`; requested modes are applied through the
|
|
@@ -304,7 +292,12 @@ denied-path decisions remain in TypeScript.
|
|
|
304
292
|
Public policy does not change with the selected mechanism: traversal and link
|
|
305
293
|
rejection, archive filters/limits/modes, exclusive target creation, source and
|
|
306
294
|
target identity fencing, publication cleanup receipts, and secret/lock policy
|
|
307
|
-
remain TypeScript-owned.
|
|
295
|
+
remain TypeScript-owned. Windows buffered replacement writes with an omitted
|
|
296
|
+
`mutationSymlinks` policy retain the existing
|
|
297
|
+
[final-link and parent-junction differences](writing.md#windows-link-modes)
|
|
298
|
+
between native and legacy JavaScript paths; use explicit `"reject"` for uniform
|
|
299
|
+
link rejection. Apart from that legacy compatibility exception, what changes
|
|
300
|
+
is the syscall strength or availability:
|
|
308
301
|
|
|
309
302
|
The table compares underlying mechanisms. On Node, public `Root.open()`,
|
|
310
303
|
`Root.read()`, and `Root.openWritable()` use guarded Node file opens and report
|
|
@@ -328,6 +321,15 @@ infer native loading from timing.
|
|
|
328
321
|
|
|
329
322
|
## Loader security
|
|
330
323
|
|
|
324
|
+
Native initialization runs in the async context captured when fs-safe's loader
|
|
325
|
+
module is evaluated. Import fs-safe during application startup, outside request
|
|
326
|
+
or lease scopes, so the addon's process-lifetime housekeeping cannot retain the
|
|
327
|
+
first operation's `AsyncLocalStorage` stores. Importing still does not load the
|
|
328
|
+
addon: only the first native operation pays the registration cost. Subsequent
|
|
329
|
+
operations and their callbacks retain their own caller context. Dynamically
|
|
330
|
+
importing fs-safe for the first time inside a request scope captures that import
|
|
331
|
+
scope instead; this boundary does not clear an already active import context.
|
|
332
|
+
|
|
331
333
|
Importing fs-safe never executes a child process. Linux libc selection uses
|
|
332
334
|
the Node process report, then the ELF `PT_INTERP` field of `process.execPath`,
|
|
333
335
|
then conventional musl library filenames. An installed compatibility loader
|
|
@@ -36,7 +36,7 @@ isPathInside("/srv/uploads", "/srv/uploads-other/x"); // false
|
|
|
36
36
|
isPathInside("/srv/uploads", "/srv/uploads"); // true (root itself counts)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
The check is platform-aware: on Windows, paths are normalized for case and separator before comparison. This is deliberately a string-only, lexical answer; case folding does not prove that two differently cased prefixes name the same directory on a case-sensitive Windows directory. Root operations perform their own filesystem-identity admission when containment depends on case folding.
|
|
39
|
+
The check is platform-aware: on Windows, paths are normalized for case and separator before comparison. This is deliberately a string-only, lexical answer; case folding does not prove that two differently cased prefixes name the same directory on a case-sensitive Windows directory. Root operations perform their own filesystem-identity admission when containment depends on case folding. A target on a different UNC share or device namespace than `rootDir` is never inside it: hosts and shares compare with ASCII-only case folding, and namespace spellings whose share or device cannot be identified count as outside unless spelled exactly under `rootDir` (see the [security model](security-model.md)).
|
|
40
40
|
It is a lexical comparison, not a filesystem-admission boundary: it does not
|
|
41
41
|
reject Windows alternate data streams or index-allocation aliases. Use a
|
|
42
42
|
filesystem operation such as `root()` when a caller-controlled path will be
|
|
@@ -85,8 +85,7 @@ Structured ACLs containing only canonical SIDs are classified directly from
|
|
|
85
85
|
the current-user SID without requiring a separate account-name lookup.
|
|
86
86
|
The advanced options retain `currentUserSid` as an explicit classification
|
|
87
87
|
override and `principalTranslationFailed: true` as an immediate unverified
|
|
88
|
-
result. The
|
|
89
|
-
is no longer needed because the query returns SIDs directly.
|
|
88
|
+
result. The query returns SIDs directly; no translation cache is needed.
|
|
90
89
|
Injected executors must return the same structured success JSON as the built-in
|
|
91
90
|
query: valid `ownerSid` and `currentUserSid` strings, an explicit boolean
|
|
92
91
|
`remote`, and complete DACL facts (`complete`, `daclPresent`, and `aces`).
|
|
@@ -24,8 +24,7 @@ The handle resolver verifies exact descriptor and pathname identities, with one
|
|
|
24
24
|
bounded retry for unknown Windows observations. It borrows the handle without
|
|
25
25
|
reading, reopening, closing it, or changing its cursor.
|
|
26
26
|
|
|
27
|
-
The error helpers are `categorizeFsSafeError` and `FsSafeErrorDetails`.
|
|
28
|
-
deprecated native-configuration bridge retains the `FsSafePythonConfig` type.
|
|
27
|
+
The error helpers are `categorizeFsSafeError` and `FsSafeErrorDetails`.
|
|
29
28
|
|
|
30
29
|
## `path` and `advanced`
|
|
31
30
|
|
|
@@ -99,14 +99,13 @@ try {
|
|
|
99
99
|
type RootReadOptions = {
|
|
100
100
|
hardlinks?: "reject" | "allow"; // override defaults.hardlinks
|
|
101
101
|
maxBytes?: number; // refuse reads larger than this many bytes
|
|
102
|
-
nonBlockingRead?: boolean; // compatibility hint; safe opens are already nonblocking where supported
|
|
103
102
|
symlinks?: "reject" | "follow-within-root" | "follow-parents-within-root"; // override defaults.symlinks
|
|
104
103
|
};
|
|
105
104
|
```
|
|
106
105
|
|
|
107
106
|
`maxBytes` is enforced eagerly: the library reads up to `maxBytes + 1` and throws `too-large` if there is more, so a hostile target cannot silently exhaust memory. Values must be non-negative safe integers or positive `Infinity`; zero is an active cap, while `Infinity` disables it. Explicitly forwarding `undefined` preserves the Root default.
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
Safe reads always add the platform's nonblocking open flag where available so a raced FIFO cannot pin a worker indefinitely; regular-file descriptor reads retain normal Node behavior.
|
|
110
109
|
|
|
111
110
|
## `readAbsolute()` and `reader()`
|
|
112
111
|
|
|
@@ -25,7 +25,6 @@ type RootDefaults = {
|
|
|
25
25
|
maxBytes?: number; // refuse reads larger than this many bytes; defaults to 16 MiB
|
|
26
26
|
mkdir?: boolean; // create missing parent dirs on write/openWritable/append; default true
|
|
27
27
|
mode?: number; // requested file mode; per-call override available
|
|
28
|
-
nonBlockingRead?: boolean; // compatibility hint; safe opens are already nonblocking where supported
|
|
29
28
|
renameIdentity?: "strict" | "verify-content-with-lock"; // default "strict"
|
|
30
29
|
symlinks?: "reject" | "follow-within-root" | "follow-parents-within-root"; // read policy
|
|
31
30
|
mutationSymlinks?: "reject" | "follow-parents-within-root"; // opt-in mutation policy
|
|
@@ -66,39 +65,11 @@ fs.reader(options?) // (path) => Promise<Buffer>; useful for loader A
|
|
|
66
65
|
fs.walk(rel, options) // root-bounded AsyncIterable<{ relativePath, kind, size }>
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
`walk()` is the
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
not its target. With an entry budget, sorted walks prepare small metadata
|
|
75
|
-
batches within the remaining budget; unbounded sorted walks reuse the full
|
|
76
|
-
directory snapshot.
|
|
77
|
-
The default `order: "sorted"` enumerates and sorts each directory's names;
|
|
78
|
-
`order: "filesystem"` streams names in filesystem order for bounded work in
|
|
79
|
-
wide directories. Budget exhaustion yields a `"truncated"` marker by
|
|
80
|
-
default or throws `FsSafeError("too-large")` with `limitBehavior: "throw"`.
|
|
81
|
-
Use `entryFilter(entry)` to return `"include"`, `"skip"`, or
|
|
82
|
-
`"skip-subtree"`, directly or through a Promise. `"skip"` omits the current
|
|
83
|
-
entry but still descends into a directory; `"skip-subtree"` omits a directory
|
|
84
|
-
and all of its descendants.
|
|
85
|
-
Filters run serially outside metadata batches, with the options object as their
|
|
86
|
-
`this` receiver. After an awaited filter resolves, the walk checks cancellation
|
|
87
|
-
and revalidates the current listing directory and Root identities before using
|
|
88
|
-
the decision. Captured entry metadata retains its snapshot semantics.
|
|
89
|
-
|
|
90
|
-
Cancellation and iterator disposal wait for a pending filter to settle; they do
|
|
91
|
-
not race the callback or close its directory while it is running. Callback
|
|
92
|
-
throws and promise rejections reject the walk through normal cleanup.
|
|
93
|
-
Directory reads remain fail-fast by default. With
|
|
94
|
-
`onDirectoryError: "skip-and-report"`, the iterator instead yields
|
|
95
|
-
`{ relativePath, kind: "directory-error", size: 0, error }` and continues with
|
|
96
|
-
the remaining tree. That policy also covers identity-check failures after an
|
|
97
|
-
awaited filter, while callback failures always reject.
|
|
98
|
-
In include mode, a directory that becomes a symlink before descent is a
|
|
99
|
-
`path-mismatch` directory error; it is never silently omitted or followed.
|
|
100
|
-
See [Directory walking](walk.md) for the pure-Node guarantees and the contrast
|
|
101
|
-
with the standalone best-effort walkers.
|
|
68
|
+
`walk()` is the root-bounded recursive iterator, with budgets, cancellation,
|
|
69
|
+
and symlink/filter policies. Default `order: "sorted"` enumerates and sorts all
|
|
70
|
+
names in each directory even with an entry budget; use `order: "filesystem"`
|
|
71
|
+
for bounded memory in wide directories. See [Root-bounded walking](walk.md#root-bounded-async-iteration)
|
|
72
|
+
for truncation, callback, and directory-error contracts.
|
|
102
73
|
|
|
103
74
|
`open()` returns a Node `FileHandle` for streaming. Prefer `await using` for cleanup:
|
|
104
75
|
|
|
@@ -143,136 +114,22 @@ fs.mkdir(rel, options?) // mkdir -p (creates missing parents)
|
|
|
143
114
|
fs.ensureRoot(options?) // accepts "" / "." as the root itself
|
|
144
115
|
```
|
|
145
116
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
creation rejects relevant inheritable parent ACLs, while noninheriting parent
|
|
153
|
-
ACLs remain allowed. A native helper with `inspectDarwinAcl` is required. Native
|
|
154
|
-
`off`, a missing helper, or an older helper without that capability rejects with
|
|
155
|
-
`helper-unavailable` before creating parents or stages. See [creation](creation.md)
|
|
156
|
-
for platform support, synchronous leaf creation, and failure handling.
|
|
117
|
+
Mutation options control parent creation, modes, durability, and publication.
|
|
118
|
+
`write`, `create`, `append`, `writeJson`, `createJson`, and `copyIn` inherit
|
|
119
|
+
`durable` from Root defaults (normally `true`); `false` skips synchronization
|
|
120
|
+
without changing publication or identity checks. See [write options](writing.md#write-options)
|
|
121
|
+
for precedence and platform behavior, and [append](writing.md#write-verbs)
|
|
122
|
+
for newline handling and creation modes.
|
|
157
123
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`write`, `create`, `append`, `writeJson`, and `createJson` accept `mode?: number`; use `0o600` for credentials and other private state. `writeJson` also accepts the same options as `JSON.stringify` plus `trailingNewline?: boolean` (defaults `true` so the file ends in `\n`).
|
|
164
|
-
|
|
165
|
-
For `append` and `openWritable`, `mode` only affects new-file creation: POSIX
|
|
166
|
-
permissions remain subject to the process umask. These methods do not chmod
|
|
167
|
-
existing files. Replacement and create-only writes apply their final mode
|
|
168
|
-
through the retained descriptor; see [Writing](writing.md#write-options).
|
|
169
|
-
|
|
170
|
-
Buffered `create` and `createJson` also accept `atomic?: boolean`. With `true`,
|
|
171
|
-
complete content is staged before exclusive publication even in native-off mode;
|
|
172
|
-
the fallback requires hardlinks. Omitted or `false` keeps the existing buffered
|
|
173
|
-
publication behavior. Streamed creates always stage complete content. The flag
|
|
174
|
-
does not change `durable` or promise stronger containment or crash durability.
|
|
175
|
-
See [atomic creation and settlement](writing.md#atomic-buffered-creation).
|
|
176
|
-
|
|
177
|
-
`create` also accepts `AsyncIterable<Uint8Array>` with `RootCreateStreamOptions`:
|
|
178
|
-
the same path, authority, mode, and durability options, plus `maxBytes` and
|
|
179
|
-
`signal`, without `encoding` or `renameIdentity`. It consumes one chunk at a
|
|
180
|
-
time and publishes the completed file exclusively. The byte cap inherits an
|
|
181
|
-
explicit `Root.defaults.maxBytes`; without either cap, consumption is unlimited.
|
|
182
|
-
See [streamed creation](writing.md#streamed-creation) for cancellation,
|
|
183
|
-
cleanup, and filesystem requirements.
|
|
184
|
-
|
|
185
|
-
`append` accepts `prependNewlineIfNeeded: true` to separate text from existing
|
|
186
|
-
content when neither side supplies a newline. String data uses its `encoding`
|
|
187
|
-
for the newline check, including UTF-16LE; Buffer data uses a single LF byte.
|
|
188
|
-
Empty strings and Buffers add no separator; an empty append still creates a
|
|
189
|
-
missing file.
|
|
190
|
-
|
|
191
|
-
These five methods and `copyIn` also accept `durable?: boolean`: the per-call value overrides
|
|
192
|
-
`Root.defaults.durable`, which defaults to `true` when omitted. An explicitly
|
|
193
|
-
`undefined` per-call value preserves the root default. `durable: false` keeps
|
|
194
|
-
the existing publication behavior, modes, and identity checks but skips file
|
|
195
|
-
and parent-directory fsync calls. Use it only for reconstructible data: a crash
|
|
196
|
-
may lose the write or leave the previous file. See [Writing](writing.md#write-options)
|
|
197
|
-
for platform details.
|
|
198
|
-
|
|
199
|
-
`create` and `createJson` additionally accept `durable: "file"` to require file
|
|
200
|
-
synchronization, including propagating `EPERM`. Parent-directory synchronization
|
|
201
|
-
retains its existing best-effort behavior. This option applies to buffered and
|
|
202
|
-
streamed creation and does not select a publication strategy.
|
|
203
|
-
|
|
204
|
-
`copyIn` accepts a `RootCopySource`: a trusted absolute source path or a file
|
|
205
|
-
within another Root. The guarded form supplies `root` with only its `open` and
|
|
206
|
-
`stat` read capabilities, plus `relativePath`:
|
|
124
|
+
`mkdir`, `ensureRoot`, `create`, and `createJson` accept `private: true`; see
|
|
125
|
+
[Creation](creation.md) for permission checks and native requirements.
|
|
126
|
+
Buffered `create` and `createJson` support [atomic publication](writing.md#atomic-buffered-creation)
|
|
127
|
+
and `durable: "file"`; `create` also supports [streamed input](writing.md#streamed-creation).
|
|
207
128
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
root: source,
|
|
213
|
-
relativePath: "config/settings.json",
|
|
214
|
-
}, {
|
|
215
|
-
overwrite: false,
|
|
216
|
-
clone: "auto",
|
|
217
|
-
mode: 0o600,
|
|
218
|
-
signal: AbortSignal.timeout(30_000),
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
The source Root applies its read policies, including confinement and symlink
|
|
223
|
-
handling. `sourceHardlinks` overrides its hardlink policy only when supplied;
|
|
224
|
-
otherwise the source Root default is retained. The admitted source
|
|
225
|
-
descriptor stays open through copying and source-identity verification; copying
|
|
226
|
-
does not consume its current file position. Both forms enforce `maxBytes` while
|
|
227
|
-
reading, including when a file grows after admission, and use bounded buffers.
|
|
228
|
-
Copies have independent file data; changing either file cannot change the other.
|
|
229
|
-
Set `preserveSourceMode: true` to select the mode from the admitted source
|
|
230
|
-
descriptor. An explicit numeric `mode`, including `Root.defaults.mode`, takes
|
|
231
|
-
precedence. By default, copying retains the existing destination-mode rules.
|
|
232
|
-
The operation verifies source identity, not a coherent snapshot of concurrent
|
|
233
|
-
in-place edits. Keep the source unchanged when snapshot consistency is required.
|
|
234
|
-
|
|
235
|
-
`overwrite` defaults to `true`, preserving the existing replacement behavior.
|
|
236
|
-
With `overwrite: false`, an existing destination produces `already-exists` and
|
|
237
|
-
is never altered. Copying prepares a private sibling file before publishing its
|
|
238
|
-
completed contents. Native mode uses no-replace rename. The guarded JavaScript
|
|
239
|
-
fallback links the completed stage and removes its temporary name in the same
|
|
240
|
-
JavaScript turn; the filesystem must support hardlinks. Other processes can
|
|
241
|
-
briefly observe both names. The source is never hardlinked to the destination.
|
|
242
|
-
|
|
243
|
-
`clone` chooses the file-data transfer strategy through `CopyCloneMode`, shared
|
|
244
|
-
with [`copyTree`](copy.md#api). File copies default to `"never"`; tree copies
|
|
245
|
-
default to `"auto"`:
|
|
246
|
-
|
|
247
|
-
| Value | Behavior |
|
|
248
|
-
| --- | --- |
|
|
249
|
-
| `never` | Copy regular file bytes using reads and writes, without explicit cloning or copy offload. |
|
|
250
|
-
| `auto` | Try native file cloning, then copy offload or ordinary byte copying when cloning is unavailable. |
|
|
251
|
-
| `always` | Require native cloning; fail when the binding or filesystem cannot provide it. |
|
|
252
|
-
|
|
253
|
-
Native file cloning supports APFS and supported Linux filesystems. Windows
|
|
254
|
-
currently uses byte copying for `never` and `auto`; `always` fails. Clone choice
|
|
255
|
-
does not change modes, durability, root confinement, or source and publication
|
|
256
|
-
identity checks. The shared strategy does not replace Root's guarded regular-file
|
|
257
|
-
contract with `copyTree`'s caller-owned immutable-tree and metadata contract.
|
|
258
|
-
|
|
259
|
-
An already aborted `signal` prevents I/O. Cancellation during copying waits for
|
|
260
|
-
admitted reads and native work to settle, then cleans only the owned unpublished
|
|
261
|
-
stage. The final authority check runs before publication. Once publication has
|
|
262
|
-
occurred, later cancellation or verification failure preserves the destination.
|
|
263
|
-
The synchronous optional `onDestinationPublished` callback receives a frozen
|
|
264
|
-
`RootCopyPublicationReceipt` containing `{ path, dev, ino }`, with exact bigint identity immediately after
|
|
265
|
-
publication, before later checks can fail. Callback errors also preserve the
|
|
266
|
-
published file. Promise, thenable, and synchronous or asynchronous generator
|
|
267
|
-
results reject with `TypeError`; returned generators are never advanced. Other
|
|
268
|
-
synchronous return values are ignored. This receipt records an outcome; it does
|
|
269
|
-
not authorize removing a file that another actor may have edited. Application recovery and cooperative
|
|
270
|
-
locking remain caller-owned.
|
|
271
|
-
|
|
272
|
-
Existing `copyIn` callers must account for completed destinations retained after
|
|
273
|
-
a post-publication source-verification failure, even without the new options.
|
|
274
|
-
Recovery must inspect current destination state rather than assume a rejected
|
|
275
|
-
copy left no file.
|
|
129
|
+
`copyIn` accepts a trusted absolute path or another Root as its source, with
|
|
130
|
+
byte limits, cloning, cancellation, and publication receipts. See the complete
|
|
131
|
+
[copy contract](writing.md#write-verbs); a rejected copy
|
|
132
|
+
can still leave a completed destination after publication.
|
|
276
133
|
|
|
277
134
|
Root operations that choose a new destination reject a leading Windows
|
|
278
135
|
drive-relative spelling such as `C:name` on every platform. This applies to
|
|
@@ -294,17 +151,11 @@ does not otherwise reject them.
|
|
|
294
151
|
|
|
295
152
|
`openWritable` opens a writable file with options `mode?: number` and `writeMode?: "replace" | "append" | "update"`. `replace` truncates existing files and is the default; `update` keeps existing contents. Before truncation or handle return, descriptor and pathname identities are compared with lossless bigint metadata; persistently unknown Windows identities fail closed. The returned `stat` remains an ordinary numeric Node `Stats` object. Use it for streaming output. Prefer `await using` for cleanup.
|
|
296
153
|
|
|
297
|
-
`remove` leaves non-empty directories unchanged unless `recursive: true
|
|
298
|
-
|
|
299
|
-
`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
follows discovered symlinks; an explicit `mutationSymlinks` policy rejects them,
|
|
303
|
-
while the omitted policy unlinks them. `force: true` ignores missing targets,
|
|
304
|
-
and `signal` stops further work after admitted I/O and resource cleanup settle.
|
|
305
|
-
Removal is not transactional: a budget, cancellation, policy, or identity
|
|
306
|
-
failure can leave a partially removed tree. See [removal](writing.md)
|
|
307
|
-
for the full counting and failure contract.
|
|
154
|
+
`remove` leaves non-empty directories unchanged unless `recursive: true`.
|
|
155
|
+
Recursive removal defaults to filesystem order, `maxEntries: 100_000`, and
|
|
156
|
+
`maxDepth: 64`. It is incremental: budget, cancellation, policy, or identity
|
|
157
|
+
failures can leave a partially removed tree. See [removal](writing.md#write-verbs)
|
|
158
|
+
for ordering, symlink, and failure semantics.
|
|
308
159
|
|
|
309
160
|
### Live mutation authority
|
|
310
161
|
|
|
@@ -540,17 +391,8 @@ const b = await load("/srv/workspace/state.bin"); // absolute, but inside the ro
|
|
|
540
391
|
|
|
541
392
|
### "Touch only if missing" seeding
|
|
542
393
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
await fs.create("config/seed.json", initialJson);
|
|
546
|
-
} catch (err) {
|
|
547
|
-
if (err instanceof FsSafeError && err.code === "already-exists") {
|
|
548
|
-
// existing config wins
|
|
549
|
-
} else {
|
|
550
|
-
throw err;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
```
|
|
394
|
+
Use `create()` and handle `already-exists` so existing configuration wins;
|
|
395
|
+
see the [seeding example](writing.md#write-verbs).
|
|
554
396
|
|
|
555
397
|
### Replace + verify
|
|
556
398
|
|
|
@@ -314,5 +314,5 @@ await withTimeout(
|
|
|
314
314
|
|
|
315
315
|
- [JSON files](json.md) — `writeJson` accepts `mode: 0o600` for non-secret JSON state.
|
|
316
316
|
- [Atomic writes](atomic.md) — the lower-level `replaceFileAtomic` used by these helpers.
|
|
317
|
-
- [Private file-store mode](
|
|
317
|
+
- [Private file-store mode](file-store.md#private-mode) — root-bounded JSON+text stores using secret-file write policy.
|
|
318
318
|
- [Migrating to 0.5](migrating-to-0.5.md) — strict/try reads and create-only adoption checklist.
|