@lousy-agents/cli 5.14.8 → 5.14.10
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.
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@biomejs/biome": "2.4.15",
|
|
21
|
-
"@lousy-agents/mcp": "5.14.
|
|
21
|
+
"@lousy-agents/mcp": "5.14.8",
|
|
22
22
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
23
23
|
"@testcontainers/postgresql": "11.14.0",
|
|
24
24
|
"@types/node": "24.12.4",
|
|
25
25
|
"@upstash/context7-mcp": "2.2.5",
|
|
26
26
|
"chance": "1.1.13",
|
|
27
27
|
"testcontainers": "11.14.0",
|
|
28
|
-
"tsx": "4.22.
|
|
28
|
+
"tsx": "4.22.1",
|
|
29
29
|
"typescript": "6.0.3",
|
|
30
30
|
"vitest": "4.1.6"
|
|
31
31
|
}
|
|
@@ -956,9 +956,9 @@
|
|
|
956
956
|
}
|
|
957
957
|
},
|
|
958
958
|
"node_modules/@lousy-agents/mcp": {
|
|
959
|
-
"version": "5.14.
|
|
960
|
-
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.14.
|
|
961
|
-
"integrity": "sha512
|
|
959
|
+
"version": "5.14.8",
|
|
960
|
+
"resolved": "https://registry.npmjs.org/@lousy-agents/mcp/-/mcp-5.14.8.tgz",
|
|
961
|
+
"integrity": "sha512-stW+xTi0Y/fEs/N8oVJ6u3RUi4Xd3dLG8NMdsB7tcP1KIYTQKE/CXHbU9QElLRSuFZwNFPtf15G4Pm4QTes1Pg==",
|
|
962
962
|
"dev": true,
|
|
963
963
|
"license": "BSD-2-Clause-Patent",
|
|
964
964
|
"dependencies": {
|
|
@@ -5423,9 +5423,9 @@
|
|
|
5423
5423
|
"optional": true
|
|
5424
5424
|
},
|
|
5425
5425
|
"node_modules/tsx": {
|
|
5426
|
-
"version": "4.22.
|
|
5427
|
-
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.
|
|
5428
|
-
"integrity": "sha512-
|
|
5426
|
+
"version": "4.22.1",
|
|
5427
|
+
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz",
|
|
5428
|
+
"integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==",
|
|
5429
5429
|
"dev": true,
|
|
5430
5430
|
"license": "MIT",
|
|
5431
5431
|
"dependencies": {
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@biomejs/biome": "2.4.15",
|
|
32
|
-
"@lousy-agents/mcp": "5.14.
|
|
32
|
+
"@lousy-agents/mcp": "5.14.8",
|
|
33
33
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
34
34
|
"@testcontainers/postgresql": "11.14.0",
|
|
35
35
|
"@types/node": "24.12.4",
|
|
36
36
|
"@upstash/context7-mcp": "2.2.5",
|
|
37
37
|
"chance": "1.1.13",
|
|
38
38
|
"testcontainers": "11.14.0",
|
|
39
|
-
"tsx": "4.22.
|
|
39
|
+
"tsx": "4.22.1",
|
|
40
40
|
"typescript": "6.0.3",
|
|
41
41
|
"vitest": "4.1.6"
|
|
42
42
|
}
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@biomejs/biome": "2.4.15",
|
|
23
|
-
"@lousy-agents/mcp": "5.14.
|
|
23
|
+
"@lousy-agents/mcp": "5.14.8",
|
|
24
24
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
25
25
|
"@types/node": "24.12.4",
|
|
26
26
|
"@upstash/context7-mcp": "2.2.5",
|
|
27
27
|
"chance": "1.1.13",
|
|
28
|
-
"tsx": "4.22.
|
|
28
|
+
"tsx": "4.22.1",
|
|
29
29
|
"typescript": "6.0.3",
|
|
30
30
|
"vitest": "4.1.6"
|
|
31
31
|
}
|
package/dist/index.js
CHANGED
|
@@ -2990,7 +2990,7 @@ exports.basename = (path, { windows } = {}) => {
|
|
|
2990
2990
|
|
|
2991
2991
|
|
|
2992
2992
|
},
|
|
2993
|
-
|
|
2993
|
+
1809(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
2994
2994
|
// NAMESPACE OBJECT: ../../node_modules/micromark/lib/constructs.js
|
|
2995
2995
|
var constructs_namespaceObject = {};
|
|
2996
2996
|
__webpack_require__.r(constructs_namespaceObject);
|
|
@@ -3210,6 +3210,9 @@ function path_isPathInside(root, target) {
|
|
|
3210
3210
|
const firstSegment = relative.split(external_node_path_.posix.sep)[0];
|
|
3211
3211
|
return relative === "" || (firstSegment !== ".." && !external_node_path_.isAbsolute(relative));
|
|
3212
3212
|
}
|
|
3213
|
+
function isPathRelativeEscape(relativePath) {
|
|
3214
|
+
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
3215
|
+
}
|
|
3213
3216
|
function resolveSafeBaseDir(rootDir) {
|
|
3214
3217
|
const resolved = path.resolve(rootDir);
|
|
3215
3218
|
return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
|
|
@@ -3275,7 +3278,7 @@ function splitSafeRelativePath(relativePath) {
|
|
|
3275
3278
|
function resolveSafeRelativePath(rootDir, relativePath) {
|
|
3276
3279
|
const root = path.resolve(rootDir);
|
|
3277
3280
|
const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
|
|
3278
|
-
if (
|
|
3281
|
+
if (!path_isPathInside(root, target)) {
|
|
3279
3282
|
throw new FsSafeError("outside-workspace", "relative path escapes root");
|
|
3280
3283
|
}
|
|
3281
3284
|
return target;
|
|
@@ -3358,17 +3361,17 @@ function directory_guard_createNearestExistingSyncDirectoryGuard(rootReal, targe
|
|
|
3358
3361
|
|
|
3359
3362
|
|
|
3360
3363
|
|
|
3364
|
+
|
|
3361
3365
|
function isSameOrChildPath(candidate, parent) {
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
function isPathEscape(relativePath) {
|
|
3365
|
-
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
3366
|
+
const parentPrefix = parent.endsWith(external_node_path_.sep) ? parent : `${parent}${external_node_path_.sep}`;
|
|
3367
|
+
return candidate === parent || candidate.startsWith(parentPrefix);
|
|
3366
3368
|
}
|
|
3367
3369
|
async function mkdirPathComponentsWithGuards(params) {
|
|
3368
3370
|
const root = external_node_path_.resolve(params.rootReal);
|
|
3371
|
+
const rootCanonical = external_node_path_.resolve(await promises_.realpath(root));
|
|
3369
3372
|
const target = external_node_path_.resolve(params.targetPath);
|
|
3370
3373
|
const relative = external_node_path_.relative(root, target);
|
|
3371
|
-
if (
|
|
3374
|
+
if (isPathRelativeEscape(relative)) {
|
|
3372
3375
|
throw new errors_FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
3373
3376
|
}
|
|
3374
3377
|
let current = root;
|
|
@@ -3391,7 +3394,7 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
3391
3394
|
}
|
|
3392
3395
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
3393
3396
|
// segment at a time and realpath-check each segment before descending.
|
|
3394
|
-
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)),
|
|
3397
|
+
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)), rootCanonical)) {
|
|
3395
3398
|
throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
|
|
3396
3399
|
}
|
|
3397
3400
|
await directory_guard_createAsyncDirectoryGuard(next);
|
|
@@ -3526,26 +3529,20 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
3526
3529
|
|
|
3527
3530
|
|
|
3528
3531
|
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
3529
|
-
import base64
|
|
3530
|
-
import errno
|
|
3531
|
-
import json
|
|
3532
|
-
import os
|
|
3533
|
-
import secrets
|
|
3534
|
-
import stat
|
|
3535
|
-
import sys
|
|
3536
|
-
|
|
3532
|
+
import base64, errno, json, os, secrets, stat, sys
|
|
3537
3533
|
DIR_FLAGS = os.O_RDONLY
|
|
3538
3534
|
if hasattr(os, "O_DIRECTORY"):
|
|
3539
3535
|
DIR_FLAGS |= os.O_DIRECTORY
|
|
3540
3536
|
if hasattr(os, "O_NOFOLLOW"):
|
|
3541
3537
|
DIR_FLAGS |= os.O_NOFOLLOW
|
|
3542
3538
|
READ_FLAGS = os.O_RDONLY
|
|
3539
|
+
if hasattr(os, "O_NONBLOCK"):
|
|
3540
|
+
READ_FLAGS |= os.O_NONBLOCK
|
|
3543
3541
|
if hasattr(os, "O_NOFOLLOW"):
|
|
3544
3542
|
READ_FLAGS |= os.O_NOFOLLOW
|
|
3545
3543
|
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
3546
3544
|
if hasattr(os, "O_NOFOLLOW"):
|
|
3547
3545
|
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
3548
|
-
|
|
3549
3546
|
def split_relative(value):
|
|
3550
3547
|
if value in ("", "."):
|
|
3551
3548
|
return []
|
|
@@ -3558,10 +3555,8 @@ def split_relative(value):
|
|
|
3558
3555
|
if part == "..":
|
|
3559
3556
|
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
3560
3557
|
return parts
|
|
3561
|
-
|
|
3562
3558
|
def open_dir(path_value, dir_fd=None):
|
|
3563
3559
|
return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
|
|
3564
|
-
|
|
3565
3560
|
def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
3566
3561
|
current_fd = os.dup(root_fd)
|
|
3567
3562
|
try:
|
|
@@ -3579,14 +3574,12 @@ def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
|
3579
3574
|
except Exception:
|
|
3580
3575
|
os.close(current_fd)
|
|
3581
3576
|
raise
|
|
3582
|
-
|
|
3583
3577
|
def parent_and_basename(root_fd, relative):
|
|
3584
3578
|
segments = split_relative(relative)
|
|
3585
3579
|
if not segments:
|
|
3586
3580
|
raise OSError(errno.EPERM, "operation requires a non-root path")
|
|
3587
3581
|
parent_fd = walk_dir(root_fd, segments[:-1])
|
|
3588
3582
|
return parent_fd, segments[-1]
|
|
3589
|
-
|
|
3590
3583
|
def encode_stat(st):
|
|
3591
3584
|
mode = st.st_mode
|
|
3592
3585
|
return {
|
|
@@ -3602,14 +3595,108 @@ def encode_stat(st):
|
|
|
3602
3595
|
"size": st.st_size,
|
|
3603
3596
|
"uid": st.st_uid,
|
|
3604
3597
|
}
|
|
3605
|
-
|
|
3606
3598
|
def reject_unsafe_endpoint(st):
|
|
3607
3599
|
mode = st.st_mode
|
|
3608
3600
|
if stat.S_ISLNK(mode):
|
|
3609
3601
|
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
3610
3602
|
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
3611
3603
|
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
3612
|
-
|
|
3604
|
+
def copy_bytes(source_fd, dest_fd):
|
|
3605
|
+
while True:
|
|
3606
|
+
chunk = os.read(source_fd, 65536)
|
|
3607
|
+
if not chunk:
|
|
3608
|
+
break
|
|
3609
|
+
view = memoryview(chunk)
|
|
3610
|
+
while view:
|
|
3611
|
+
written = os.write(dest_fd, view)
|
|
3612
|
+
if written <= 0:
|
|
3613
|
+
raise OSError(errno.EIO, "short write")
|
|
3614
|
+
view = view[written:]
|
|
3615
|
+
def write_all(fd, data):
|
|
3616
|
+
view = memoryview(data)
|
|
3617
|
+
while view:
|
|
3618
|
+
written = os.write(fd, view)
|
|
3619
|
+
if written <= 0:
|
|
3620
|
+
raise OSError(errno.EIO, "short write")
|
|
3621
|
+
view = view[written:]
|
|
3622
|
+
def link_unsupported(exc):
|
|
3623
|
+
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
3624
|
+
return getattr(exc, "errno", None) in unsupported
|
|
3625
|
+
def link_no_replace(name, new_name, source_fd, target_fd):
|
|
3626
|
+
linked = False
|
|
3627
|
+
try:
|
|
3628
|
+
os.link(name, new_name, src_dir_fd=source_fd, dst_dir_fd=target_fd, follow_symlinks=False)
|
|
3629
|
+
linked = True
|
|
3630
|
+
os.unlink(name, dir_fd=source_fd)
|
|
3631
|
+
except Exception:
|
|
3632
|
+
if linked:
|
|
3633
|
+
try: os.unlink(new_name, dir_fd=target_fd)
|
|
3634
|
+
except FileNotFoundError: pass
|
|
3635
|
+
raise
|
|
3636
|
+
os.fsync(source_fd)
|
|
3637
|
+
if source_fd != target_fd:
|
|
3638
|
+
os.fsync(target_fd)
|
|
3639
|
+
def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
|
|
3640
|
+
source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
|
|
3641
|
+
dest_fd = None; success = False; dest_stat = None
|
|
3642
|
+
try:
|
|
3643
|
+
if expected is not None:
|
|
3644
|
+
source_stat = os.fstat(source_fd)
|
|
3645
|
+
if source_stat.st_dev != expected.st_dev or source_stat.st_ino != expected.st_ino:
|
|
3646
|
+
raise RuntimeError("fs-safe-source-mismatch")
|
|
3647
|
+
dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
|
|
3648
|
+
copy_bytes(source_fd, dest_fd)
|
|
3649
|
+
os.fsync(dest_fd)
|
|
3650
|
+
dest_stat = os.fstat(dest_fd)
|
|
3651
|
+
success = True
|
|
3652
|
+
finally:
|
|
3653
|
+
os.close(source_fd)
|
|
3654
|
+
if dest_fd is not None:
|
|
3655
|
+
os.close(dest_fd)
|
|
3656
|
+
if dest_fd is not None and not success:
|
|
3657
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
3658
|
+
except FileNotFoundError: pass
|
|
3659
|
+
if unlink_source:
|
|
3660
|
+
try:
|
|
3661
|
+
os.unlink(source_name, dir_fd=source_parent_fd)
|
|
3662
|
+
except Exception:
|
|
3663
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
3664
|
+
except FileNotFoundError: pass
|
|
3665
|
+
raise
|
|
3666
|
+
return dest_stat
|
|
3667
|
+
def same_identity(left, right):
|
|
3668
|
+
return left.st_dev == right.st_dev and left.st_ino == right.st_ino
|
|
3669
|
+
def verify_temp_name(parent_fd, temp_name, expected_stat):
|
|
3670
|
+
current_stat = os.lstat(temp_name, dir_fd=parent_fd)
|
|
3671
|
+
if stat.S_ISLNK(current_stat.st_mode) or not same_identity(current_stat, expected_stat):
|
|
3672
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
3673
|
+
def verify_committed_temp(parent_fd, basename, expected_stat):
|
|
3674
|
+
final_stat = os.lstat(basename, dir_fd=parent_fd)
|
|
3675
|
+
if not stat.S_ISLNK(final_stat.st_mode) and same_identity(final_stat, expected_stat):
|
|
3676
|
+
return final_stat
|
|
3677
|
+
try: os.unlink(basename, dir_fd=parent_fd)
|
|
3678
|
+
except FileNotFoundError: pass
|
|
3679
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
3680
|
+
def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, expected_stat):
|
|
3681
|
+
verify_temp_name(parent_fd, temp_name, expected_stat)
|
|
3682
|
+
if overwrite:
|
|
3683
|
+
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
3684
|
+
return verify_committed_temp(parent_fd, basename, expected_stat)
|
|
3685
|
+
else:
|
|
3686
|
+
try:
|
|
3687
|
+
os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
|
|
3688
|
+
final_stat = verify_committed_temp(parent_fd, basename, expected_stat)
|
|
3689
|
+
os.unlink(temp_name, dir_fd=parent_fd)
|
|
3690
|
+
return final_stat
|
|
3691
|
+
except OSError as exc:
|
|
3692
|
+
if not link_unsupported(exc):
|
|
3693
|
+
raise
|
|
3694
|
+
return copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, expected_stat, True)
|
|
3695
|
+
def assert_expected_root(root_fd, payload):
|
|
3696
|
+
if "rootDev" in payload or "rootIno" in payload:
|
|
3697
|
+
root_stat = os.fstat(root_fd)
|
|
3698
|
+
if root_stat.st_dev != int(payload["rootDev"]) or root_stat.st_ino != int(payload["rootIno"]):
|
|
3699
|
+
raise RuntimeError("fs-safe-root-mismatch")
|
|
3613
3700
|
def stat_path(root_fd, payload):
|
|
3614
3701
|
relative = payload.get("relativePath", "")
|
|
3615
3702
|
segments = split_relative(relative)
|
|
@@ -3623,7 +3710,6 @@ def stat_path(root_fd, payload):
|
|
|
3623
3710
|
return encode_stat(st)
|
|
3624
3711
|
finally:
|
|
3625
3712
|
os.close(parent_fd)
|
|
3626
|
-
|
|
3627
3713
|
def readdir_path(root_fd, payload):
|
|
3628
3714
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
|
|
3629
3715
|
try:
|
|
@@ -3639,12 +3725,9 @@ def readdir_path(root_fd, payload):
|
|
|
3639
3725
|
return entries
|
|
3640
3726
|
finally:
|
|
3641
3727
|
os.close(dir_fd)
|
|
3642
|
-
|
|
3643
3728
|
def mkdirp_path(root_fd, payload):
|
|
3644
3729
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
|
|
3645
|
-
os.close(dir_fd)
|
|
3646
|
-
return None
|
|
3647
|
-
|
|
3730
|
+
os.close(dir_fd); return None
|
|
3648
3731
|
def remove_tree(parent_fd, basename):
|
|
3649
3732
|
st = os.lstat(basename, dir_fd=parent_fd)
|
|
3650
3733
|
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
@@ -3657,7 +3740,6 @@ def remove_tree(parent_fd, basename):
|
|
|
3657
3740
|
os.rmdir(basename, dir_fd=parent_fd)
|
|
3658
3741
|
else:
|
|
3659
3742
|
os.unlink(basename, dir_fd=parent_fd)
|
|
3660
|
-
|
|
3661
3743
|
def remove_path(root_fd, payload):
|
|
3662
3744
|
parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
|
|
3663
3745
|
try:
|
|
@@ -3684,7 +3766,18 @@ def rename_path(root_fd, payload):
|
|
|
3684
3766
|
try:
|
|
3685
3767
|
from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
|
|
3686
3768
|
reject_unsafe_endpoint(from_stat)
|
|
3687
|
-
|
|
3769
|
+
overwrite = payload.get("overwrite", True)
|
|
3770
|
+
if not overwrite and stat.S_ISREG(from_stat.st_mode):
|
|
3771
|
+
try:
|
|
3772
|
+
link_no_replace(from_base, to_base, from_parent_fd, to_parent_fd)
|
|
3773
|
+
except OSError as exc:
|
|
3774
|
+
if not link_unsupported(exc):
|
|
3775
|
+
raise
|
|
3776
|
+
copy_file_no_replace(from_parent_fd, from_base, to_parent_fd, to_base, stat.S_IMODE(from_stat.st_mode), from_stat, True)
|
|
3777
|
+
return None
|
|
3778
|
+
if not overwrite and stat.S_ISDIR(from_stat.st_mode):
|
|
3779
|
+
raise RuntimeError("fs-safe-directory-noreplace-unsupported")
|
|
3780
|
+
if not overwrite:
|
|
3688
3781
|
try:
|
|
3689
3782
|
os.lstat(to_base, dir_fd=to_parent_fd)
|
|
3690
3783
|
raise FileExistsError(errno.EEXIST, "destination exists", to_base)
|
|
@@ -3729,19 +3822,15 @@ def write_path(root_fd, payload):
|
|
|
3729
3822
|
except FileNotFoundError:
|
|
3730
3823
|
pass
|
|
3731
3824
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
written = os.write(temp_fd, view)
|
|
3735
|
-
if written <= 0:
|
|
3736
|
-
raise OSError(errno.EIO, "short write")
|
|
3737
|
-
view = view[written:]
|
|
3825
|
+
os.fchmod(temp_fd, mode)
|
|
3826
|
+
write_all(temp_fd, data)
|
|
3738
3827
|
os.fsync(temp_fd)
|
|
3828
|
+
temp_stat = os.fstat(temp_fd)
|
|
3739
3829
|
os.close(temp_fd)
|
|
3740
3830
|
temp_fd = None
|
|
3741
|
-
|
|
3831
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
3742
3832
|
temp_name = None
|
|
3743
3833
|
os.fsync(parent_fd)
|
|
3744
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
3745
3834
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
3746
3835
|
finally:
|
|
3747
3836
|
if temp_fd is not None:
|
|
@@ -3771,13 +3860,8 @@ def copy_path(root_fd, payload):
|
|
|
3771
3860
|
if max_bytes >= 0 and source_stat.st_size > max_bytes:
|
|
3772
3861
|
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
3773
3862
|
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
3774
|
-
if not overwrite:
|
|
3775
|
-
try:
|
|
3776
|
-
os.lstat(basename, dir_fd=parent_fd)
|
|
3777
|
-
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
3778
|
-
except FileNotFoundError:
|
|
3779
|
-
pass
|
|
3780
3863
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
3864
|
+
os.fchmod(temp_fd, mode)
|
|
3781
3865
|
written_bytes = 0
|
|
3782
3866
|
while True:
|
|
3783
3867
|
chunk = os.read(source_fd, 65536)
|
|
@@ -3793,12 +3877,12 @@ def copy_path(root_fd, payload):
|
|
|
3793
3877
|
raise OSError(errno.EIO, "short write")
|
|
3794
3878
|
view = view[written:]
|
|
3795
3879
|
os.fsync(temp_fd)
|
|
3880
|
+
temp_stat = os.fstat(temp_fd)
|
|
3796
3881
|
os.close(temp_fd)
|
|
3797
3882
|
temp_fd = None
|
|
3798
|
-
|
|
3883
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
3799
3884
|
temp_name = None
|
|
3800
3885
|
os.fsync(parent_fd)
|
|
3801
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
3802
3886
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
3803
3887
|
finally:
|
|
3804
3888
|
os.close(source_fd)
|
|
@@ -3815,6 +3899,7 @@ def copy_path(root_fd, payload):
|
|
|
3815
3899
|
def run_operation(operation, root_path, payload):
|
|
3816
3900
|
root_fd = open_dir(root_path)
|
|
3817
3901
|
try:
|
|
3902
|
+
assert_expected_root(root_fd, payload)
|
|
3818
3903
|
if operation == "stat":
|
|
3819
3904
|
return stat_path(root_fd, payload)
|
|
3820
3905
|
if operation == "readdir":
|
|
@@ -3919,6 +4004,15 @@ function mapWorkerError(response) {
|
|
|
3919
4004
|
if (message.includes("fs-safe-source-mismatch")) {
|
|
3920
4005
|
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
3921
4006
|
}
|
|
4007
|
+
if (message.includes("fs-safe-temp-mismatch")) {
|
|
4008
|
+
return new errors_FsSafeError("path-mismatch", "temp path changed during write");
|
|
4009
|
+
}
|
|
4010
|
+
if (message.includes("fs-safe-root-mismatch")) {
|
|
4011
|
+
return new errors_FsSafeError("path-mismatch", "root path changed during operation");
|
|
4012
|
+
}
|
|
4013
|
+
if (message.includes("fs-safe-directory-noreplace-unsupported")) {
|
|
4014
|
+
return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
4015
|
+
}
|
|
3922
4016
|
if (code === "FileNotFoundError" || errno === 2) {
|
|
3923
4017
|
return new errors_FsSafeError("not-found", "file not found");
|
|
3924
4018
|
}
|
|
@@ -3936,19 +4030,19 @@ function mapWorkerError(response) {
|
|
|
3936
4030
|
}
|
|
3937
4031
|
return new errors_FsSafeError("helper-failed", message);
|
|
3938
4032
|
}
|
|
3939
|
-
function rejectPending(error) {
|
|
3940
|
-
if (!worker) {
|
|
4033
|
+
function rejectPending(error, targetWorker = worker) {
|
|
4034
|
+
if (!targetWorker || worker !== targetWorker) {
|
|
3941
4035
|
return;
|
|
3942
4036
|
}
|
|
3943
|
-
setWorkerRef(
|
|
3944
|
-
for (const pending of
|
|
4037
|
+
setWorkerRef(targetWorker, false);
|
|
4038
|
+
for (const pending of targetWorker.pending.values()) {
|
|
3945
4039
|
pending.reject(error);
|
|
3946
4040
|
}
|
|
3947
|
-
|
|
4041
|
+
targetWorker.pending.clear();
|
|
3948
4042
|
worker = null;
|
|
3949
4043
|
}
|
|
3950
|
-
function handleWorkerLine(line) {
|
|
3951
|
-
if (
|
|
4044
|
+
function handleWorkerLine(currentWorker, line) {
|
|
4045
|
+
if (worker !== currentWorker || !line.trim()) {
|
|
3952
4046
|
return;
|
|
3953
4047
|
}
|
|
3954
4048
|
let decoded;
|
|
@@ -3956,11 +4050,11 @@ function handleWorkerLine(line) {
|
|
|
3956
4050
|
decoded = JSON.parse(line);
|
|
3957
4051
|
}
|
|
3958
4052
|
catch {
|
|
3959
|
-
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
|
|
4053
|
+
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`), currentWorker);
|
|
3960
4054
|
return;
|
|
3961
4055
|
}
|
|
3962
4056
|
if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
|
|
3963
|
-
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
|
|
4057
|
+
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"), currentWorker);
|
|
3964
4058
|
return;
|
|
3965
4059
|
}
|
|
3966
4060
|
const response = decoded;
|
|
@@ -3968,13 +4062,13 @@ function handleWorkerLine(line) {
|
|
|
3968
4062
|
if (id === undefined) {
|
|
3969
4063
|
return;
|
|
3970
4064
|
}
|
|
3971
|
-
const pending =
|
|
4065
|
+
const pending = currentWorker.pending.get(id);
|
|
3972
4066
|
if (!pending) {
|
|
3973
4067
|
return;
|
|
3974
4068
|
}
|
|
3975
|
-
|
|
3976
|
-
if (
|
|
3977
|
-
setWorkerRef(
|
|
4069
|
+
currentWorker.pending.delete(id);
|
|
4070
|
+
if (currentWorker.pending.size === 0) {
|
|
4071
|
+
setWorkerRef(currentWorker, false);
|
|
3978
4072
|
}
|
|
3979
4073
|
if (response.ok === true) {
|
|
3980
4074
|
pending.resolve(response.result);
|
|
@@ -3990,28 +4084,28 @@ function getWorker() {
|
|
|
3990
4084
|
const child = (0,external_node_child_process_.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
|
|
3991
4085
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3992
4086
|
});
|
|
3993
|
-
|
|
4087
|
+
const currentWorker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
|
|
4088
|
+
worker = currentWorker;
|
|
3994
4089
|
child.stdout.setEncoding("utf8");
|
|
3995
4090
|
child.stderr.setEncoding("utf8");
|
|
3996
4091
|
child.stdout.on("data", (chunk) => {
|
|
3997
|
-
|
|
3998
|
-
if (!current) {
|
|
4092
|
+
if (worker !== currentWorker) {
|
|
3999
4093
|
return;
|
|
4000
4094
|
}
|
|
4001
|
-
|
|
4095
|
+
currentWorker.stdoutBuffer += chunk;
|
|
4002
4096
|
for (;;) {
|
|
4003
|
-
const newline =
|
|
4097
|
+
const newline = currentWorker.stdoutBuffer.indexOf("\n");
|
|
4004
4098
|
if (newline < 0) {
|
|
4005
4099
|
break;
|
|
4006
4100
|
}
|
|
4007
|
-
const line =
|
|
4008
|
-
|
|
4009
|
-
handleWorkerLine(line);
|
|
4101
|
+
const line = currentWorker.stdoutBuffer.slice(0, newline);
|
|
4102
|
+
currentWorker.stdoutBuffer = currentWorker.stdoutBuffer.slice(newline + 1);
|
|
4103
|
+
handleWorkerLine(currentWorker, line);
|
|
4010
4104
|
}
|
|
4011
4105
|
});
|
|
4012
4106
|
child.stderr.on("data", (chunk) => {
|
|
4013
|
-
if (worker) {
|
|
4014
|
-
|
|
4107
|
+
if (worker === currentWorker) {
|
|
4108
|
+
currentWorker.stderr = `${currentWorker.stderr}${chunk}`.slice(-4096);
|
|
4015
4109
|
}
|
|
4016
4110
|
});
|
|
4017
4111
|
child.once("error", (error) => {
|
|
@@ -4020,17 +4114,17 @@ function getWorker() {
|
|
|
4020
4114
|
: error instanceof Error
|
|
4021
4115
|
? error
|
|
4022
4116
|
: new Error(String(error));
|
|
4023
|
-
rejectPending(mapped);
|
|
4117
|
+
rejectPending(mapped, currentWorker);
|
|
4024
4118
|
});
|
|
4025
4119
|
child.once("close", (code, signal) => {
|
|
4026
|
-
const stderr =
|
|
4027
|
-
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
|
|
4120
|
+
const stderr = currentWorker.stderr.trim();
|
|
4121
|
+
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`), currentWorker);
|
|
4028
4122
|
});
|
|
4029
4123
|
process.once("exit", () => {
|
|
4030
4124
|
child.kill("SIGTERM");
|
|
4031
4125
|
});
|
|
4032
|
-
setWorkerRef(
|
|
4033
|
-
return
|
|
4126
|
+
setWorkerRef(currentWorker, false);
|
|
4127
|
+
return currentWorker;
|
|
4034
4128
|
}
|
|
4035
4129
|
function setRefable(value, ref) {
|
|
4036
4130
|
if (!value) {
|
|
@@ -4096,9 +4190,7 @@ function validatePinnedOperationPayload(payload) {
|
|
|
4096
4190
|
}
|
|
4097
4191
|
}
|
|
4098
4192
|
function isPinnedHelperUnavailable(error) {
|
|
4099
|
-
return error instanceof Error &&
|
|
4100
|
-
"code" in error &&
|
|
4101
|
-
error.code === "helper-unavailable";
|
|
4193
|
+
return error instanceof Error && "code" in error && error.code === "helper-unavailable";
|
|
4102
4194
|
}
|
|
4103
4195
|
function validatePinnedRelativePath(relativePath) {
|
|
4104
4196
|
if (relativePath.length === 0 || relativePath === ".") {
|
|
@@ -4196,29 +4288,21 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
4196
4288
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
4197
4289
|
}
|
|
4198
4290
|
}
|
|
4199
|
-
function
|
|
4200
|
-
if (maxBytes === undefined) {
|
|
4201
|
-
return undefined;
|
|
4202
|
-
}
|
|
4291
|
+
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
4203
4292
|
let bytes = 0;
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4293
|
+
for await (const chunk of stream) {
|
|
4294
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4295
|
+
bytes += buffer.byteLength;
|
|
4296
|
+
assertWithinMaxBytes(bytes, maxBytes);
|
|
4297
|
+
let offset = 0;
|
|
4298
|
+
while (offset < buffer.byteLength) {
|
|
4299
|
+
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
|
|
4300
|
+
if (bytesWritten <= 0) {
|
|
4301
|
+
throw new errors_FsSafeError("helper-failed", "fallback stream write made no progress");
|
|
4210
4302
|
}
|
|
4211
|
-
|
|
4212
|
-
}
|
|
4213
|
-
});
|
|
4214
|
-
}
|
|
4215
|
-
async function pipelineWithMaxBytes(stream, destination, maxBytes) {
|
|
4216
|
-
const limiter = pinned_write_createMaxBytesTransform(maxBytes);
|
|
4217
|
-
if (limiter) {
|
|
4218
|
-
await (0,external_node_stream_promises_.pipeline)(stream, limiter, destination);
|
|
4219
|
-
return;
|
|
4303
|
+
offset += bytesWritten;
|
|
4304
|
+
}
|
|
4220
4305
|
}
|
|
4221
|
-
await (0,external_node_stream_promises_.pipeline)(stream, destination);
|
|
4222
4306
|
}
|
|
4223
4307
|
async function inputToBase64(input, maxBytes) {
|
|
4224
4308
|
if (input.kind === "buffer") {
|
|
@@ -4243,7 +4327,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
4243
4327
|
relativeParentPath: params.relativeParentPath,
|
|
4244
4328
|
});
|
|
4245
4329
|
if (getFsSafePythonConfig().mode === "off") {
|
|
4246
|
-
return await
|
|
4330
|
+
return await runPinnedWriteFallbackOrThrow(params);
|
|
4247
4331
|
}
|
|
4248
4332
|
if (params.input.kind === "stream") {
|
|
4249
4333
|
try {
|
|
@@ -4251,7 +4335,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
4251
4335
|
}
|
|
4252
4336
|
catch (error) {
|
|
4253
4337
|
if (canFallbackFromPythonError(error)) {
|
|
4254
|
-
return await
|
|
4338
|
+
return await runPinnedWriteFallbackOrThrow(params, error);
|
|
4255
4339
|
}
|
|
4256
4340
|
throw error;
|
|
4257
4341
|
}
|
|
@@ -4264,6 +4348,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
4264
4348
|
mode: params.mode || 0o600,
|
|
4265
4349
|
overwrite: params.overwrite !== false,
|
|
4266
4350
|
relativeParentPath: params.relativeParentPath,
|
|
4351
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
4267
4352
|
};
|
|
4268
4353
|
try {
|
|
4269
4354
|
return await runPinnedPythonOperation({
|
|
@@ -4274,7 +4359,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
4274
4359
|
}
|
|
4275
4360
|
catch (error) {
|
|
4276
4361
|
if (canFallbackFromPythonError(error)) {
|
|
4277
|
-
return await
|
|
4362
|
+
return await runPinnedWriteFallbackOrThrow(params, error);
|
|
4278
4363
|
}
|
|
4279
4364
|
throw error;
|
|
4280
4365
|
}
|
|
@@ -4294,22 +4379,29 @@ async function runPinnedCopyHelper(params) {
|
|
|
4294
4379
|
mode: params.mode || 0o600,
|
|
4295
4380
|
overwrite: params.overwrite !== false,
|
|
4296
4381
|
relativeParentPath: params.relativeParentPath,
|
|
4382
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
4297
4383
|
sourceDev: params.sourceIdentity.dev,
|
|
4298
4384
|
sourceIno: params.sourceIdentity.ino,
|
|
4299
4385
|
sourcePath: params.sourcePath,
|
|
4300
4386
|
},
|
|
4301
4387
|
});
|
|
4302
4388
|
}
|
|
4389
|
+
async function runPinnedWriteFallbackOrThrow(params, cause) {
|
|
4390
|
+
if (process.platform !== "win32") {
|
|
4391
|
+
throw new errors_FsSafeError("helper-unavailable", "Python helper is required for pinned writes on this platform", { cause });
|
|
4392
|
+
}
|
|
4393
|
+
return await runPinnedWriteFallback(params);
|
|
4394
|
+
}
|
|
4303
4395
|
async function runPinnedWriteFallback(params) {
|
|
4304
4396
|
const parentPath = params.relativeParentPath
|
|
4305
4397
|
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
4306
4398
|
: params.rootPath;
|
|
4307
|
-
const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
4308
4399
|
if (params.mkdir) {
|
|
4309
|
-
await
|
|
4310
|
-
await promises_.mkdir(parentPath, { recursive: true });
|
|
4311
|
-
});
|
|
4400
|
+
await mkdirPathComponentsWithGuards({ rootReal: params.rootPath, targetPath: parentPath });
|
|
4312
4401
|
}
|
|
4402
|
+
const parentGuard = params.mkdir
|
|
4403
|
+
? await directory_guard_createAsyncDirectoryGuard(parentPath)
|
|
4404
|
+
: await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
4313
4405
|
const targetPath = external_node_path_.join(parentPath, params.basename);
|
|
4314
4406
|
if (params.overwrite === false) {
|
|
4315
4407
|
let handle = await withAsyncDirectoryGuards([parentGuard], async () => await promises_.open(targetPath, external_node_fs_.constants.O_WRONLY | external_node_fs_.constants.O_CREAT | external_node_fs_.constants.O_EXCL, params.mode), {
|
|
@@ -4331,7 +4423,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
4331
4423
|
}
|
|
4332
4424
|
}
|
|
4333
4425
|
else {
|
|
4334
|
-
await
|
|
4426
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
4335
4427
|
}
|
|
4336
4428
|
const stat = await handle.stat();
|
|
4337
4429
|
created = false;
|
|
@@ -4352,7 +4444,9 @@ async function runPinnedWriteFallback(params) {
|
|
|
4352
4444
|
? external_node_fs_.constants.O_NOFOLLOW
|
|
4353
4445
|
: 0);
|
|
4354
4446
|
let handle;
|
|
4355
|
-
let
|
|
4447
|
+
let tempStat;
|
|
4448
|
+
let targetStat;
|
|
4449
|
+
let renamed = false;
|
|
4356
4450
|
try {
|
|
4357
4451
|
handle = await promises_.open(tempPath, tempFlags, params.mode);
|
|
4358
4452
|
if (params.input.kind === "buffer") {
|
|
@@ -4365,29 +4459,36 @@ async function runPinnedWriteFallback(params) {
|
|
|
4365
4459
|
}
|
|
4366
4460
|
}
|
|
4367
4461
|
else {
|
|
4368
|
-
|
|
4369
|
-
writable.once("close", () => {
|
|
4370
|
-
handleClosedByStream = true;
|
|
4371
|
-
});
|
|
4372
|
-
await pipelineWithMaxBytes(params.input.stream, writable, params.maxBytes);
|
|
4462
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
4373
4463
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4464
|
+
tempStat = await handle.stat();
|
|
4465
|
+
const tempPathStat = await promises_.lstat(tempPath);
|
|
4466
|
+
if (tempPathStat.isSymbolicLink() || !file_identity_sameFileIdentity(tempPathStat, tempStat)) {
|
|
4467
|
+
throw new errors_FsSafeError("path-mismatch", "fallback temp path changed during write");
|
|
4377
4468
|
}
|
|
4469
|
+
const expectedTempStat = tempStat;
|
|
4470
|
+
await handle.close().catch(() => undefined);
|
|
4471
|
+
handle = undefined;
|
|
4378
4472
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
4379
4473
|
await promises_.rename(tempPath, targetPath);
|
|
4474
|
+
renamed = true;
|
|
4475
|
+
targetStat = await promises_.lstat(targetPath);
|
|
4476
|
+
if (targetStat.isSymbolicLink() || !file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
|
|
4477
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
4478
|
+
}
|
|
4380
4479
|
});
|
|
4381
4480
|
}
|
|
4382
4481
|
catch (error) {
|
|
4383
|
-
|
|
4384
|
-
|
|
4482
|
+
await handle?.close().catch(() => undefined);
|
|
4483
|
+
if (!renamed) {
|
|
4484
|
+
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
4385
4485
|
}
|
|
4386
|
-
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
4387
4486
|
throw error;
|
|
4388
4487
|
}
|
|
4389
|
-
|
|
4390
|
-
|
|
4488
|
+
if (!targetStat) {
|
|
4489
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target was not verified");
|
|
4490
|
+
}
|
|
4491
|
+
return { dev: targetStat.dev, ino: targetStat.ino };
|
|
4391
4492
|
}
|
|
4392
4493
|
|
|
4393
4494
|
// EXTERNAL MODULE: external "node:os"
|
|
@@ -4926,7 +5027,7 @@ function relativeInsideRoot(rootPath, targetPath) {
|
|
|
4926
5027
|
if (!relative || relative === ".") {
|
|
4927
5028
|
return "";
|
|
4928
5029
|
}
|
|
4929
|
-
if (
|
|
5030
|
+
if (isPathRelativeEscape(relative)) {
|
|
4930
5031
|
return "";
|
|
4931
5032
|
}
|
|
4932
5033
|
return relative;
|
|
@@ -5277,6 +5378,15 @@ function __setFsSafeTestHooksForTest(hooks) {
|
|
|
5277
5378
|
test_hooks_fsSafeTestHooks = hooks;
|
|
5278
5379
|
}
|
|
5279
5380
|
|
|
5381
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
|
|
5382
|
+
function stringifyJsonDocument(value, replacer, space) {
|
|
5383
|
+
const text = JSON.stringify(value, replacer, space);
|
|
5384
|
+
if (typeof text !== "string") {
|
|
5385
|
+
throw new TypeError("value is not representable as a JSON document");
|
|
5386
|
+
}
|
|
5387
|
+
return text;
|
|
5388
|
+
}
|
|
5389
|
+
|
|
5280
5390
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
|
|
5281
5391
|
|
|
5282
5392
|
const tempCleanupEntries = new Map();
|
|
@@ -5364,6 +5474,7 @@ async function serializePathWrite(key, run) {
|
|
|
5364
5474
|
|
|
5365
5475
|
|
|
5366
5476
|
|
|
5477
|
+
|
|
5367
5478
|
|
|
5368
5479
|
|
|
5369
5480
|
function logWarn(message) {
|
|
@@ -5604,12 +5715,12 @@ class RootHandle {
|
|
|
5604
5715
|
}
|
|
5605
5716
|
async writeJson(relativePath, data, options = {}) {
|
|
5606
5717
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
5607
|
-
const json =
|
|
5718
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
5608
5719
|
await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
5609
5720
|
}
|
|
5610
5721
|
async createJson(relativePath, data, options = {}) {
|
|
5611
5722
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
5612
|
-
const json =
|
|
5723
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
5613
5724
|
await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
5614
5725
|
}
|
|
5615
5726
|
async copyIn(relativePath, sourcePath, options = {}) {
|
|
@@ -6394,6 +6505,9 @@ async function movePathFallback(root, params) {
|
|
|
6394
6505
|
if (sourceStat.isFile() && sourceStat.nlink > 1) {
|
|
6395
6506
|
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
6396
6507
|
}
|
|
6508
|
+
if (!params.overwrite && sourceStat.isDirectory()) {
|
|
6509
|
+
throw new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
6510
|
+
}
|
|
6397
6511
|
if (!params.overwrite) {
|
|
6398
6512
|
try {
|
|
6399
6513
|
await promises_.lstat(target.resolved);
|
|
@@ -59042,4 +59156,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
59042
59156
|
// module factories are used so entry inlining is disabled
|
|
59043
59157
|
// startup
|
|
59044
59158
|
// Load entry module and return exports
|
|
59045
|
-
var __webpack_exports__ = __webpack_require__(
|
|
59159
|
+
var __webpack_exports__ = __webpack_require__(1809);
|
package/package.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@biomejs/biome": "2.4.15",
|
|
25
25
|
"@eslint/eslintrc": "3.3.5",
|
|
26
|
-
"@lousy-agents/mcp": "5.14.
|
|
26
|
+
"@lousy-agents/mcp": "5.14.8",
|
|
27
27
|
"@modelcontextprotocol/server-sequential-thinking": "2025.12.18",
|
|
28
28
|
"@testing-library/jest-dom": "6.9.1",
|
|
29
29
|
"@testing-library/react": "16.3.2",
|