@openclaw/fs-safe 0.19.0 → 0.20.0
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/CHANGELOG.md +27 -0
- package/README.md +15 -5
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.js +1 -0
- package/dist/archive-plan.d.ts +2 -7
- package/dist/archive-read.js +9 -18
- package/dist/archive-zip-entry.d.ts +11 -11
- package/dist/archive-zip-entry.js +3 -35
- package/dist/archive-zip-integrity.d.ts +2 -2
- package/dist/archive-zip-integrity.js +2 -12
- package/dist/archive-zip-loader.d.ts +7 -3
- package/dist/archive-zip-loader.js +10 -9
- package/dist/archive-zip-preflight.d.ts +2 -1
- package/dist/archive-zip-preflight.js +16 -7
- package/dist/archive.js +17 -16
- package/dist/directory-receipt.js +5 -7
- package/dist/effective-uid.js +1 -4
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +3 -2
- package/dist/file-lock-sync-root-held.js +1 -4
- package/dist/file-store.d.ts +4 -7
- package/dist/json-document-store.d.ts +4 -9
- package/dist/local-file-access.js +2 -5
- package/dist/move-path-cleanup.js +4 -4
- package/dist/native-binding.d.ts +6 -1
- package/dist/native-staged-symlink.d.ts +13 -0
- package/dist/native-staged-symlink.js +303 -0
- package/dist/owner-dacl-batch-worker.d.ts +1 -0
- package/dist/owner-dacl-batch-worker.js +54 -0
- package/dist/owner-dacl-batch.d.ts +5 -0
- package/dist/owner-dacl-batch.js +64 -0
- package/dist/owner-dacl.d.ts +2 -0
- package/dist/owner-dacl.js +3 -0
- package/dist/path.js +17 -1
- package/dist/permission-exec.js +3 -6
- package/dist/permissions-public.d.ts +1 -0
- package/dist/permissions-public.js +1 -0
- package/dist/pinned-mutation-admission.d.ts +0 -1
- package/dist/pinned-open.d.ts +0 -1
- package/dist/pinned-open.js +1 -2
- package/dist/publish-copy-stage.js +4 -0
- package/dist/read-opened-file.d.ts +2 -5
- package/dist/regular-file.js +3 -3
- package/dist/replace-file-copy-fallback.d.ts +1 -2
- package/dist/root-impl.js +0 -3
- package/dist/root-observed-path.d.ts +0 -1
- package/dist/root-observed-path.js +0 -3
- package/dist/root-path-observation.d.ts +4 -11
- package/dist/root-path.js +7 -10
- package/dist/root-write-admission.js +0 -2
- package/dist/safe-path-segment.d.ts +1 -0
- package/dist/safe-path-segment.js +8 -2
- package/dist/secure-file.js +3 -2
- package/dist/sidecar-lock.js +5 -3
- package/dist/staged-symlink-types.d.ts +49 -0
- package/dist/staged-symlink-types.js +1 -0
- package/dist/symlink-parents.js +58 -7
- package/dist/temp-target.js +4 -2
- package/dist/temp-workspace-owner.js +4 -9
- package/dist/text-atomic.d.ts +2 -1
- package/dist/text-atomic.js +2 -0
- package/dist/trash.js +27 -1
- package/dist/walk.d.ts +2 -5
- package/dist/windows-owner.d.ts +0 -1
- package/dist/windows-owner.js +0 -1
- package/dist/windows-security-bridge.cs +6 -4
- package/dist/windows-security-bridge.ps1 +78 -3
- package/dist/windows-security-command.d.ts +8 -0
- package/dist/windows-security-command.js +66 -12
- package/dist/windows-security-facts.d.ts +3 -0
- package/dist/windows-security-facts.js +4 -0
- package/docs/advanced.md +3 -2
- package/docs/archive.md +8 -0
- package/docs/atomic.md +11 -3
- package/docs/contributing.md +30 -0
- package/docs/install.md +28 -0
- package/docs/native-helper.md +5 -4
- package/docs/native.md +45 -1
- package/docs/permissions.md +66 -0
- package/docs/public-api.md +7 -1
- package/docs/security-model.md +4 -1
- package/docs/sidecar-lock.md +2 -0
- package/docs/staged-symlink.md +123 -0
- package/docs/store.md +3 -1
- package/docs/testing.md +28 -0
- package/docs/writing.md +10 -0
- package/package.json +9 -9
package/docs/security-model.md
CHANGED
|
@@ -232,13 +232,16 @@ The library does not modify or constrain the global Node.js `fs` namespace, and
|
|
|
232
232
|
|
|
233
233
|
| Mechanism | Reported containment | Boundary |
|
|
234
234
|
|---|---|---|
|
|
235
|
-
| Linux native | `kernel-atomic` | `openat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS)` resolves and opens under the root in one kernel operation. |
|
|
235
|
+
| Linux native with `openat2` | `kernel-atomic` | `openat2(RESOLVE_BENEATH | RESOLVE_NO_MAGICLINKS)` resolves and opens under the root in one kernel operation. |
|
|
236
|
+
| Linux native without `openat2` | `best-effort` | A no-follow `openat` component walk retains each parent and verifies exact identity associations before and after the final open; all symlink components are rejected. |
|
|
236
237
|
| macOS native | `best-effort` | macOS 15.4 and newer use `O_RESOLVE_BENEATH` first; older kernels use the guarded `openat(O_NOFOLLOW)` component walk. Both verify the opened descriptor with `F_GETPATH`. |
|
|
237
238
|
| Windows native | `best-effort` | Handle-relative `NtCreateFile` rejects reparse points, but this package does not claim a Linux-style atomic beneath guarantee. |
|
|
238
239
|
| JavaScript fallback | `best-effort` | Canonical checks, no-follow opens where Node exposes them, and post-open identity checks form a check-then-use sequence. |
|
|
239
240
|
|
|
240
241
|
The macOS `F_GETPATH` verification is an escape detector, not a race-atomic guarantee. A hostile same-UID process can rename a directory after `O_RESOLVE_BENEATH` or the manual walk and race the post-open sample or a later descriptor-relative mutation. The native result therefore remains `best-effort` on macOS even when the kernel flag is available. No policy decision is attached to these labels; callers can inspect the fact and decide what their own threat model requires.
|
|
241
242
|
|
|
243
|
+
The Linux fallback's identity checks are also detection-based: a directory can be renamed between chain samples or before a later descriptor-relative mutation. It cannot provide atomic resolution, and rejection after a mutating open does not promise rollback. See [Linux without openat2](native.md#linux-without-openat2) for the cached capability probe, conservative symlink rejection, and operations that remain unavailable.
|
|
244
|
+
|
|
242
245
|
The public `OpenResult`, `ReadResult`, and `WritableOpenResult` expose `containment`. Those root APIs currently report `best-effort`; direct native `openBeneath()` reports the platform value above. No-replace publication uses `renameat2(RENAME_NOREPLACE)` on Linux, `renameatx_np(RENAME_EXCL)` on macOS, and `FileRenameInfoEx` with replacement disabled on Windows, but those separate mutation semantics do not upgrade an open result's containment label.
|
|
243
246
|
|
|
244
247
|
## Limitations to keep in mind
|
package/docs/sidecar-lock.md
CHANGED
|
@@ -63,6 +63,8 @@ Exit cleanup tolerates shared managers created by older package copies that lack
|
|
|
63
63
|
|
|
64
64
|
Each new sidecar also carries an internal random ownership token encoded as JSON trailing whitespace. `JSON.parse()` and every payload callback still see exactly the caller-provided object. Only the process that successfully created the sidecar keeps that token as release authority; merely reading token-shaped bytes from disk does not enable this mode. Release compares the in-memory token and exact serialized bytes, and requires the pathname to remain a regular file, instead of requiring an opened descriptor and pathname lookup to report the same inode identity. This preserves ownership checks on filesystems such as Docker Desktop VirtioFS where those two views can legitimately differ. Sidecars created by older releases have no token and retain the legacy identity-plus-content check. If Windows reports a zero device or inode for either identity observation, that check is inconclusive and removal is skipped.
|
|
65
65
|
|
|
66
|
+
Last-chance raw-lock exit cleanup requires known Windows device and inode values from both pathname observations and the opened descriptor. A zero value leaves the sidecar in place, including for token-owned locks. Known descriptor/path identity differences remain supported; a pathname identity change during the read still prevents cleanup.
|
|
67
|
+
|
|
66
68
|
The raw sidecar bytes are not a canonical JSON representation: tools that trim or rewrite the trailing whitespace invalidate the ownership token, so release leaves the changed sidecar in place and fails closed. The token distinguishes cooperating acquisitions; it is not a secret and does not make pathname compare-and-remove atomic against a hostile process that can replace files outside the lock protocol.
|
|
67
69
|
|
|
68
70
|
`release()` propagates an I/O failure that prevents deletion of an unchanged, owned sidecar; it never reports successful cleanup while leaving that lock behind. The handle and manager retain the exact cleanup receipt after a failure, so the same handle can retry `release()` and `manager.drain()` can retry retained cleanup. A changed sidecar remains an ownership mismatch rather than a deletion failure and is left untouched. If both a `withFileLock()` or `withFileLockSync()` callback and release fail, the release error is the primary `SuppressedError.error` and the callback failure remains available as `SuppressedError.suppressed`. Failed asynchronous acquisition cleanup uses the same shape, with the cleanup error primary and the acquisition failure suppressed. On Node runtimes without the global `SuppressedError` constructor, fs-safe returns the equivalent `Error` shape with the same name and properties.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Retained symlink publication
|
|
3
|
+
description: "Admit and retain an identified staged symlink for no-replace publication and explicit recovery."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retained symlink publication
|
|
7
|
+
|
|
8
|
+
`retainSymlinkInDirectory()` from `@openclaw/fs-safe/advanced` admits an
|
|
9
|
+
**existing** direct-child symlink against caller-captured identity, ownership,
|
|
10
|
+
change time and target. It holds a no-follow descriptor to that inode and its
|
|
11
|
+
parent until cleanup. It does not create a symlink or infer ownership from a
|
|
12
|
+
same-target pathname observation. This dedicated lifecycle does not relax
|
|
13
|
+
`Root.move()` or regular-file staging's symlink rejection.
|
|
14
|
+
|
|
15
|
+
The caller owns staging and admission evidence. Create and inspect the stage
|
|
16
|
+
under the application's cooperative writer lock, and keep that lock through
|
|
17
|
+
admission. A historical stat is not a lifetime handle: if peers can recycle
|
|
18
|
+
inodes between capture and admission, identity numbers alone cannot establish
|
|
19
|
+
historical ownership. Once admitted, the retained descriptor prevents inode
|
|
20
|
+
reuse until this owner closes. Failed admission closes descriptors without
|
|
21
|
+
unlinking anything; all staging cleanup remains with the caller.
|
|
22
|
+
|
|
23
|
+
Linux uses `O_PATH | O_NOFOLLOW`; macOS uses metadata-only `O_EVTONLY | O_SYMLINK`
|
|
24
|
+
with nonblocking/no-controlling-terminal flags. A non-following preflight rejects
|
|
25
|
+
observed special files before open; the opened descriptor must still match.
|
|
26
|
+
Windows, native mode off, and missing or older bindings reject before namespace
|
|
27
|
+
mutation. No fallback interpreter, copy, following open, or public descriptor
|
|
28
|
+
is used. Symlinks must have one link and a UTF-8 target; their targets are never
|
|
29
|
+
opened or confined by this API. The application must authorize the target.
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {
|
|
35
|
+
retainSymlinkInDirectory,
|
|
36
|
+
type StagedSymlinkExpected,
|
|
37
|
+
type PublishedSymlinkReceipt,
|
|
38
|
+
} from "@openclaw/fs-safe/advanced";
|
|
39
|
+
import type { DirectoryReceipt } from "@openclaw/fs-safe/durability";
|
|
40
|
+
|
|
41
|
+
// Application owns stage creation, capture, locking and transaction policy.
|
|
42
|
+
export async function publishInstallerLink(options: {
|
|
43
|
+
directory: DirectoryReceipt;
|
|
44
|
+
stagedBasename: string;
|
|
45
|
+
expected: StagedSymlinkExpected;
|
|
46
|
+
finalBasename: string;
|
|
47
|
+
assertAuthorizedAndCurrent(): void;
|
|
48
|
+
}): Promise<PublishedSymlinkReceipt> {
|
|
49
|
+
await using staged = await retainSymlinkInDirectory({
|
|
50
|
+
directory: options.directory,
|
|
51
|
+
basename: options.stagedBasename,
|
|
52
|
+
expected: options.expected,
|
|
53
|
+
assertBeforeMutation: options.assertAuthorizedAndCurrent,
|
|
54
|
+
});
|
|
55
|
+
return await staged.publish(options.finalBasename);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`expected` requires exact bigint `dev`, `ino`, `ctimeNs`, integer `uid`
|
|
60
|
+
and `gid`, and `target`. Admission snapshots these values and the parent
|
|
61
|
+
receipt. The frozen returned receipt contains `directory`,
|
|
62
|
+
`temporaryBasename`, `target`, and preparation-time `identity` metadata,
|
|
63
|
+
like [regular-file staging](staged-file.md). Rename may change timestamps;
|
|
64
|
+
the original receipt is never replaced by a post-publication identity.
|
|
65
|
+
|
|
66
|
+
## Lifetime and outcomes
|
|
67
|
+
|
|
68
|
+
- `assertCurrent()` checks the admitted parent pathname, original name, retained
|
|
69
|
+
inode, single-link count, ownership, mode and target before publication.
|
|
70
|
+
- `publish(basename)` accepts a distinct portable direct-child name. It performs
|
|
71
|
+
the synchronous authority assertion, rechecks source and parent, and dispatches
|
|
72
|
+
guarded native no-replace rename. An existing file, symlink or directory is
|
|
73
|
+
never overwritten, and the link is never nested inside a destination directory.
|
|
74
|
+
- Successful dispatch is recorded as `published` **before** fallible postchecks.
|
|
75
|
+
A foreign same-target replacement fails verification without becoming owned.
|
|
76
|
+
`assertPublished()` checks against the still-retained inode.
|
|
77
|
+
- `removePublished()` is an explicit recovery action, not automatic rollback.
|
|
78
|
+
It reasserts authority and removes only an observed matching published link
|
|
79
|
+
through the retained original parent. It returns `removed`, `name-absent`,
|
|
80
|
+
or `preserved`. Its outcome or error is cached: it never retries an uncertain
|
|
81
|
+
unlink or touches a later replacement. It does not restore an old destination.
|
|
82
|
+
- `cleanup()` removes only an unattempted, still-owned stage, then closes both
|
|
83
|
+
handles. Published names are preserved. Indeterminate publication preserves
|
|
84
|
+
both names. Authority rejection during cleanup preserves the stage and still
|
|
85
|
+
closes handles. Repeated cleanup replays the cached result/error without
|
|
86
|
+
touching descriptor numbers.
|
|
87
|
+
- `await using` invokes cleanup and raises on a `preserved` outcome. Invocation
|
|
88
|
+
order serializes descriptor work; reentrant calls from the authority callback
|
|
89
|
+
reject. Callbacks must be synchronous; returned thenables are refused.
|
|
90
|
+
|
|
91
|
+
Errors carry `StagedSymlinkFailureDetails` in `FsSafeError.details`: the phase,
|
|
92
|
+
recorded `publication`, and a cleanup receipt when applicable. Publication is
|
|
93
|
+
`not-published`, `published` (with the precaptured receipt), or
|
|
94
|
+
`indeterminate` (with the attempted name). Native errno, including a collision,
|
|
95
|
+
does not rule out a committed remote rename whose response was lost. Only
|
|
96
|
+
pre-dispatch rejection leaves publication `not-published`. After an indeterminate
|
|
97
|
+
result, this owner cannot publish again or remove the possibly published name.
|
|
98
|
+
If inspecting a native rename error itself fails, publication is likewise
|
|
99
|
+
`indeterminate`; the original thrown value remains the reported cause.
|
|
100
|
+
|
|
101
|
+
Cleanup records `temporaryBasename`, `publication`, `resources`
|
|
102
|
+
(`closed` or `close-failed`), and `status` (`removed`, `name-absent`,
|
|
103
|
+
`preserved`, `failed`, or `not-needed`). Failure closes every retained handle
|
|
104
|
+
once and aggregates cleanup/close errors. A failed explicit recovery unlink is
|
|
105
|
+
reported to its caller and is not converted into successful recovery by cleanup.
|
|
106
|
+
Thrown values whose error metadata cannot be inspected are reported as
|
|
107
|
+
`helper-failed` with the original value as `cause`; cleanup and recovery still
|
|
108
|
+
cache their terminal error without retrying callbacks or descriptor closes.
|
|
109
|
+
|
|
110
|
+
## Limits
|
|
111
|
+
|
|
112
|
+
This is directory-relative targeting, **not CAS**. Identity checks followed by
|
|
113
|
+
rename or unlink are not atomic conditional mutations. An uncooperative writer
|
|
114
|
+
can replace a child in the final syscall gap; observed substitutes are preserved,
|
|
115
|
+
but callers must coordinate writers to exclude that gap. The retained parent
|
|
116
|
+
prevents redirection into a replacement parent; a parent move can still cause
|
|
117
|
+
publication in the moved original and a `published` postcheck failure.
|
|
118
|
+
|
|
119
|
+
No receipt promises crash durability or recovery after process death, arbitrary
|
|
120
|
+
renames, permission revocation or I/O failure. This API does not sync directories
|
|
121
|
+
or persist descriptors. Caller journals, state capture, conditional restoration,
|
|
122
|
+
durability, and transaction settlement remain separate requirements. Keep the
|
|
123
|
+
owner alive until the application's publication or recovery decision settles.
|
package/docs/store.md
CHANGED
|
@@ -62,7 +62,9 @@ const pending = await loadPendingJsonDurableQueueEntries({ queueDir, tempPrefix:
|
|
|
62
62
|
|
|
63
63
|
`id` must be a single safe path segment: non-empty, not dot-prefixed, and made
|
|
64
64
|
from letters, numbers, `_`, `-`, and `.`. Slashes, backslashes, NUL bytes, `.`,
|
|
65
|
-
and
|
|
65
|
+
`..`, and Windows reserved device names such as `CON`, `NUL`, and `COM1` are
|
|
66
|
+
rejected on every platform so queue IDs remain portable. Temporary filenames
|
|
67
|
+
continue to suffix reserved stems (for example, `CON.txt` becomes `CON_.txt`).
|
|
66
68
|
|
|
67
69
|
Use `ackJsonDurableQueueEntry()` after durable processing succeeds and
|
|
68
70
|
`moveJsonDurableQueueEntryToFailed()` when the caller wants to quarantine an
|
package/docs/testing.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Testing
|
|
2
2
|
|
|
3
|
+
## Linux openat2 fallback
|
|
4
|
+
|
|
5
|
+
Build the host addon and package first. The test hook is cached with the native
|
|
6
|
+
capability probe; set it before starting the process, rather than changing it
|
|
7
|
+
between tests in one process:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm native:build
|
|
11
|
+
pnpm build
|
|
12
|
+
FS_SAFE_TEST_NO_OPENAT2=1 FS_SAFE_NATIVE_MODE=require pnpm test test/linux-openat2-fallback.test.ts test/root-move-noreplace.test.ts test/root-move-native-integration.test.ts test/native-write-containment.test.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
On Linux, the seccomp harness also exercises the real syscall failure without
|
|
16
|
+
the environment hook. It needs a C compiler and permission to install an
|
|
17
|
+
unprivileged seccomp filter; it affects only its child process:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cc test/fixtures/deny-openat2.c -o /tmp/fs-safe-deny-openat2
|
|
21
|
+
/tmp/fs-safe-deny-openat2 ENOSYS node test/fixtures/linux-openat2-fallback.mjs "$PWD/native/fs-safe-native.linux-x64-gnu.node"
|
|
22
|
+
/tmp/fs-safe-deny-openat2 EPERM node test/fixtures/linux-openat2-fallback.mjs "$PWD/native/fs-safe-native.linux-x64-gnu.node"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use the matching native artifact filename on other Linux architectures/libcs.
|
|
26
|
+
The fixture proves nested moves, collisions, read/write, traversal, symlink and
|
|
27
|
+
hardlink rejection, cached selection, and `helper-unavailable` for strict
|
|
28
|
+
bounded cleanup. Bounded-cleanup success tests require real `openat2`; run the
|
|
29
|
+
full suite with the environment hook unset.
|
|
30
|
+
|
|
3
31
|
`@openclaw/fs-safe/test-hooks` exposes test-only injection points. Registration
|
|
4
32
|
is allowed only when `process.env.NODE_ENV === "test"` or
|
|
5
33
|
`process.env.VITEST === "true"`; registering a non-empty hook set elsewhere
|
package/docs/writing.md
CHANGED
|
@@ -211,6 +211,11 @@ can establish them; native disposal can retain them inside a `SuppressedError`
|
|
|
211
211
|
cause. Preserve those details when handling errors: a rejection can follow
|
|
212
212
|
complete publication, and an indeterminate link or native rename must preserve names for
|
|
213
213
|
recovery. A cleanup or close failure also retains the original operation failure.
|
|
214
|
+
The JavaScript fallback checks the destination again immediately before attempting
|
|
215
|
+
publication. A collision observed there leaves publication unattempted and cleans
|
|
216
|
+
the owned stage. A later collision or other error from the link call remains
|
|
217
|
+
indeterminate, including `EEXIST`; the error code alone does not prove that the
|
|
218
|
+
filesystem left both names unchanged.
|
|
214
219
|
No later verification, mode, or synchronization failure authorizes deleting an
|
|
215
220
|
already published complete destination. See [receipt meanings](staged-file.md).
|
|
216
221
|
|
|
@@ -354,6 +359,11 @@ replacing rename. After dispatch it rechecks both parent identities, so a
|
|
|
354
359
|
post-operation rejection can mean the no-replace rename completed. Directory
|
|
355
360
|
moves continue to require `overwrite: true`.
|
|
356
361
|
|
|
362
|
+
Linux without `openat2` uses the [guarded native parent walk](native.md#linux-without-openat2).
|
|
363
|
+
The move still uses `renameat2(RENAME_NOREPLACE)` and preserves collisions;
|
|
364
|
+
parent resolution reports the documented `best-effort` containment class.
|
|
365
|
+
Disabling the addon still makes no-clobber moves unavailable.
|
|
366
|
+
|
|
357
367
|
Both selected canonical endpoints are admitted inside the retained Root after
|
|
358
368
|
native parent admission. With `mutationSymlinks: "reject"`, both full operation
|
|
359
369
|
paths are rechecked after the live mutation-authority callback and before
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -169,18 +169,18 @@
|
|
|
169
169
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
170
170
|
},
|
|
171
171
|
"optionalDependencies": {
|
|
172
|
-
"@openclaw/fs-safe-darwin-arm64": "0.
|
|
173
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
174
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.
|
|
175
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.
|
|
176
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.
|
|
177
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.
|
|
178
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.
|
|
172
|
+
"@openclaw/fs-safe-darwin-arm64": "0.20.0",
|
|
173
|
+
"@openclaw/fs-safe-darwin-x64": "0.20.0",
|
|
174
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.20.0",
|
|
175
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.20.0",
|
|
176
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.20.0",
|
|
177
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.20.0",
|
|
178
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.20.0",
|
|
179
179
|
"jszip": "^3.10.2"
|
|
180
180
|
},
|
|
181
181
|
"devDependencies": {
|
|
182
182
|
"@emnapi/runtime": "2.0.0-alpha.5",
|
|
183
|
-
"@napi-rs/cli": "3.10.
|
|
183
|
+
"@napi-rs/cli": "3.10.5",
|
|
184
184
|
"@types/node": "^26.6.1",
|
|
185
185
|
"@vitest/coverage-v8": "5.0.1",
|
|
186
186
|
"fast-check": "^4.10.1",
|