@openclaw/openshell-sandbox 2026.10.6 → 2026.10.8
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/.source-module-urls/fcca51e67b787234d4169b8d7f34d9367d49b71edb1b23c6d9a9ede01de9ef51.json +1 -0
- package/node_modules/@openclaw/fs-safe/CHANGELOG.md +48 -0
- package/node_modules/@openclaw/fs-safe/README.md +14 -4
- package/node_modules/@openclaw/fs-safe/dist/archive-merge.js +28 -3
- package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
- package/node_modules/@openclaw/fs-safe/dist/archive-read.js +11 -5
- package/node_modules/@openclaw/fs-safe/dist/archive-zip-loader.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/clone-metadata.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/copy-link.d.ts +26 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-link.js +169 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-metadata.js +39 -0
- package/node_modules/@openclaw/fs-safe/dist/copy-root-file-sync.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +3 -6
- package/node_modules/@openclaw/fs-safe/dist/copy.js +3 -3
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.d.ts +12 -0
- package/node_modules/@openclaw/fs-safe/dist/darwin-acl.js +29 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.d.ts +3 -0
- package/node_modules/@openclaw/fs-safe/dist/directory-mode-node.js +27 -2
- package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/file-handle-transfer.js +4 -2
- package/node_modules/@openclaw/fs-safe/dist/file-hash.js +4 -3
- package/node_modules/@openclaw/fs-safe/dist/file-lock.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-lock.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/file-write-lease.js +42 -0
- package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/hardlink-fallback.js +5 -0
- package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-ownership.js +16 -3
- package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +57 -18
- package/node_modules/@openclaw/fs-safe/dist/native-directory-observation.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-move-fallback.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-operations.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write-windows.js +19 -1
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.d.ts +4 -1
- package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +18 -0
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/native.d.ts +5 -3
- package/node_modules/@openclaw/fs-safe/dist/native.js +25 -6
- package/node_modules/@openclaw/fs-safe/dist/opened-realpath.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/overwrite-file-handle.js +4 -1
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-public.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/permissions-windows.js +36 -47
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-input.js +2 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +3 -0
- package/node_modules/@openclaw/fs-safe/dist/pinned-write-types.d.ts +2 -1
- package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +5 -3
- package/node_modules/@openclaw/fs-safe/dist/pipe.d.ts +9 -0
- package/node_modules/@openclaw/fs-safe/dist/pipe.js +26 -0
- package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +2 -5
- package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +92 -10
- package/node_modules/@openclaw/fs-safe/dist/publish-file.d.ts +1 -1
- package/node_modules/@openclaw/fs-safe/dist/publish-file.js +12 -11
- package/node_modules/@openclaw/fs-safe/dist/root-copy.d.ts +18 -0
- package/node_modules/@openclaw/fs-safe/dist/root-copy.js +128 -0
- package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +12 -4
- package/node_modules/@openclaw/fs-safe/dist/root-impl.js +6 -92
- package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -0
- package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-retained.js +1 -1
- package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-target.js +8 -1
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.d.ts +1 -0
- package/node_modules/@openclaw/fs-safe/dist/test-hooks.js +1 -0
- package/node_modules/@openclaw/fs-safe/dist/watch-native.js +2 -2
- package/node_modules/@openclaw/fs-safe/dist/windows-security-bridge.cs +11 -5
- package/node_modules/@openclaw/fs-safe/dist/windows-security-command.js +9 -1
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.d.ts +16 -0
- package/node_modules/@openclaw/fs-safe/dist/windows-test-hooks.js +51 -0
- package/node_modules/@openclaw/fs-safe/docs/archive.md +16 -0
- package/node_modules/@openclaw/fs-safe/docs/contributing.md +35 -5
- package/node_modules/@openclaw/fs-safe/docs/copy.md +22 -0
- package/node_modules/@openclaw/fs-safe/docs/durability.md +4 -2
- package/node_modules/@openclaw/fs-safe/docs/errors.md +8 -0
- package/node_modules/@openclaw/fs-safe/docs/guest.md +2 -0
- package/node_modules/@openclaw/fs-safe/docs/install.md +13 -5
- package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +5 -5
- package/node_modules/@openclaw/fs-safe/docs/native-helper.md +15 -5
- package/node_modules/@openclaw/fs-safe/docs/native.md +51 -2
- package/node_modules/@openclaw/fs-safe/docs/pipe.md +81 -0
- package/node_modules/@openclaw/fs-safe/docs/platform-capabilities.md +63 -0
- package/node_modules/@openclaw/fs-safe/docs/quickstart.md +23 -3
- package/node_modules/@openclaw/fs-safe/docs/reading.md +7 -1
- package/node_modules/@openclaw/fs-safe/docs/secret-file.md +5 -5
- package/node_modules/@openclaw/fs-safe/docs/sidecar-lock.md +31 -13
- package/node_modules/@openclaw/fs-safe/docs/store.md +11 -0
- package/node_modules/@openclaw/fs-safe/docs/types.md +45 -80
- package/node_modules/@openclaw/fs-safe/docs/writing.md +121 -14
- package/node_modules/@openclaw/fs-safe/package.json +18 -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-freebsd-arm64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-arm64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-freebsd-x64/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-arm64-msvc/package.json +29 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
- package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
- package/package.json +5 -5
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Quickstart
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build a `root()` and use it to read, write, atomically replace, and unpack an
|
|
4
|
+
archive under a trusted workspace. Confinement strength and concurrent-mutation
|
|
5
|
+
limits depend on the operation and platform; see the [security model](security-model.md).
|
|
4
6
|
|
|
5
7
|
If you have used Go's `os.Root` / `OpenInRoot` or Rust's [`cap-std`](https://github.com/bytecodealliance/cap-std), this is the same shape: a capability-style handle that carries the boundary across every operation. The first thing to internalize is that you stop reasoning about *paths* and start reasoning about *the handle*.
|
|
6
8
|
|
|
@@ -31,7 +33,9 @@ await fs.write("notes/today.txt", "hello\n");
|
|
|
31
33
|
const text = await fs.readText("notes/today.txt");
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
Replacement writes use a sibling temp file plus `rename`, so a partial write
|
|
37
|
+
never appears at the destination. Reads open with `O_NOFOLLOW` where available
|
|
38
|
+
and verify the opened fd matches the path identity before returning the buffer.
|
|
35
39
|
|
|
36
40
|
`create()` is the don't-clobber variant of `write()` and throws `already-exists` when the target is already there:
|
|
37
41
|
|
|
@@ -39,6 +43,13 @@ Writes use a sibling temp file plus `rename`, so a partial write never appears a
|
|
|
39
43
|
await fs.create("notes/README.md", "seed\n"); // throws if it already exists
|
|
40
44
|
```
|
|
41
45
|
|
|
46
|
+
Default create-only writes can expose incomplete content on the JavaScript
|
|
47
|
+
fallback. Pass `{ atomic: true }` to stage complete content before publication.
|
|
48
|
+
When hardlinks are refused, staged publication can still expose an empty
|
|
49
|
+
destination placeholder before replacing it with the completed file. See
|
|
50
|
+
[atomic creation](writing.md#atomic-buffered-creation) and
|
|
51
|
+
[publication without hardlinks](writing.md#publication-without-hardlinks).
|
|
52
|
+
|
|
42
53
|
## 3. JSON, with parsing
|
|
43
54
|
|
|
44
55
|
```ts
|
|
@@ -60,7 +71,16 @@ await fs.move("notes/today.txt", "notes/archive/today.txt", { overwrite: true })
|
|
|
60
71
|
await fs.remove("notes/archive/today.txt");
|
|
61
72
|
```
|
|
62
73
|
|
|
63
|
-
`move()` defaults to no clobber
|
|
74
|
+
`move()` defaults to no clobber and requires native support. Linux `auto` can
|
|
75
|
+
fall back when no-replace rename is unsupported; its directory fallback can
|
|
76
|
+
replace a concurrently created empty directory. Use native `require` when
|
|
77
|
+
that must fail closed. See the [move contract](writing.md#fs-move-from-to-options).
|
|
78
|
+
Pass `{ overwrite: true }` when replacement is intended.
|
|
79
|
+
|
|
80
|
+
`remove()` removes files and empty directories by default. For a non-empty
|
|
81
|
+
directory, pass `{ recursive: true }`; use `maxEntries`, `maxDepth`, and `signal`
|
|
82
|
+
to bound the work. See [`root()`](root.md) for removal ordering, limits, and
|
|
83
|
+
partial-removal semantics.
|
|
64
84
|
|
|
65
85
|
## 5. Inspect
|
|
66
86
|
|
|
@@ -81,6 +81,12 @@ For tighter control over malformed-or-missing JSON, use the standalone helpers i
|
|
|
81
81
|
|
|
82
82
|
Returns a `FileHandle` plus `containment: "best-effort"`, the verified `realPath`, and `stat`. Use this for streaming or partial reads, and **always close the handle**:
|
|
83
83
|
|
|
84
|
+
`open()` accepts `RootOpenOptions`: the read symlink and hardlink policies,
|
|
85
|
+
without `maxBytes`. It does not consume bytes or apply the Root's read-size
|
|
86
|
+
budget to later handle or stream operations. Bound that I/O in the caller, or
|
|
87
|
+
use `read()` / `readBytes()` when the library should enforce the whole-file cap.
|
|
88
|
+
The result also supports `await using` for automatic handle cleanup.
|
|
89
|
+
|
|
84
90
|
```ts
|
|
85
91
|
const opened = await fs.open("large.log");
|
|
86
92
|
try {
|
|
@@ -103,7 +109,7 @@ type RootReadOptions = {
|
|
|
103
109
|
};
|
|
104
110
|
```
|
|
105
111
|
|
|
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.
|
|
112
|
+
For the byte-consuming read methods, `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.
|
|
107
113
|
|
|
108
114
|
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.
|
|
109
115
|
|
|
@@ -27,11 +27,11 @@ import {
|
|
|
27
27
|
|
|
28
28
|
## Constants
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
| Exported constant | Value |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `DEFAULT_SECRET_FILE_MAX_BYTES` | `16 * 1024` (16 KiB) |
|
|
33
|
+
| `PRIVATE_SECRET_DIR_MODE` | `0o700` |
|
|
34
|
+
| `PRIVATE_SECRET_FILE_MODE` | `0o600` |
|
|
35
35
|
|
|
36
36
|
The 16 KiB cap is intentionally aggressive — credentials should be small. If you need bigger, pass `maxBytes` explicitly.
|
|
37
37
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# File lock
|
|
2
2
|
|
|
3
|
+
For [Linux kernel write leases](platform-capabilities.md#linux-write-leases), `FileWriteLease.isHeld() === false` means “do not act on the file,” including while the kernel is still processing a lease break. The caller’s `SIGIO` handler still owns break notification.
|
|
4
|
+
|
|
3
5
|
`acquireFileLock()` and `withFileLock()` provide a cross-process file lock with retry and process-exit cleanup. The lock is implemented as a sidecar file (e.g. `state.json` ↔ `state.json.lock`) — only one acquirer can create the sidecar with `O_CREAT | O_EXCL` at a time.
|
|
4
6
|
|
|
5
7
|
On Windows, both the target and an explicitly supplied `lockPath` reject NTFS
|
|
@@ -72,21 +74,29 @@ The raw sidecar bytes are not a canonical JSON representation: tools that trim o
|
|
|
72
74
|
## API
|
|
73
75
|
|
|
74
76
|
```ts
|
|
75
|
-
|
|
77
|
+
import type {
|
|
78
|
+
FileLockAcquireOptions,
|
|
79
|
+
FileLockHandle,
|
|
80
|
+
FileLockManager,
|
|
81
|
+
FileLockSyncAcquireOptions,
|
|
82
|
+
FileLockSyncHandle,
|
|
83
|
+
} from "@openclaw/fs-safe/file-lock";
|
|
84
|
+
|
|
85
|
+
declare function acquireFileLock<TPayload extends Record<string, unknown>>(
|
|
76
86
|
targetPath: string,
|
|
77
87
|
options: FileLockAcquireOptions<TPayload>,
|
|
78
88
|
): Promise<FileLockHandle>;
|
|
79
89
|
|
|
80
|
-
function withFileLock<T, TPayload
|
|
90
|
+
declare function withFileLock<T, TPayload extends Record<string, unknown>>(
|
|
81
91
|
targetPath: string,
|
|
82
92
|
options: FileLockAcquireOptions<TPayload>,
|
|
83
93
|
fn: () => Promise<T>,
|
|
84
94
|
): Promise<T>;
|
|
85
95
|
|
|
86
|
-
function createFileLockManager(key: string): FileLockManager;
|
|
96
|
+
declare function createFileLockManager(key: string): FileLockManager;
|
|
87
97
|
|
|
88
|
-
function acquireFileLockSync<TPayload
|
|
89
|
-
function withFileLockSync<T, TPayload
|
|
98
|
+
declare function acquireFileLockSync<TPayload extends Record<string, unknown>>(targetPath: string, options: FileLockSyncAcquireOptions<TPayload>): FileLockSyncHandle;
|
|
99
|
+
declare function withFileLockSync<T, TPayload extends Record<string, unknown>>(targetPath: string, options: FileLockSyncAcquireOptions<TPayload>, fn: () => T): T;
|
|
90
100
|
```
|
|
91
101
|
|
|
92
102
|
`managerKey` is an optional identifier used to keep state isolated across multiple lock domains in the same process. Use distinct keys for distinct domains (`"snapshot"`, `"compact"`, `"build"`). If omitted, fs-safe derives one from the target path.
|
|
@@ -141,7 +151,7 @@ type FileLockAcquireOptions<TPayload extends Record<string, unknown>> = {
|
|
|
141
151
|
shouldReclaim?: (params: {
|
|
142
152
|
lockPath: string;
|
|
143
153
|
normalizedTargetPath: string;
|
|
144
|
-
payload:
|
|
154
|
+
payload: unknown;
|
|
145
155
|
staleMs: number;
|
|
146
156
|
nowMs: number;
|
|
147
157
|
heldByThisProcess: boolean;
|
|
@@ -150,7 +160,7 @@ type FileLockAcquireOptions<TPayload extends Record<string, unknown>> = {
|
|
|
150
160
|
lockPath: string;
|
|
151
161
|
normalizedTargetPath: string;
|
|
152
162
|
raw: string;
|
|
153
|
-
payload:
|
|
163
|
+
payload: unknown;
|
|
154
164
|
}) => boolean | Promise<boolean>;
|
|
155
165
|
metadata?: Record<string, unknown>; // attached to heldEntries() output for diagnostics
|
|
156
166
|
parsePayload?: (raw: string) => unknown;
|
|
@@ -542,24 +552,32 @@ holder still alive":
|
|
|
542
552
|
|
|
543
553
|
```ts
|
|
544
554
|
import { kill } from "node:process";
|
|
555
|
+
import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
|
|
545
556
|
|
|
546
557
|
const handle = await acquireFileLock(targetPath, {
|
|
547
558
|
staleMs: 60_000,
|
|
548
559
|
payload: () => ({ pid: process.pid }),
|
|
549
|
-
shouldReclaim: ({ payload
|
|
550
|
-
if (!payload) return true;
|
|
551
|
-
const pid =
|
|
552
|
-
if (!Number.
|
|
560
|
+
shouldReclaim: ({ payload }) => {
|
|
561
|
+
if (!payload || typeof payload !== "object" || !("pid" in payload)) return true;
|
|
562
|
+
const pid = payload.pid;
|
|
563
|
+
if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
553
564
|
try {
|
|
554
565
|
kill(pid, 0);
|
|
555
566
|
return false; // process still alive — keep waiting
|
|
556
|
-
} catch {
|
|
557
|
-
return
|
|
567
|
+
} catch (error) {
|
|
568
|
+
return error !== null && typeof error === "object" &&
|
|
569
|
+
"code" in error && error.code === "ESRCH"; // only a missing process is stale
|
|
558
570
|
}
|
|
559
571
|
},
|
|
560
572
|
});
|
|
561
573
|
```
|
|
562
574
|
|
|
575
|
+
The observed payload is `unknown`, even when this caller writes a typed payload.
|
|
576
|
+
Validate it before reading fields. Permission failures such as `EPERM` do not
|
|
577
|
+
prove that the holder exited. PID checks assume a shared local PID namespace;
|
|
578
|
+
PID reuse can keep a stale lock waiting. This callback classifies staleness,
|
|
579
|
+
not ownership or permission to remove another holder's lock.
|
|
580
|
+
|
|
563
581
|
`heldByThisProcess` is true when this manager already holds the lock. A `true` result marks the observed sidecar as stale; `staleRecovery` then decides whether acquisition fails closed or attempts caller-approved removal.
|
|
564
582
|
|
|
565
583
|
## Stale recovery: guarded `remove-if-unchanged`
|
|
@@ -84,6 +84,17 @@ Batch loading skips invalid entry names, malformed, oversized, or unreadable ent
|
|
|
84
84
|
|
|
85
85
|
Failed destinations are create-only. Quarantine publishes the claimed file by hardlink, so the queue and failed directories must share a filesystem with hardlink support. If `failed/<id>.json` already exists, quarantine rejects while preserving both that earlier evidence and the current claimed entry instead of overwriting either file. The `read` callback continues to receive the logical `.json` path even though bytes are read and migrations are written through the claimed path.
|
|
86
86
|
|
|
87
|
+
Claim, quarantine, and retirement recovery require hardlinks. If a link syscall
|
|
88
|
+
is refused with `EACCES`, `EPERM`, `EXDEV`, `ENOTSUP`, `EOPNOTSUPP`, or `ENOSYS`,
|
|
89
|
+
the operation rejects with `FsSafeError("helper-unavailable")` and retains the
|
|
90
|
+
original error in `cause`. This includes Android/Termux environments that deny
|
|
91
|
+
hardlinks. The refusal leaves the pending entry, processing claim, or retirement
|
|
92
|
+
record intact for retry after the capability or permission problem is resolved;
|
|
93
|
+
it does not acknowledge delivery. Batch loading propagates this capability
|
|
94
|
+
failure too. The queue does not fall back to copying or overwriting rename:
|
|
95
|
+
those operations cannot preserve its shared-inode recovery and create-only
|
|
96
|
+
collision evidence. Enqueue alone does not prove that an entry can be claimed.
|
|
97
|
+
|
|
87
98
|
Migrations stay bound to the exact processing file opened for that load. The
|
|
88
99
|
read descriptor remains pinned while the callback runs outside the transfer
|
|
89
100
|
lock; after the callback returns, migration reacquires the lock and rechecks the
|
|
@@ -79,103 +79,68 @@ Used by helpers that accept either an encoding (returning a string) or `null` (r
|
|
|
79
79
|
|
|
80
80
|
## `OpenResult` / `ReadResult`
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Import the result types rather than copying their shapes:
|
|
83
83
|
|
|
84
84
|
```ts
|
|
85
|
-
type OpenResult
|
|
86
|
-
handle: import("node:fs/promises").FileHandle;
|
|
87
|
-
containment: "kernel-atomic" | "best-effort";
|
|
88
|
-
realPath: string;
|
|
89
|
-
stat: import("node:fs").Stats;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
type ReadResult = {
|
|
93
|
-
buffer: Buffer;
|
|
94
|
-
containment: "kernel-atomic" | "best-effort";
|
|
95
|
-
realPath: string;
|
|
96
|
-
stat: import("node:fs").Stats;
|
|
97
|
-
};
|
|
85
|
+
import type { OpenResult, ReadResult, WritableOpenResult } from "@openclaw/fs-safe/root";
|
|
98
86
|
```
|
|
99
87
|
|
|
100
88
|
`realPath` is the canonical real path the read or open landed on, after symlink resolution; `stat` is the verified `fstat` result. Public root results currently report `containment: "best-effort"`; the union also describes direct native `openBeneath()` results, which report `"kernel-atomic"` on Linux. See the [security model](security-model.md#containment-guarantees-by-platform).
|
|
101
89
|
|
|
90
|
+
`ReadResult` carries the read `buffer`. `OpenResult` and `WritableOpenResult`
|
|
91
|
+
carry an owned Node `FileHandle` as `handle` and implement
|
|
92
|
+
`[Symbol.asyncDispose]()`; use `await using` or explicitly close the handle.
|
|
93
|
+
See [reading](reading.md#fs-open-rel-options) and
|
|
94
|
+
[writable handles](writing.md#openwritable-for-streaming) for ownership and streaming.
|
|
95
|
+
|
|
102
96
|
## `RootDefaults` / `RootOptions`
|
|
103
97
|
|
|
104
98
|
```ts
|
|
105
|
-
type
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
hardlinks?: "reject" | "allow";
|
|
112
|
-
maxBytes?: number;
|
|
113
|
-
mkdir?: boolean; // default true for mutation methods
|
|
114
|
-
mode?: number;
|
|
115
|
-
renameIdentity?: RenameIdentityPolicy;
|
|
116
|
-
symlinks?: "reject" | "follow-within-root" | "follow-parents-within-root";
|
|
117
|
-
mutationSymlinks?: MutationSymlinkPolicy;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
type DenyMutationPolicy = {
|
|
121
|
-
paths?: readonly string[];
|
|
122
|
-
prefixes?: readonly string[];
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
type RootOptions = {
|
|
126
|
-
rootDir: string;
|
|
127
|
-
defaults?: RootDefaults;
|
|
128
|
-
};
|
|
99
|
+
import type {
|
|
100
|
+
DenyMutationPolicy,
|
|
101
|
+
RenameIdentityPolicy,
|
|
102
|
+
RootDefaults,
|
|
103
|
+
RootOptions,
|
|
104
|
+
} from "@openclaw/fs-safe/root";
|
|
129
105
|
```
|
|
130
106
|
|
|
131
|
-
`RootDefaults` is what `root(rootDir, defaults)` accepts.
|
|
107
|
+
`RootDefaults` is what `root(rootDir, defaults)` accepts. `RootOptions` is the
|
|
108
|
+
`{ rootDir, defaults? }` record. See the [Root signature and defaults](root.md#signature)
|
|
109
|
+
for fields and behavior. `denyMutations` and `assertBeforeMutation` compose with
|
|
110
|
+
per-call restrictions: deny entries are merged, and the root authority assertion
|
|
111
|
+
runs before the per-call assertion.
|
|
132
112
|
|
|
133
113
|
## `RootReadOptions` / `RootWriteOptions` / `RootCopyOptions`
|
|
134
114
|
|
|
135
115
|
```ts
|
|
136
|
-
import type {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
151
|
-
type RootOpenWritableOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mkdir" | "mode" | "mutationSymlinks"> & {
|
|
152
|
-
writeMode?: "replace" | "append" | "update";
|
|
153
|
-
};
|
|
154
|
-
type RootWriteJsonOptions = RootWriteOptions & {
|
|
155
|
-
replacer?: Parameters<typeof JSON.stringify>[1];
|
|
156
|
-
space?: Parameters<typeof JSON.stringify>[2];
|
|
157
|
-
trailingNewline?: boolean;
|
|
158
|
-
};
|
|
159
|
-
type RootAppendOptions = RootWriteOptions & {
|
|
160
|
-
prependNewlineIfNeeded?: boolean;
|
|
161
|
-
};
|
|
162
|
-
type RootMoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
163
|
-
overwrite?: boolean;
|
|
164
|
-
};
|
|
165
|
-
type RootRemoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
166
|
-
recursive?: boolean;
|
|
167
|
-
force?: boolean;
|
|
168
|
-
order?: "filesystem" | "sorted";
|
|
169
|
-
maxEntries?: number;
|
|
170
|
-
maxDepth?: number;
|
|
171
|
-
signal?: AbortSignal;
|
|
172
|
-
};
|
|
173
|
-
type RootMkdirOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
174
|
-
private?: boolean;
|
|
175
|
-
};
|
|
116
|
+
import type {
|
|
117
|
+
RootAppendOptions,
|
|
118
|
+
RootCopyOptions,
|
|
119
|
+
RootCreateJsonOptions,
|
|
120
|
+
RootCreateOptions,
|
|
121
|
+
RootCreateStreamOptions,
|
|
122
|
+
RootMkdirOptions,
|
|
123
|
+
RootMoveOptions,
|
|
124
|
+
RootOpenOptions,
|
|
125
|
+
RootOpenWritableOptions,
|
|
126
|
+
RootReadOptions,
|
|
127
|
+
RootRemoveOptions,
|
|
128
|
+
RootWriteJsonOptions,
|
|
129
|
+
RootWriteOptions,
|
|
130
|
+
} from "@openclaw/fs-safe/root";
|
|
176
131
|
```
|
|
177
132
|
|
|
178
|
-
|
|
133
|
+
Each method accepts only its applicable defaults and method-specific options:
|
|
134
|
+
|
|
135
|
+
| Types | Contract |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `RootReadOptions` | Read symlink/hardlink policy and `maxBytes`; see [read options](reading.md#read-options). |
|
|
138
|
+
| `RootOpenOptions` | Read link policies, without `maxBytes`. The returned handle's I/O belongs to the caller. |
|
|
139
|
+
| `RootWriteOptions`, `RootWriteJsonOptions`, `RootAppendOptions` | [Write options](writing.md#write-options), JSON formatting, and append newline handling. |
|
|
140
|
+
| `RootCreateOptions`, `RootCreateJsonOptions`, `RootCreateStreamOptions` | [Exclusive creation](writing.md#atomic-buffered-creation), private permissions, and `durable: "file"`; streamed creation also accepts byte limits and cancellation. |
|
|
141
|
+
| `RootCopyOptions` | [Guarded copying](writing.md#write-verbs), source policy, `CopyCloneMode`, and `RootCopyPublicationReceipt`. |
|
|
142
|
+
| `RootOpenWritableOptions` | [Writable handles](writing.md#openwritable-for-streaming) with `writeMode`, without buffered-write durability or byte limits. |
|
|
143
|
+
| `RootMoveOptions`, `RootRemoveOptions`, `RootMkdirOptions` | [Mutation methods](root.md#writes) with their collision, removal-budget, and directory-privacy options. |
|
|
179
144
|
|
|
180
145
|
## `SymlinkPolicy` / `MutationSymlinkPolicy` / `HardlinkPolicy`
|
|
181
146
|
|
|
@@ -223,12 +223,12 @@ await fs.createJson("config/settings.json", { enabled: true }, { atomic: true })
|
|
|
223
223
|
await fs.create("config/flushed.json", initial, { atomic: true, durable: "file" });
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
`atomic: true`
|
|
226
|
+
`atomic: true` finishes all bytes in a sibling stage before publication.
|
|
227
227
|
The native backend uses its no-replace rename; the JavaScript fallback hardlinks
|
|
228
228
|
the completed stage and unlinks its temporary name in the same JavaScript turn.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
When the OS refuses hardlinks, it uses the [placeholder-rename fallback](#publication-without-hardlinks).
|
|
230
|
+
Other processes can briefly observe both linked names, or an empty placeholder
|
|
231
|
+
before the atomic replacement. Entries present at the exclusive claim are preserved, including dangling symlinks;
|
|
232
232
|
ordinary confinement, type, hardlink, and symlink-policy rejections still apply.
|
|
233
233
|
`assertBeforeMutation` retains its live checks through content writes and publication.
|
|
234
234
|
|
|
@@ -254,7 +254,7 @@ complete publication, and an indeterminate link or native rename must preserve n
|
|
|
254
254
|
recovery. A cleanup or close failure also retains the original operation failure.
|
|
255
255
|
The JavaScript fallback checks the destination again immediately before attempting
|
|
256
256
|
publication. A collision observed there leaves publication unattempted and cleans
|
|
257
|
-
the owned stage. A later collision or
|
|
257
|
+
the owned stage. A later collision or unclassified error from the link call remains
|
|
258
258
|
indeterminate, including `EEXIST`; the error code alone does not prove that the
|
|
259
259
|
filesystem left both names unchanged.
|
|
260
260
|
No later verification, mode, or synchronization failure authorizes deleting an
|
|
@@ -293,9 +293,10 @@ unlimited. Zero permits an empty input only. Invalid limits reject before I/O.
|
|
|
293
293
|
Unlike buffered creation's JavaScript fallback, streamed creation stages all
|
|
294
294
|
chunks before publishing the final name. Native mode uses no-replace rename;
|
|
295
295
|
the JavaScript fallback hardlinks the completed stage and removes its temporary
|
|
296
|
-
name in the same JavaScript turn.
|
|
297
|
-
hardlinks; other processes may
|
|
298
|
-
|
|
296
|
+
name in the same JavaScript turn. If hardlinks are refused, it uses the
|
|
297
|
+
[placeholder-rename fallback](#publication-without-hardlinks); other processes may
|
|
298
|
+
briefly observe an empty placeholder. A destination present at the exclusive
|
|
299
|
+
claim is preserved. Existing-target preflight does
|
|
299
300
|
not consume the input. The final mode and durability policy use the same guarded
|
|
300
301
|
writer as other Root operations.
|
|
301
302
|
|
|
@@ -408,8 +409,103 @@ With `overwrite: false`, an existing destination produces `already-exists` and
|
|
|
408
409
|
is never altered. Copying prepares a private sibling file before publishing its
|
|
409
410
|
completed contents. Native mode uses no-replace rename. The guarded JavaScript
|
|
410
411
|
fallback links the completed stage and removes its temporary name in the same
|
|
411
|
-
JavaScript turn
|
|
412
|
-
|
|
412
|
+
JavaScript turn. If the OS refuses hardlinks, it uses the placeholder-rename
|
|
413
|
+
fallback below. Other processes can briefly observe both linked names or an
|
|
414
|
+
empty placeholder. The source is never hardlinked to the destination.
|
|
415
|
+
|
|
416
|
+
### Copy metadata and source links
|
|
417
|
+
|
|
418
|
+
`preserveMetadata: true` restores metadata to the completed staging entry before
|
|
419
|
+
publication. It defaults to `false`; it does not select permissions, ownership,
|
|
420
|
+
ACLs, or extended attributes. Use the existing `preserveSourceMode` option as
|
|
421
|
+
well when source permissions should be selected. An explicit `mode` or Root
|
|
422
|
+
mode default continues to take precedence over `preserveSourceMode`.
|
|
423
|
+
|
|
424
|
+
| State | `preserveMetadata: true` | Permission selection |
|
|
425
|
+
| --- | --- | --- |
|
|
426
|
+
| POSIX access and modification times | Preserved | Not affected by `mode` |
|
|
427
|
+
| POSIX permission and executable bits | Not selected by this option | Existing `mode` / `preserveSourceMode` rules |
|
|
428
|
+
| Windows creation, access and write times | Preserved by native copying | Not affected by `mode` |
|
|
429
|
+
| Windows hidden, system, archive, temporary, offline, not-content-indexed | Preserved by native copying | Not affected by `mode` |
|
|
430
|
+
| Windows readonly | Not selected by this option | Existing `mode` / `preserveSourceMode` rules |
|
|
431
|
+
| Ownership, ACLs, POSIX extended attributes | Not copied | Existing destination policy |
|
|
432
|
+
| Windows sparse/integrity flags | Not set through metadata copying | Owned by the clone/FSCTL mechanism |
|
|
433
|
+
|
|
434
|
+
This applies to clones, native byte copies, and JavaScript byte copies. On
|
|
435
|
+
POSIX, native timestamp restoration retains nanoseconds; JavaScript restoration
|
|
436
|
+
uses Node's timestamp precision, as the portable tree copier does. On
|
|
437
|
+
Windows without the native metadata helper, JavaScript can restore only access
|
|
438
|
+
and write times; creation time and the non-permission attributes are not
|
|
439
|
+
preserved. Native `require` mode rejects a missing helper before publication.
|
|
440
|
+
The Windows attribute mask and POSIX timestamp conversion share the
|
|
441
|
+
[tree-copy implementation](copy.md#api); native tree copying has additional
|
|
442
|
+
metadata guarantees that this narrower option does not request.
|
|
443
|
+
|
|
444
|
+
`sourceSymlinks` defaults to `"reject"`, preserving the existing rejection of
|
|
445
|
+
symlinked absolute string sources with code `symlink` and message
|
|
446
|
+
`symlink not allowed`. There is no follow mode. Set
|
|
447
|
+
`sourceSymlinks: "copy-link"` to copy a source link itself, including a dangling
|
|
448
|
+
file or directory link. Its literal target remains data: copying never resolves
|
|
449
|
+
it. Windows uses the link's own directory attribute and copies the reparse
|
|
450
|
+
payload through the same native mechanism as `copyTree`.
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
await destination.copyIn("template-link", "/srv/templates/template-link", {
|
|
454
|
+
sourceSymlinks: "copy-link",
|
|
455
|
+
preserveMetadata: true,
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Link destinations are always exclusive. Omit `overwrite` or set it to `false`;
|
|
460
|
+
explicit `overwrite: true` with a link source fails with `invalid-path` before
|
|
461
|
+
mutation. Regular files retain the usual overwrite behavior. Destination Root
|
|
462
|
+
confinement, denied paths, parent-link policy, and mutation-authority checks
|
|
463
|
+
apply to copied links too.
|
|
464
|
+
macOS link permissions use the selected mode on the link itself; Linux link
|
|
465
|
+
permissions remain fixed by the kernel. The link target's permissions are never
|
|
466
|
+
changed.
|
|
467
|
+
|
|
468
|
+
`"copy-link"` requires an absolute-path string source. A `{ root, relativePath }`
|
|
469
|
+
source fails with `invalid-path` before either source method is called or a
|
|
470
|
+
destination is mutated: its `open` and `stat` capabilities cannot expose literal
|
|
471
|
+
link text or Windows link type without following the link. With the default
|
|
472
|
+
`"reject"`, capability sources continue to use their own Root read policies.
|
|
473
|
+
|
|
474
|
+
macOS and Windows link copying requires native support and otherwise reports
|
|
475
|
+
`helper-unavailable` before destination mutation. macOS `link(2)` follows its
|
|
476
|
+
source, so it cannot publish dangling symlink inodes exclusively. Linux can
|
|
477
|
+
publish the staged symlink inode with guarded hardlink/unlink when native
|
|
478
|
+
support is off or automatic no-replace rename is unavailable. This has the
|
|
479
|
+
same documented pathname identity windows as the JavaScript write fallback;
|
|
480
|
+
no operation follows the link target. Successful native publication uses
|
|
481
|
+
no-replace rename. No link publication uses the regular-file placeholder
|
|
482
|
+
fallback.
|
|
483
|
+
|
|
484
|
+
### Publication without hardlinks
|
|
485
|
+
|
|
486
|
+
For staged JavaScript no-replace copies and writes, `EACCES`, `EPERM`, `EXDEV`,
|
|
487
|
+
`ENOTSUP`, `EOPNOTSUPP`, and `ENOSYS` from the link operation permit a
|
|
488
|
+
**placeholder-rename** fallback, including Android/Termux hardlink restrictions.
|
|
489
|
+
It exclusively creates a mode-`0600`, no-follow destination, closes it after
|
|
490
|
+
capturing its exact identity, rechecks the parent and stage, and requires the
|
|
491
|
+
same empty, singly linked regular placeholder before renaming the completed
|
|
492
|
+
stage over it. A real creation-permission failure still rejects. `EEXIST` from
|
|
493
|
+
the link operation does not permit fallback or establish non-publication.
|
|
494
|
+
|
|
495
|
+
An empty placeholder is observable before the atomic replacement, and a crash
|
|
496
|
+
can leave that placeholder and the completed stage. Detected placeholder swaps
|
|
497
|
+
reject without removing foreign entries; failed pre-rename operations remove
|
|
498
|
+
only a verified owned empty placeholder. Failed cleanup reports `helper-failed`
|
|
499
|
+
with `details.publication: "not-published"` and `details.cleanup: "failed"`.
|
|
500
|
+
After rename, verification requires the stage name to be absent and the target
|
|
501
|
+
to retain the staged identity; the complete published file is preserved on failure.
|
|
502
|
+
|
|
503
|
+
These pathname checks are best-effort, not compare-and-swap: a same-directory
|
|
504
|
+
writer can replace the placeholder after the final check, and rename can replace
|
|
505
|
+
that intervening file or symlink (without following it). Protect the parent from
|
|
506
|
+
untrusted mutation when strict concurrent no-clobber is required. This fallback
|
|
507
|
+
preserves existing receipt shapes; `RootCopyPublicationReceipt` reports identity
|
|
508
|
+
and path, with no method or fallback field.
|
|
413
509
|
|
|
414
510
|
`clone` chooses the file-data transfer strategy through `CopyCloneMode`, shared
|
|
415
511
|
with [`copyTree`](copy.md#api). File copies default to `"never"`; tree copies
|
|
@@ -421,11 +517,19 @@ default to `"auto"`:
|
|
|
421
517
|
| `auto` | Try native file cloning, then copy offload or ordinary byte copying when cloning is unavailable. |
|
|
422
518
|
| `always` | Require native cloning; fail when the binding or filesystem cannot provide it. |
|
|
423
519
|
|
|
424
|
-
Native file cloning supports APFS
|
|
520
|
+
Native file cloning supports APFS, supported Linux filesystems, and Windows ReFS
|
|
521
|
+
(including Dev Drive). On Linux, a
|
|
425
522
|
`FICLONE` request denied with `EPERM`, as container seccomp profiles commonly
|
|
426
523
|
do, counts as unavailable cloning: `auto` copies bytes and `always` fails with
|
|
427
|
-
`unsupported-platform`. Windows
|
|
428
|
-
`
|
|
524
|
+
`unsupported-platform`. On Windows, `always` requires source and destination on
|
|
525
|
+
the same ReFS volume; other volumes report `unsupported-platform`. `auto` falls
|
|
526
|
+
back to bytes only for unavailable cloning capabilities, and `never` copies bytes.
|
|
527
|
+
ReFS named streams are rejected rather than silently discarded by cloning.
|
|
528
|
+
For successful native ReFS clones through either `copyIn` or `copyTree`, all full
|
|
529
|
+
clusters are block-cloned; a final partial cluster may be copied. Exact tail bytes and logical file size are
|
|
530
|
+
preserved. See the [shared ReFS contract](copy.md) and Microsoft's
|
|
531
|
+
[cluster-alignment and EOF requirements](https://learn.microsoft.com/en-us/windows/win32/fileio/block-cloning#restrictions-and-remarks).
|
|
532
|
+
Clone choice
|
|
429
533
|
does not change modes, durability, root confinement, or source and publication
|
|
430
534
|
identity checks. The shared strategy does not replace Root's guarded regular-file
|
|
431
535
|
contract with `copyTree`'s caller-owned immutable-tree and metadata contract.
|
|
@@ -560,6 +664,8 @@ await fs.remove("scratch/finished-job", {
|
|
|
560
664
|
Budgets must be non-negative safe integers or explicit `Infinity`, and require
|
|
561
665
|
`recursive: true`. Omitted budgets retain their finite defaults. An entry or
|
|
562
666
|
depth limit throws `too-large` before processing the over-budget entry.
|
|
667
|
+
Once the parent is admitted, `maxEntries: 0` rejects before inspecting even a
|
|
668
|
+
missing requested entry; `force: true` does not bypass that budget.
|
|
563
669
|
|
|
564
670
|
The default filesystem order streams each directory once, using memory and open
|
|
565
671
|
handles proportional to depth rather than directory width. Sorted order also
|
|
@@ -643,7 +749,8 @@ for the precise parent-pinning guarantee and remaining same-call limitations.
|
|
|
643
749
|
|
|
644
750
|
### `fs.mkdir(rel)`
|
|
645
751
|
|
|
646
|
-
`mkdir -p`. Creates missing parents.
|
|
752
|
+
`mkdir -p`. Creates missing parents. An existing regular file at the requested
|
|
753
|
+
directory name rejects with `not-file` in every native mode and remains unchanged.
|
|
647
754
|
|
|
648
755
|
`require` mode creates each missing component relative to a retained directory,
|
|
649
756
|
opens the child without following a final symlink, and checks its identity before
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -83,6 +83,10 @@
|
|
|
83
83
|
"types": "./dist/secret.d.ts",
|
|
84
84
|
"default": "./dist/secret.js"
|
|
85
85
|
},
|
|
86
|
+
"./pipe": {
|
|
87
|
+
"types": "./dist/pipe.d.ts",
|
|
88
|
+
"default": "./dist/pipe.js"
|
|
89
|
+
},
|
|
86
90
|
"./permissions": {
|
|
87
91
|
"types": "./dist/permissions-public.d.ts",
|
|
88
92
|
"default": "./dist/permissions-public.js"
|
|
@@ -156,6 +160,7 @@
|
|
|
156
160
|
"docs:check": "node scripts/check-doc-examples.mjs",
|
|
157
161
|
"docs:site": "node scripts/build-docs-site.mjs",
|
|
158
162
|
"native:build": "pnpm --filter @openclaw/fs-safe-native-build build",
|
|
163
|
+
"native:build:freebsd": "node scripts/build-freebsd.mjs",
|
|
159
164
|
"native:test": "cargo test --workspace",
|
|
160
165
|
"pack:check": "pnpm build && node scripts/check-pack.mjs",
|
|
161
166
|
"public-api:update": "pnpm build && node scripts/check-pack.mjs --update-public-api",
|
|
@@ -173,19 +178,22 @@
|
|
|
173
178
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
174
179
|
},
|
|
175
180
|
"optionalDependencies": {
|
|
176
|
-
"@openclaw/fs-safe-darwin-arm64": "0.
|
|
177
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
178
|
-
"@openclaw/fs-safe-
|
|
179
|
-
"@openclaw/fs-safe-
|
|
180
|
-
"@openclaw/fs-safe-linux-
|
|
181
|
-
"@openclaw/fs-safe-linux-
|
|
182
|
-
"@openclaw/fs-safe-
|
|
181
|
+
"@openclaw/fs-safe-darwin-arm64": "0.26.0",
|
|
182
|
+
"@openclaw/fs-safe-darwin-x64": "0.26.0",
|
|
183
|
+
"@openclaw/fs-safe-freebsd-arm64": "0.26.0",
|
|
184
|
+
"@openclaw/fs-safe-freebsd-x64": "0.26.0",
|
|
185
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.26.0",
|
|
186
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.26.0",
|
|
187
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.26.0",
|
|
188
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.26.0",
|
|
189
|
+
"@openclaw/fs-safe-win32-arm64-msvc": "0.26.0",
|
|
190
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.26.0",
|
|
183
191
|
"jszip": "^3.10.2"
|
|
184
192
|
},
|
|
185
193
|
"devDependencies": {
|
|
186
194
|
"@emnapi/runtime": "2.0.0-alpha.6",
|
|
187
195
|
"@napi-rs/cli": "3.10.8",
|
|
188
|
-
"@types/node": "^26.6.
|
|
196
|
+
"@types/node": "^26.6.5",
|
|
189
197
|
"@vitest/coverage-v8": "5.0.3",
|
|
190
198
|
"fast-check": "^4.10.2",
|
|
191
199
|
"istanbul-lib-coverage": "3.2.2",
|
|
@@ -194,7 +202,7 @@
|
|
|
194
202
|
"sigstore": "5.0.0",
|
|
195
203
|
"tar": "7.5.22",
|
|
196
204
|
"typescript": "^7.0.2",
|
|
197
|
-
"vite": "8.3.
|
|
205
|
+
"vite": "8.3.4",
|
|
198
206
|
"vitest": "^5.0.3"
|
|
199
207
|
},
|
|
200
208
|
"engines": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|