@lousy-agents/lint 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.
- package/dist/index.js +237 -123
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -566,7 +566,7 @@ module.exports = webpackEmptyAsyncContext;
|
|
|
566
566
|
|
|
567
567
|
|
|
568
568
|
},
|
|
569
|
-
|
|
569
|
+
4403(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
|
|
570
570
|
|
|
571
571
|
// EXPORTS
|
|
572
572
|
__webpack_require__.d(__webpack_exports__, {
|
|
@@ -1031,6 +1031,9 @@ function path_isPathInside(root, target) {
|
|
|
1031
1031
|
const firstSegment = relative.split(external_node_path_.posix.sep)[0];
|
|
1032
1032
|
return relative === "" || (firstSegment !== ".." && !external_node_path_.isAbsolute(relative));
|
|
1033
1033
|
}
|
|
1034
|
+
function isPathRelativeEscape(relativePath) {
|
|
1035
|
+
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
1036
|
+
}
|
|
1034
1037
|
function resolveSafeBaseDir(rootDir) {
|
|
1035
1038
|
const resolved = path.resolve(rootDir);
|
|
1036
1039
|
return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
|
|
@@ -1096,7 +1099,7 @@ function splitSafeRelativePath(relativePath) {
|
|
|
1096
1099
|
function resolveSafeRelativePath(rootDir, relativePath) {
|
|
1097
1100
|
const root = path.resolve(rootDir);
|
|
1098
1101
|
const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
|
|
1099
|
-
if (
|
|
1102
|
+
if (!path_isPathInside(root, target)) {
|
|
1100
1103
|
throw new FsSafeError("outside-workspace", "relative path escapes root");
|
|
1101
1104
|
}
|
|
1102
1105
|
return target;
|
|
@@ -1179,17 +1182,17 @@ function directory_guard_createNearestExistingSyncDirectoryGuard(rootReal, targe
|
|
|
1179
1182
|
|
|
1180
1183
|
|
|
1181
1184
|
|
|
1185
|
+
|
|
1182
1186
|
function isSameOrChildPath(candidate, parent) {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
function isPathEscape(relativePath) {
|
|
1186
|
-
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
1187
|
+
const parentPrefix = parent.endsWith(external_node_path_.sep) ? parent : `${parent}${external_node_path_.sep}`;
|
|
1188
|
+
return candidate === parent || candidate.startsWith(parentPrefix);
|
|
1187
1189
|
}
|
|
1188
1190
|
async function mkdirPathComponentsWithGuards(params) {
|
|
1189
1191
|
const root = external_node_path_.resolve(params.rootReal);
|
|
1192
|
+
const rootCanonical = external_node_path_.resolve(await promises_.realpath(root));
|
|
1190
1193
|
const target = external_node_path_.resolve(params.targetPath);
|
|
1191
1194
|
const relative = external_node_path_.relative(root, target);
|
|
1192
|
-
if (
|
|
1195
|
+
if (isPathRelativeEscape(relative)) {
|
|
1193
1196
|
throw new errors_FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
1194
1197
|
}
|
|
1195
1198
|
let current = root;
|
|
@@ -1212,7 +1215,7 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
1212
1215
|
}
|
|
1213
1216
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
1214
1217
|
// segment at a time and realpath-check each segment before descending.
|
|
1215
|
-
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)),
|
|
1218
|
+
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)), rootCanonical)) {
|
|
1216
1219
|
throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
|
|
1217
1220
|
}
|
|
1218
1221
|
await directory_guard_createAsyncDirectoryGuard(next);
|
|
@@ -1347,26 +1350,20 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
1347
1350
|
|
|
1348
1351
|
|
|
1349
1352
|
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
1350
|
-
import base64
|
|
1351
|
-
import errno
|
|
1352
|
-
import json
|
|
1353
|
-
import os
|
|
1354
|
-
import secrets
|
|
1355
|
-
import stat
|
|
1356
|
-
import sys
|
|
1357
|
-
|
|
1353
|
+
import base64, errno, json, os, secrets, stat, sys
|
|
1358
1354
|
DIR_FLAGS = os.O_RDONLY
|
|
1359
1355
|
if hasattr(os, "O_DIRECTORY"):
|
|
1360
1356
|
DIR_FLAGS |= os.O_DIRECTORY
|
|
1361
1357
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1362
1358
|
DIR_FLAGS |= os.O_NOFOLLOW
|
|
1363
1359
|
READ_FLAGS = os.O_RDONLY
|
|
1360
|
+
if hasattr(os, "O_NONBLOCK"):
|
|
1361
|
+
READ_FLAGS |= os.O_NONBLOCK
|
|
1364
1362
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1365
1363
|
READ_FLAGS |= os.O_NOFOLLOW
|
|
1366
1364
|
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
1367
1365
|
if hasattr(os, "O_NOFOLLOW"):
|
|
1368
1366
|
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
1369
|
-
|
|
1370
1367
|
def split_relative(value):
|
|
1371
1368
|
if value in ("", "."):
|
|
1372
1369
|
return []
|
|
@@ -1379,10 +1376,8 @@ def split_relative(value):
|
|
|
1379
1376
|
if part == "..":
|
|
1380
1377
|
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
1381
1378
|
return parts
|
|
1382
|
-
|
|
1383
1379
|
def open_dir(path_value, dir_fd=None):
|
|
1384
1380
|
return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
|
|
1385
|
-
|
|
1386
1381
|
def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
1387
1382
|
current_fd = os.dup(root_fd)
|
|
1388
1383
|
try:
|
|
@@ -1400,14 +1395,12 @@ def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
|
1400
1395
|
except Exception:
|
|
1401
1396
|
os.close(current_fd)
|
|
1402
1397
|
raise
|
|
1403
|
-
|
|
1404
1398
|
def parent_and_basename(root_fd, relative):
|
|
1405
1399
|
segments = split_relative(relative)
|
|
1406
1400
|
if not segments:
|
|
1407
1401
|
raise OSError(errno.EPERM, "operation requires a non-root path")
|
|
1408
1402
|
parent_fd = walk_dir(root_fd, segments[:-1])
|
|
1409
1403
|
return parent_fd, segments[-1]
|
|
1410
|
-
|
|
1411
1404
|
def encode_stat(st):
|
|
1412
1405
|
mode = st.st_mode
|
|
1413
1406
|
return {
|
|
@@ -1423,14 +1416,108 @@ def encode_stat(st):
|
|
|
1423
1416
|
"size": st.st_size,
|
|
1424
1417
|
"uid": st.st_uid,
|
|
1425
1418
|
}
|
|
1426
|
-
|
|
1427
1419
|
def reject_unsafe_endpoint(st):
|
|
1428
1420
|
mode = st.st_mode
|
|
1429
1421
|
if stat.S_ISLNK(mode):
|
|
1430
1422
|
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
1431
1423
|
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
1432
1424
|
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
1433
|
-
|
|
1425
|
+
def copy_bytes(source_fd, dest_fd):
|
|
1426
|
+
while True:
|
|
1427
|
+
chunk = os.read(source_fd, 65536)
|
|
1428
|
+
if not chunk:
|
|
1429
|
+
break
|
|
1430
|
+
view = memoryview(chunk)
|
|
1431
|
+
while view:
|
|
1432
|
+
written = os.write(dest_fd, view)
|
|
1433
|
+
if written <= 0:
|
|
1434
|
+
raise OSError(errno.EIO, "short write")
|
|
1435
|
+
view = view[written:]
|
|
1436
|
+
def write_all(fd, data):
|
|
1437
|
+
view = memoryview(data)
|
|
1438
|
+
while view:
|
|
1439
|
+
written = os.write(fd, view)
|
|
1440
|
+
if written <= 0:
|
|
1441
|
+
raise OSError(errno.EIO, "short write")
|
|
1442
|
+
view = view[written:]
|
|
1443
|
+
def link_unsupported(exc):
|
|
1444
|
+
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
1445
|
+
return getattr(exc, "errno", None) in unsupported
|
|
1446
|
+
def link_no_replace(name, new_name, source_fd, target_fd):
|
|
1447
|
+
linked = False
|
|
1448
|
+
try:
|
|
1449
|
+
os.link(name, new_name, src_dir_fd=source_fd, dst_dir_fd=target_fd, follow_symlinks=False)
|
|
1450
|
+
linked = True
|
|
1451
|
+
os.unlink(name, dir_fd=source_fd)
|
|
1452
|
+
except Exception:
|
|
1453
|
+
if linked:
|
|
1454
|
+
try: os.unlink(new_name, dir_fd=target_fd)
|
|
1455
|
+
except FileNotFoundError: pass
|
|
1456
|
+
raise
|
|
1457
|
+
os.fsync(source_fd)
|
|
1458
|
+
if source_fd != target_fd:
|
|
1459
|
+
os.fsync(target_fd)
|
|
1460
|
+
def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
|
|
1461
|
+
source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
|
|
1462
|
+
dest_fd = None; success = False; dest_stat = None
|
|
1463
|
+
try:
|
|
1464
|
+
if expected is not None:
|
|
1465
|
+
source_stat = os.fstat(source_fd)
|
|
1466
|
+
if source_stat.st_dev != expected.st_dev or source_stat.st_ino != expected.st_ino:
|
|
1467
|
+
raise RuntimeError("fs-safe-source-mismatch")
|
|
1468
|
+
dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
|
|
1469
|
+
copy_bytes(source_fd, dest_fd)
|
|
1470
|
+
os.fsync(dest_fd)
|
|
1471
|
+
dest_stat = os.fstat(dest_fd)
|
|
1472
|
+
success = True
|
|
1473
|
+
finally:
|
|
1474
|
+
os.close(source_fd)
|
|
1475
|
+
if dest_fd is not None:
|
|
1476
|
+
os.close(dest_fd)
|
|
1477
|
+
if dest_fd is not None and not success:
|
|
1478
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
1479
|
+
except FileNotFoundError: pass
|
|
1480
|
+
if unlink_source:
|
|
1481
|
+
try:
|
|
1482
|
+
os.unlink(source_name, dir_fd=source_parent_fd)
|
|
1483
|
+
except Exception:
|
|
1484
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
1485
|
+
except FileNotFoundError: pass
|
|
1486
|
+
raise
|
|
1487
|
+
return dest_stat
|
|
1488
|
+
def same_identity(left, right):
|
|
1489
|
+
return left.st_dev == right.st_dev and left.st_ino == right.st_ino
|
|
1490
|
+
def verify_temp_name(parent_fd, temp_name, expected_stat):
|
|
1491
|
+
current_stat = os.lstat(temp_name, dir_fd=parent_fd)
|
|
1492
|
+
if stat.S_ISLNK(current_stat.st_mode) or not same_identity(current_stat, expected_stat):
|
|
1493
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
1494
|
+
def verify_committed_temp(parent_fd, basename, expected_stat):
|
|
1495
|
+
final_stat = os.lstat(basename, dir_fd=parent_fd)
|
|
1496
|
+
if not stat.S_ISLNK(final_stat.st_mode) and same_identity(final_stat, expected_stat):
|
|
1497
|
+
return final_stat
|
|
1498
|
+
try: os.unlink(basename, dir_fd=parent_fd)
|
|
1499
|
+
except FileNotFoundError: pass
|
|
1500
|
+
raise RuntimeError("fs-safe-temp-mismatch")
|
|
1501
|
+
def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, expected_stat):
|
|
1502
|
+
verify_temp_name(parent_fd, temp_name, expected_stat)
|
|
1503
|
+
if overwrite:
|
|
1504
|
+
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
1505
|
+
return verify_committed_temp(parent_fd, basename, expected_stat)
|
|
1506
|
+
else:
|
|
1507
|
+
try:
|
|
1508
|
+
os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
|
|
1509
|
+
final_stat = verify_committed_temp(parent_fd, basename, expected_stat)
|
|
1510
|
+
os.unlink(temp_name, dir_fd=parent_fd)
|
|
1511
|
+
return final_stat
|
|
1512
|
+
except OSError as exc:
|
|
1513
|
+
if not link_unsupported(exc):
|
|
1514
|
+
raise
|
|
1515
|
+
return copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, expected_stat, True)
|
|
1516
|
+
def assert_expected_root(root_fd, payload):
|
|
1517
|
+
if "rootDev" in payload or "rootIno" in payload:
|
|
1518
|
+
root_stat = os.fstat(root_fd)
|
|
1519
|
+
if root_stat.st_dev != int(payload["rootDev"]) or root_stat.st_ino != int(payload["rootIno"]):
|
|
1520
|
+
raise RuntimeError("fs-safe-root-mismatch")
|
|
1434
1521
|
def stat_path(root_fd, payload):
|
|
1435
1522
|
relative = payload.get("relativePath", "")
|
|
1436
1523
|
segments = split_relative(relative)
|
|
@@ -1444,7 +1531,6 @@ def stat_path(root_fd, payload):
|
|
|
1444
1531
|
return encode_stat(st)
|
|
1445
1532
|
finally:
|
|
1446
1533
|
os.close(parent_fd)
|
|
1447
|
-
|
|
1448
1534
|
def readdir_path(root_fd, payload):
|
|
1449
1535
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
|
|
1450
1536
|
try:
|
|
@@ -1460,12 +1546,9 @@ def readdir_path(root_fd, payload):
|
|
|
1460
1546
|
return entries
|
|
1461
1547
|
finally:
|
|
1462
1548
|
os.close(dir_fd)
|
|
1463
|
-
|
|
1464
1549
|
def mkdirp_path(root_fd, payload):
|
|
1465
1550
|
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
|
|
1466
|
-
os.close(dir_fd)
|
|
1467
|
-
return None
|
|
1468
|
-
|
|
1551
|
+
os.close(dir_fd); return None
|
|
1469
1552
|
def remove_tree(parent_fd, basename):
|
|
1470
1553
|
st = os.lstat(basename, dir_fd=parent_fd)
|
|
1471
1554
|
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
@@ -1478,7 +1561,6 @@ def remove_tree(parent_fd, basename):
|
|
|
1478
1561
|
os.rmdir(basename, dir_fd=parent_fd)
|
|
1479
1562
|
else:
|
|
1480
1563
|
os.unlink(basename, dir_fd=parent_fd)
|
|
1481
|
-
|
|
1482
1564
|
def remove_path(root_fd, payload):
|
|
1483
1565
|
parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
|
|
1484
1566
|
try:
|
|
@@ -1505,7 +1587,18 @@ def rename_path(root_fd, payload):
|
|
|
1505
1587
|
try:
|
|
1506
1588
|
from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
|
|
1507
1589
|
reject_unsafe_endpoint(from_stat)
|
|
1508
|
-
|
|
1590
|
+
overwrite = payload.get("overwrite", True)
|
|
1591
|
+
if not overwrite and stat.S_ISREG(from_stat.st_mode):
|
|
1592
|
+
try:
|
|
1593
|
+
link_no_replace(from_base, to_base, from_parent_fd, to_parent_fd)
|
|
1594
|
+
except OSError as exc:
|
|
1595
|
+
if not link_unsupported(exc):
|
|
1596
|
+
raise
|
|
1597
|
+
copy_file_no_replace(from_parent_fd, from_base, to_parent_fd, to_base, stat.S_IMODE(from_stat.st_mode), from_stat, True)
|
|
1598
|
+
return None
|
|
1599
|
+
if not overwrite and stat.S_ISDIR(from_stat.st_mode):
|
|
1600
|
+
raise RuntimeError("fs-safe-directory-noreplace-unsupported")
|
|
1601
|
+
if not overwrite:
|
|
1509
1602
|
try:
|
|
1510
1603
|
os.lstat(to_base, dir_fd=to_parent_fd)
|
|
1511
1604
|
raise FileExistsError(errno.EEXIST, "destination exists", to_base)
|
|
@@ -1550,19 +1643,15 @@ def write_path(root_fd, payload):
|
|
|
1550
1643
|
except FileNotFoundError:
|
|
1551
1644
|
pass
|
|
1552
1645
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
written = os.write(temp_fd, view)
|
|
1556
|
-
if written <= 0:
|
|
1557
|
-
raise OSError(errno.EIO, "short write")
|
|
1558
|
-
view = view[written:]
|
|
1646
|
+
os.fchmod(temp_fd, mode)
|
|
1647
|
+
write_all(temp_fd, data)
|
|
1559
1648
|
os.fsync(temp_fd)
|
|
1649
|
+
temp_stat = os.fstat(temp_fd)
|
|
1560
1650
|
os.close(temp_fd)
|
|
1561
1651
|
temp_fd = None
|
|
1562
|
-
|
|
1652
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
1563
1653
|
temp_name = None
|
|
1564
1654
|
os.fsync(parent_fd)
|
|
1565
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
1566
1655
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
1567
1656
|
finally:
|
|
1568
1657
|
if temp_fd is not None:
|
|
@@ -1592,13 +1681,8 @@ def copy_path(root_fd, payload):
|
|
|
1592
1681
|
if max_bytes >= 0 and source_stat.st_size > max_bytes:
|
|
1593
1682
|
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
1594
1683
|
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
1595
|
-
if not overwrite:
|
|
1596
|
-
try:
|
|
1597
|
-
os.lstat(basename, dir_fd=parent_fd)
|
|
1598
|
-
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
1599
|
-
except FileNotFoundError:
|
|
1600
|
-
pass
|
|
1601
1684
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
1685
|
+
os.fchmod(temp_fd, mode)
|
|
1602
1686
|
written_bytes = 0
|
|
1603
1687
|
while True:
|
|
1604
1688
|
chunk = os.read(source_fd, 65536)
|
|
@@ -1614,12 +1698,12 @@ def copy_path(root_fd, payload):
|
|
|
1614
1698
|
raise OSError(errno.EIO, "short write")
|
|
1615
1699
|
view = view[written:]
|
|
1616
1700
|
os.fsync(temp_fd)
|
|
1701
|
+
temp_stat = os.fstat(temp_fd)
|
|
1617
1702
|
os.close(temp_fd)
|
|
1618
1703
|
temp_fd = None
|
|
1619
|
-
|
|
1704
|
+
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
1620
1705
|
temp_name = None
|
|
1621
1706
|
os.fsync(parent_fd)
|
|
1622
|
-
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
1623
1707
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
1624
1708
|
finally:
|
|
1625
1709
|
os.close(source_fd)
|
|
@@ -1636,6 +1720,7 @@ def copy_path(root_fd, payload):
|
|
|
1636
1720
|
def run_operation(operation, root_path, payload):
|
|
1637
1721
|
root_fd = open_dir(root_path)
|
|
1638
1722
|
try:
|
|
1723
|
+
assert_expected_root(root_fd, payload)
|
|
1639
1724
|
if operation == "stat":
|
|
1640
1725
|
return stat_path(root_fd, payload)
|
|
1641
1726
|
if operation == "readdir":
|
|
@@ -1740,6 +1825,15 @@ function mapWorkerError(response) {
|
|
|
1740
1825
|
if (message.includes("fs-safe-source-mismatch")) {
|
|
1741
1826
|
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
1742
1827
|
}
|
|
1828
|
+
if (message.includes("fs-safe-temp-mismatch")) {
|
|
1829
|
+
return new errors_FsSafeError("path-mismatch", "temp path changed during write");
|
|
1830
|
+
}
|
|
1831
|
+
if (message.includes("fs-safe-root-mismatch")) {
|
|
1832
|
+
return new errors_FsSafeError("path-mismatch", "root path changed during operation");
|
|
1833
|
+
}
|
|
1834
|
+
if (message.includes("fs-safe-directory-noreplace-unsupported")) {
|
|
1835
|
+
return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
1836
|
+
}
|
|
1743
1837
|
if (code === "FileNotFoundError" || errno === 2) {
|
|
1744
1838
|
return new errors_FsSafeError("not-found", "file not found");
|
|
1745
1839
|
}
|
|
@@ -1757,19 +1851,19 @@ function mapWorkerError(response) {
|
|
|
1757
1851
|
}
|
|
1758
1852
|
return new errors_FsSafeError("helper-failed", message);
|
|
1759
1853
|
}
|
|
1760
|
-
function rejectPending(error) {
|
|
1761
|
-
if (!worker) {
|
|
1854
|
+
function rejectPending(error, targetWorker = worker) {
|
|
1855
|
+
if (!targetWorker || worker !== targetWorker) {
|
|
1762
1856
|
return;
|
|
1763
1857
|
}
|
|
1764
|
-
setWorkerRef(
|
|
1765
|
-
for (const pending of
|
|
1858
|
+
setWorkerRef(targetWorker, false);
|
|
1859
|
+
for (const pending of targetWorker.pending.values()) {
|
|
1766
1860
|
pending.reject(error);
|
|
1767
1861
|
}
|
|
1768
|
-
|
|
1862
|
+
targetWorker.pending.clear();
|
|
1769
1863
|
worker = null;
|
|
1770
1864
|
}
|
|
1771
|
-
function handleWorkerLine(line) {
|
|
1772
|
-
if (
|
|
1865
|
+
function handleWorkerLine(currentWorker, line) {
|
|
1866
|
+
if (worker !== currentWorker || !line.trim()) {
|
|
1773
1867
|
return;
|
|
1774
1868
|
}
|
|
1775
1869
|
let decoded;
|
|
@@ -1777,11 +1871,11 @@ function handleWorkerLine(line) {
|
|
|
1777
1871
|
decoded = JSON.parse(line);
|
|
1778
1872
|
}
|
|
1779
1873
|
catch {
|
|
1780
|
-
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
|
|
1874
|
+
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`), currentWorker);
|
|
1781
1875
|
return;
|
|
1782
1876
|
}
|
|
1783
1877
|
if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
|
|
1784
|
-
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
|
|
1878
|
+
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"), currentWorker);
|
|
1785
1879
|
return;
|
|
1786
1880
|
}
|
|
1787
1881
|
const response = decoded;
|
|
@@ -1789,13 +1883,13 @@ function handleWorkerLine(line) {
|
|
|
1789
1883
|
if (id === undefined) {
|
|
1790
1884
|
return;
|
|
1791
1885
|
}
|
|
1792
|
-
const pending =
|
|
1886
|
+
const pending = currentWorker.pending.get(id);
|
|
1793
1887
|
if (!pending) {
|
|
1794
1888
|
return;
|
|
1795
1889
|
}
|
|
1796
|
-
|
|
1797
|
-
if (
|
|
1798
|
-
setWorkerRef(
|
|
1890
|
+
currentWorker.pending.delete(id);
|
|
1891
|
+
if (currentWorker.pending.size === 0) {
|
|
1892
|
+
setWorkerRef(currentWorker, false);
|
|
1799
1893
|
}
|
|
1800
1894
|
if (response.ok === true) {
|
|
1801
1895
|
pending.resolve(response.result);
|
|
@@ -1811,28 +1905,28 @@ function getWorker() {
|
|
|
1811
1905
|
const child = (0,external_node_child_process_.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
|
|
1812
1906
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1813
1907
|
});
|
|
1814
|
-
|
|
1908
|
+
const currentWorker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
|
|
1909
|
+
worker = currentWorker;
|
|
1815
1910
|
child.stdout.setEncoding("utf8");
|
|
1816
1911
|
child.stderr.setEncoding("utf8");
|
|
1817
1912
|
child.stdout.on("data", (chunk) => {
|
|
1818
|
-
|
|
1819
|
-
if (!current) {
|
|
1913
|
+
if (worker !== currentWorker) {
|
|
1820
1914
|
return;
|
|
1821
1915
|
}
|
|
1822
|
-
|
|
1916
|
+
currentWorker.stdoutBuffer += chunk;
|
|
1823
1917
|
for (;;) {
|
|
1824
|
-
const newline =
|
|
1918
|
+
const newline = currentWorker.stdoutBuffer.indexOf("\n");
|
|
1825
1919
|
if (newline < 0) {
|
|
1826
1920
|
break;
|
|
1827
1921
|
}
|
|
1828
|
-
const line =
|
|
1829
|
-
|
|
1830
|
-
handleWorkerLine(line);
|
|
1922
|
+
const line = currentWorker.stdoutBuffer.slice(0, newline);
|
|
1923
|
+
currentWorker.stdoutBuffer = currentWorker.stdoutBuffer.slice(newline + 1);
|
|
1924
|
+
handleWorkerLine(currentWorker, line);
|
|
1831
1925
|
}
|
|
1832
1926
|
});
|
|
1833
1927
|
child.stderr.on("data", (chunk) => {
|
|
1834
|
-
if (worker) {
|
|
1835
|
-
|
|
1928
|
+
if (worker === currentWorker) {
|
|
1929
|
+
currentWorker.stderr = `${currentWorker.stderr}${chunk}`.slice(-4096);
|
|
1836
1930
|
}
|
|
1837
1931
|
});
|
|
1838
1932
|
child.once("error", (error) => {
|
|
@@ -1841,17 +1935,17 @@ function getWorker() {
|
|
|
1841
1935
|
: error instanceof Error
|
|
1842
1936
|
? error
|
|
1843
1937
|
: new Error(String(error));
|
|
1844
|
-
rejectPending(mapped);
|
|
1938
|
+
rejectPending(mapped, currentWorker);
|
|
1845
1939
|
});
|
|
1846
1940
|
child.once("close", (code, signal) => {
|
|
1847
|
-
const stderr =
|
|
1848
|
-
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
|
|
1941
|
+
const stderr = currentWorker.stderr.trim();
|
|
1942
|
+
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`), currentWorker);
|
|
1849
1943
|
});
|
|
1850
1944
|
process.once("exit", () => {
|
|
1851
1945
|
child.kill("SIGTERM");
|
|
1852
1946
|
});
|
|
1853
|
-
setWorkerRef(
|
|
1854
|
-
return
|
|
1947
|
+
setWorkerRef(currentWorker, false);
|
|
1948
|
+
return currentWorker;
|
|
1855
1949
|
}
|
|
1856
1950
|
function setRefable(value, ref) {
|
|
1857
1951
|
if (!value) {
|
|
@@ -1917,9 +2011,7 @@ function validatePinnedOperationPayload(payload) {
|
|
|
1917
2011
|
}
|
|
1918
2012
|
}
|
|
1919
2013
|
function isPinnedHelperUnavailable(error) {
|
|
1920
|
-
return error instanceof Error &&
|
|
1921
|
-
"code" in error &&
|
|
1922
|
-
error.code === "helper-unavailable";
|
|
2014
|
+
return error instanceof Error && "code" in error && error.code === "helper-unavailable";
|
|
1923
2015
|
}
|
|
1924
2016
|
function validatePinnedRelativePath(relativePath) {
|
|
1925
2017
|
if (relativePath.length === 0 || relativePath === ".") {
|
|
@@ -2017,29 +2109,21 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
2017
2109
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
2018
2110
|
}
|
|
2019
2111
|
}
|
|
2020
|
-
function
|
|
2021
|
-
if (maxBytes === undefined) {
|
|
2022
|
-
return undefined;
|
|
2023
|
-
}
|
|
2112
|
+
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
2024
2113
|
let bytes = 0;
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2114
|
+
for await (const chunk of stream) {
|
|
2115
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
2116
|
+
bytes += buffer.byteLength;
|
|
2117
|
+
assertWithinMaxBytes(bytes, maxBytes);
|
|
2118
|
+
let offset = 0;
|
|
2119
|
+
while (offset < buffer.byteLength) {
|
|
2120
|
+
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
|
|
2121
|
+
if (bytesWritten <= 0) {
|
|
2122
|
+
throw new errors_FsSafeError("helper-failed", "fallback stream write made no progress");
|
|
2031
2123
|
}
|
|
2032
|
-
|
|
2033
|
-
}
|
|
2034
|
-
});
|
|
2035
|
-
}
|
|
2036
|
-
async function pipelineWithMaxBytes(stream, destination, maxBytes) {
|
|
2037
|
-
const limiter = pinned_write_createMaxBytesTransform(maxBytes);
|
|
2038
|
-
if (limiter) {
|
|
2039
|
-
await (0,external_node_stream_promises_.pipeline)(stream, limiter, destination);
|
|
2040
|
-
return;
|
|
2124
|
+
offset += bytesWritten;
|
|
2125
|
+
}
|
|
2041
2126
|
}
|
|
2042
|
-
await (0,external_node_stream_promises_.pipeline)(stream, destination);
|
|
2043
2127
|
}
|
|
2044
2128
|
async function inputToBase64(input, maxBytes) {
|
|
2045
2129
|
if (input.kind === "buffer") {
|
|
@@ -2064,7 +2148,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
2064
2148
|
relativeParentPath: params.relativeParentPath,
|
|
2065
2149
|
});
|
|
2066
2150
|
if (getFsSafePythonConfig().mode === "off") {
|
|
2067
|
-
return await
|
|
2151
|
+
return await runPinnedWriteFallbackOrThrow(params);
|
|
2068
2152
|
}
|
|
2069
2153
|
if (params.input.kind === "stream") {
|
|
2070
2154
|
try {
|
|
@@ -2072,7 +2156,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
2072
2156
|
}
|
|
2073
2157
|
catch (error) {
|
|
2074
2158
|
if (canFallbackFromPythonError(error)) {
|
|
2075
|
-
return await
|
|
2159
|
+
return await runPinnedWriteFallbackOrThrow(params, error);
|
|
2076
2160
|
}
|
|
2077
2161
|
throw error;
|
|
2078
2162
|
}
|
|
@@ -2085,6 +2169,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
2085
2169
|
mode: params.mode || 0o600,
|
|
2086
2170
|
overwrite: params.overwrite !== false,
|
|
2087
2171
|
relativeParentPath: params.relativeParentPath,
|
|
2172
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
2088
2173
|
};
|
|
2089
2174
|
try {
|
|
2090
2175
|
return await runPinnedPythonOperation({
|
|
@@ -2095,7 +2180,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
2095
2180
|
}
|
|
2096
2181
|
catch (error) {
|
|
2097
2182
|
if (canFallbackFromPythonError(error)) {
|
|
2098
|
-
return await
|
|
2183
|
+
return await runPinnedWriteFallbackOrThrow(params, error);
|
|
2099
2184
|
}
|
|
2100
2185
|
throw error;
|
|
2101
2186
|
}
|
|
@@ -2115,22 +2200,29 @@ async function runPinnedCopyHelper(params) {
|
|
|
2115
2200
|
mode: params.mode || 0o600,
|
|
2116
2201
|
overwrite: params.overwrite !== false,
|
|
2117
2202
|
relativeParentPath: params.relativeParentPath,
|
|
2203
|
+
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
2118
2204
|
sourceDev: params.sourceIdentity.dev,
|
|
2119
2205
|
sourceIno: params.sourceIdentity.ino,
|
|
2120
2206
|
sourcePath: params.sourcePath,
|
|
2121
2207
|
},
|
|
2122
2208
|
});
|
|
2123
2209
|
}
|
|
2210
|
+
async function runPinnedWriteFallbackOrThrow(params, cause) {
|
|
2211
|
+
if (process.platform !== "win32") {
|
|
2212
|
+
throw new errors_FsSafeError("helper-unavailable", "Python helper is required for pinned writes on this platform", { cause });
|
|
2213
|
+
}
|
|
2214
|
+
return await runPinnedWriteFallback(params);
|
|
2215
|
+
}
|
|
2124
2216
|
async function runPinnedWriteFallback(params) {
|
|
2125
2217
|
const parentPath = params.relativeParentPath
|
|
2126
2218
|
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
2127
2219
|
: params.rootPath;
|
|
2128
|
-
const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
2129
2220
|
if (params.mkdir) {
|
|
2130
|
-
await
|
|
2131
|
-
await promises_.mkdir(parentPath, { recursive: true });
|
|
2132
|
-
});
|
|
2221
|
+
await mkdirPathComponentsWithGuards({ rootReal: params.rootPath, targetPath: parentPath });
|
|
2133
2222
|
}
|
|
2223
|
+
const parentGuard = params.mkdir
|
|
2224
|
+
? await directory_guard_createAsyncDirectoryGuard(parentPath)
|
|
2225
|
+
: await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
2134
2226
|
const targetPath = external_node_path_.join(parentPath, params.basename);
|
|
2135
2227
|
if (params.overwrite === false) {
|
|
2136
2228
|
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), {
|
|
@@ -2152,7 +2244,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
2152
2244
|
}
|
|
2153
2245
|
}
|
|
2154
2246
|
else {
|
|
2155
|
-
await
|
|
2247
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
2156
2248
|
}
|
|
2157
2249
|
const stat = await handle.stat();
|
|
2158
2250
|
created = false;
|
|
@@ -2173,7 +2265,9 @@ async function runPinnedWriteFallback(params) {
|
|
|
2173
2265
|
? external_node_fs_.constants.O_NOFOLLOW
|
|
2174
2266
|
: 0);
|
|
2175
2267
|
let handle;
|
|
2176
|
-
let
|
|
2268
|
+
let tempStat;
|
|
2269
|
+
let targetStat;
|
|
2270
|
+
let renamed = false;
|
|
2177
2271
|
try {
|
|
2178
2272
|
handle = await promises_.open(tempPath, tempFlags, params.mode);
|
|
2179
2273
|
if (params.input.kind === "buffer") {
|
|
@@ -2186,29 +2280,36 @@ async function runPinnedWriteFallback(params) {
|
|
|
2186
2280
|
}
|
|
2187
2281
|
}
|
|
2188
2282
|
else {
|
|
2189
|
-
|
|
2190
|
-
writable.once("close", () => {
|
|
2191
|
-
handleClosedByStream = true;
|
|
2192
|
-
});
|
|
2193
|
-
await pipelineWithMaxBytes(params.input.stream, writable, params.maxBytes);
|
|
2283
|
+
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
2194
2284
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2285
|
+
tempStat = await handle.stat();
|
|
2286
|
+
const tempPathStat = await promises_.lstat(tempPath);
|
|
2287
|
+
if (tempPathStat.isSymbolicLink() || !file_identity_sameFileIdentity(tempPathStat, tempStat)) {
|
|
2288
|
+
throw new errors_FsSafeError("path-mismatch", "fallback temp path changed during write");
|
|
2198
2289
|
}
|
|
2290
|
+
const expectedTempStat = tempStat;
|
|
2291
|
+
await handle.close().catch(() => undefined);
|
|
2292
|
+
handle = undefined;
|
|
2199
2293
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
2200
2294
|
await promises_.rename(tempPath, targetPath);
|
|
2295
|
+
renamed = true;
|
|
2296
|
+
targetStat = await promises_.lstat(targetPath);
|
|
2297
|
+
if (targetStat.isSymbolicLink() || !file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
|
|
2298
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
2299
|
+
}
|
|
2201
2300
|
});
|
|
2202
2301
|
}
|
|
2203
2302
|
catch (error) {
|
|
2204
|
-
|
|
2205
|
-
|
|
2303
|
+
await handle?.close().catch(() => undefined);
|
|
2304
|
+
if (!renamed) {
|
|
2305
|
+
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
2206
2306
|
}
|
|
2207
|
-
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
2208
2307
|
throw error;
|
|
2209
2308
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2309
|
+
if (!targetStat) {
|
|
2310
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target was not verified");
|
|
2311
|
+
}
|
|
2312
|
+
return { dev: targetStat.dev, ino: targetStat.ino };
|
|
2212
2313
|
}
|
|
2213
2314
|
|
|
2214
2315
|
// EXTERNAL MODULE: external "node:os"
|
|
@@ -2747,7 +2848,7 @@ function relativeInsideRoot(rootPath, targetPath) {
|
|
|
2747
2848
|
if (!relative || relative === ".") {
|
|
2748
2849
|
return "";
|
|
2749
2850
|
}
|
|
2750
|
-
if (
|
|
2851
|
+
if (isPathRelativeEscape(relative)) {
|
|
2751
2852
|
return "";
|
|
2752
2853
|
}
|
|
2753
2854
|
return relative;
|
|
@@ -3098,6 +3199,15 @@ function __setFsSafeTestHooksForTest(hooks) {
|
|
|
3098
3199
|
test_hooks_fsSafeTestHooks = hooks;
|
|
3099
3200
|
}
|
|
3100
3201
|
|
|
3202
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
|
|
3203
|
+
function stringifyJsonDocument(value, replacer, space) {
|
|
3204
|
+
const text = JSON.stringify(value, replacer, space);
|
|
3205
|
+
if (typeof text !== "string") {
|
|
3206
|
+
throw new TypeError("value is not representable as a JSON document");
|
|
3207
|
+
}
|
|
3208
|
+
return text;
|
|
3209
|
+
}
|
|
3210
|
+
|
|
3101
3211
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
|
|
3102
3212
|
|
|
3103
3213
|
const tempCleanupEntries = new Map();
|
|
@@ -3185,6 +3295,7 @@ async function serializePathWrite(key, run) {
|
|
|
3185
3295
|
|
|
3186
3296
|
|
|
3187
3297
|
|
|
3298
|
+
|
|
3188
3299
|
|
|
3189
3300
|
|
|
3190
3301
|
function logWarn(message) {
|
|
@@ -3425,12 +3536,12 @@ class RootHandle {
|
|
|
3425
3536
|
}
|
|
3426
3537
|
async writeJson(relativePath, data, options = {}) {
|
|
3427
3538
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
3428
|
-
const json =
|
|
3539
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
3429
3540
|
await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
3430
3541
|
}
|
|
3431
3542
|
async createJson(relativePath, data, options = {}) {
|
|
3432
3543
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
3433
|
-
const json =
|
|
3544
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
3434
3545
|
await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
3435
3546
|
}
|
|
3436
3547
|
async copyIn(relativePath, sourcePath, options = {}) {
|
|
@@ -4215,6 +4326,9 @@ async function movePathFallback(root, params) {
|
|
|
4215
4326
|
if (sourceStat.isFile() && sourceStat.nlink > 1) {
|
|
4216
4327
|
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
4217
4328
|
}
|
|
4329
|
+
if (!params.overwrite && sourceStat.isDirectory()) {
|
|
4330
|
+
throw new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
4331
|
+
}
|
|
4218
4332
|
if (!params.overwrite) {
|
|
4219
4333
|
try {
|
|
4220
4334
|
await promises_.lstat(target.resolved);
|
|
@@ -46023,7 +46137,7 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
46023
46137
|
// module factories are used so entry inlining is disabled
|
|
46024
46138
|
// startup
|
|
46025
46139
|
// Load entry module and return exports
|
|
46026
|
-
var __webpack_exports__ = __webpack_require__(
|
|
46140
|
+
var __webpack_exports__ = __webpack_require__(4403);
|
|
46027
46141
|
var __webpack_exports__DEFAULT_LINT_RULES = __webpack_exports__.Kd;
|
|
46028
46142
|
var __webpack_exports__LintValidationError = __webpack_exports__.F5;
|
|
46029
46143
|
var __webpack_exports__createFormatter = __webpack_exports__.xi;
|
package/package.json
CHANGED