@lousy-agents/lint 5.14.7 → 5.14.9

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.
Files changed (2) hide show
  1. package/dist/index.js +143 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -566,7 +566,7 @@ module.exports = webpackEmptyAsyncContext;
566
566
 
567
567
 
568
568
  },
569
- 2202(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
569
+ 4403(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
570
570
 
571
571
  // EXPORTS
572
572
  __webpack_require__.d(__webpack_exports__, {
@@ -1096,7 +1096,7 @@ function splitSafeRelativePath(relativePath) {
1096
1096
  function resolveSafeRelativePath(rootDir, relativePath) {
1097
1097
  const root = path.resolve(rootDir);
1098
1098
  const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
1099
- if (target !== root && !target.startsWith(root + path.sep)) {
1099
+ if (!path_isPathInside(root, target)) {
1100
1100
  throw new FsSafeError("outside-workspace", "relative path escapes root");
1101
1101
  }
1102
1102
  return target;
@@ -1431,6 +1431,85 @@ def reject_unsafe_endpoint(st):
1431
1431
  if stat.S_ISREG(mode) and st.st_nlink > 1:
1432
1432
  raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
1433
1433
 
1434
+ def copy_bytes(source_fd, dest_fd):
1435
+ while True:
1436
+ chunk = os.read(source_fd, 65536)
1437
+ if not chunk:
1438
+ break
1439
+ view = memoryview(chunk)
1440
+ while view:
1441
+ written = os.write(dest_fd, view)
1442
+ if written <= 0:
1443
+ raise OSError(errno.EIO, "short write")
1444
+ view = view[written:]
1445
+
1446
+ def write_all(fd, data):
1447
+ view = memoryview(data)
1448
+ while view:
1449
+ written = os.write(fd, view)
1450
+ if written <= 0:
1451
+ raise OSError(errno.EIO, "short write")
1452
+ view = view[written:]
1453
+
1454
+ def link_unsupported(exc):
1455
+ unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
1456
+ return getattr(exc, "errno", None) in unsupported
1457
+
1458
+ def link_no_replace(name, new_name, source_fd, target_fd):
1459
+ linked = False
1460
+ try:
1461
+ os.link(name, new_name, src_dir_fd=source_fd, dst_dir_fd=target_fd, follow_symlinks=False)
1462
+ linked = True
1463
+ os.unlink(name, dir_fd=source_fd)
1464
+ except Exception:
1465
+ if linked:
1466
+ try: os.unlink(new_name, dir_fd=target_fd)
1467
+ except FileNotFoundError: pass
1468
+ raise
1469
+ os.fsync(source_fd)
1470
+ if source_fd != target_fd:
1471
+ os.fsync(target_fd)
1472
+
1473
+ def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
1474
+ source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
1475
+ dest_fd = None
1476
+ success = False
1477
+ try:
1478
+ if expected is not None:
1479
+ source_stat = os.fstat(source_fd)
1480
+ if source_stat.st_dev != expected.st_dev or source_stat.st_ino != expected.st_ino:
1481
+ raise RuntimeError("fs-safe-source-mismatch")
1482
+ dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
1483
+ copy_bytes(source_fd, dest_fd)
1484
+ os.fsync(dest_fd)
1485
+ success = True
1486
+ finally:
1487
+ os.close(source_fd)
1488
+ if dest_fd is not None:
1489
+ os.close(dest_fd)
1490
+ if dest_fd is not None and not success:
1491
+ try: os.unlink(basename, dir_fd=target_parent_fd)
1492
+ except FileNotFoundError: pass
1493
+ if unlink_source:
1494
+ try:
1495
+ os.unlink(source_name, dir_fd=source_parent_fd)
1496
+ except Exception:
1497
+ try: os.unlink(basename, dir_fd=target_parent_fd)
1498
+ except FileNotFoundError: pass
1499
+ raise
1500
+
1501
+ def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode):
1502
+ if overwrite:
1503
+ os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
1504
+ else:
1505
+ try:
1506
+ os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
1507
+ os.unlink(temp_name, dir_fd=parent_fd)
1508
+ except OSError as exc:
1509
+ if not link_unsupported(exc):
1510
+ raise
1511
+ copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, unlink_source=True)
1512
+
1434
1513
  def stat_path(root_fd, payload):
1435
1514
  relative = payload.get("relativePath", "")
1436
1515
  segments = split_relative(relative)
@@ -1505,7 +1584,18 @@ def rename_path(root_fd, payload):
1505
1584
  try:
1506
1585
  from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
1507
1586
  reject_unsafe_endpoint(from_stat)
1508
- if not payload.get("overwrite", True):
1587
+ overwrite = payload.get("overwrite", True)
1588
+ if not overwrite and stat.S_ISREG(from_stat.st_mode):
1589
+ try:
1590
+ link_no_replace(from_base, to_base, from_parent_fd, to_parent_fd)
1591
+ except OSError as exc:
1592
+ if not link_unsupported(exc):
1593
+ raise
1594
+ copy_file_no_replace(from_parent_fd, from_base, to_parent_fd, to_base, stat.S_IMODE(from_stat.st_mode), from_stat, True)
1595
+ return None
1596
+ if not overwrite and stat.S_ISDIR(from_stat.st_mode):
1597
+ raise RuntimeError("fs-safe-directory-noreplace-unsupported")
1598
+ if not overwrite:
1509
1599
  try:
1510
1600
  os.lstat(to_base, dir_fd=to_parent_fd)
1511
1601
  raise FileExistsError(errno.EEXIST, "destination exists", to_base)
@@ -1550,16 +1640,11 @@ def write_path(root_fd, payload):
1550
1640
  except FileNotFoundError:
1551
1641
  pass
1552
1642
  temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
1553
- view = memoryview(data)
1554
- while view:
1555
- written = os.write(temp_fd, view)
1556
- if written <= 0:
1557
- raise OSError(errno.EIO, "short write")
1558
- view = view[written:]
1643
+ write_all(temp_fd, data)
1559
1644
  os.fsync(temp_fd)
1560
1645
  os.close(temp_fd)
1561
1646
  temp_fd = None
1562
- os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
1647
+ commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
1563
1648
  temp_name = None
1564
1649
  os.fsync(parent_fd)
1565
1650
  result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
@@ -1592,12 +1677,6 @@ def copy_path(root_fd, payload):
1592
1677
  if max_bytes >= 0 and source_stat.st_size > max_bytes:
1593
1678
  raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
1594
1679
  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
1680
  temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
1602
1681
  written_bytes = 0
1603
1682
  while True:
@@ -1616,7 +1695,7 @@ def copy_path(root_fd, payload):
1616
1695
  os.fsync(temp_fd)
1617
1696
  os.close(temp_fd)
1618
1697
  temp_fd = None
1619
- os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
1698
+ commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
1620
1699
  temp_name = None
1621
1700
  os.fsync(parent_fd)
1622
1701
  result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
@@ -1740,6 +1819,9 @@ function mapWorkerError(response) {
1740
1819
  if (message.includes("fs-safe-source-mismatch")) {
1741
1820
  return new errors_FsSafeError("path-mismatch", "source path changed during copy");
1742
1821
  }
1822
+ if (message.includes("fs-safe-directory-noreplace-unsupported")) {
1823
+ return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
1824
+ }
1743
1825
  if (code === "FileNotFoundError" || errno === 2) {
1744
1826
  return new errors_FsSafeError("not-found", "file not found");
1745
1827
  }
@@ -1757,19 +1839,19 @@ function mapWorkerError(response) {
1757
1839
  }
1758
1840
  return new errors_FsSafeError("helper-failed", message);
1759
1841
  }
1760
- function rejectPending(error) {
1761
- if (!worker) {
1842
+ function rejectPending(error, targetWorker = worker) {
1843
+ if (!targetWorker || worker !== targetWorker) {
1762
1844
  return;
1763
1845
  }
1764
- setWorkerRef(worker, false);
1765
- for (const pending of worker.pending.values()) {
1846
+ setWorkerRef(targetWorker, false);
1847
+ for (const pending of targetWorker.pending.values()) {
1766
1848
  pending.reject(error);
1767
1849
  }
1768
- worker.pending.clear();
1850
+ targetWorker.pending.clear();
1769
1851
  worker = null;
1770
1852
  }
1771
- function handleWorkerLine(line) {
1772
- if (!worker || !line.trim()) {
1853
+ function handleWorkerLine(currentWorker, line) {
1854
+ if (worker !== currentWorker || !line.trim()) {
1773
1855
  return;
1774
1856
  }
1775
1857
  let decoded;
@@ -1777,11 +1859,11 @@ function handleWorkerLine(line) {
1777
1859
  decoded = JSON.parse(line);
1778
1860
  }
1779
1861
  catch {
1780
- rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
1862
+ rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`), currentWorker);
1781
1863
  return;
1782
1864
  }
1783
1865
  if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
1784
- rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
1866
+ rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"), currentWorker);
1785
1867
  return;
1786
1868
  }
1787
1869
  const response = decoded;
@@ -1789,13 +1871,13 @@ function handleWorkerLine(line) {
1789
1871
  if (id === undefined) {
1790
1872
  return;
1791
1873
  }
1792
- const pending = worker.pending.get(id);
1874
+ const pending = currentWorker.pending.get(id);
1793
1875
  if (!pending) {
1794
1876
  return;
1795
1877
  }
1796
- worker.pending.delete(id);
1797
- if (worker.pending.size === 0) {
1798
- setWorkerRef(worker, false);
1878
+ currentWorker.pending.delete(id);
1879
+ if (currentWorker.pending.size === 0) {
1880
+ setWorkerRef(currentWorker, false);
1799
1881
  }
1800
1882
  if (response.ok === true) {
1801
1883
  pending.resolve(response.result);
@@ -1811,28 +1893,28 @@ function getWorker() {
1811
1893
  const child = (0,external_node_child_process_.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
1812
1894
  stdio: ["pipe", "pipe", "pipe"],
1813
1895
  });
1814
- worker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
1896
+ const currentWorker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
1897
+ worker = currentWorker;
1815
1898
  child.stdout.setEncoding("utf8");
1816
1899
  child.stderr.setEncoding("utf8");
1817
1900
  child.stdout.on("data", (chunk) => {
1818
- const current = worker;
1819
- if (!current) {
1901
+ if (worker !== currentWorker) {
1820
1902
  return;
1821
1903
  }
1822
- current.stdoutBuffer += chunk;
1904
+ currentWorker.stdoutBuffer += chunk;
1823
1905
  for (;;) {
1824
- const newline = current.stdoutBuffer.indexOf("\n");
1906
+ const newline = currentWorker.stdoutBuffer.indexOf("\n");
1825
1907
  if (newline < 0) {
1826
1908
  break;
1827
1909
  }
1828
- const line = current.stdoutBuffer.slice(0, newline);
1829
- current.stdoutBuffer = current.stdoutBuffer.slice(newline + 1);
1830
- handleWorkerLine(line);
1910
+ const line = currentWorker.stdoutBuffer.slice(0, newline);
1911
+ currentWorker.stdoutBuffer = currentWorker.stdoutBuffer.slice(newline + 1);
1912
+ handleWorkerLine(currentWorker, line);
1831
1913
  }
1832
1914
  });
1833
1915
  child.stderr.on("data", (chunk) => {
1834
- if (worker) {
1835
- worker.stderr = `${worker.stderr}${chunk}`.slice(-4096);
1916
+ if (worker === currentWorker) {
1917
+ currentWorker.stderr = `${currentWorker.stderr}${chunk}`.slice(-4096);
1836
1918
  }
1837
1919
  });
1838
1920
  child.once("error", (error) => {
@@ -1841,17 +1923,17 @@ function getWorker() {
1841
1923
  : error instanceof Error
1842
1924
  ? error
1843
1925
  : new Error(String(error));
1844
- rejectPending(mapped);
1926
+ rejectPending(mapped, currentWorker);
1845
1927
  });
1846
1928
  child.once("close", (code, signal) => {
1847
- const stderr = worker?.stderr.trim();
1848
- rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
1929
+ const stderr = currentWorker.stderr.trim();
1930
+ rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`), currentWorker);
1849
1931
  });
1850
1932
  process.once("exit", () => {
1851
1933
  child.kill("SIGTERM");
1852
1934
  });
1853
- setWorkerRef(worker, false);
1854
- return worker;
1935
+ setWorkerRef(currentWorker, false);
1936
+ return currentWorker;
1855
1937
  }
1856
1938
  function setRefable(value, ref) {
1857
1939
  if (!value) {
@@ -3098,6 +3180,15 @@ function __setFsSafeTestHooksForTest(hooks) {
3098
3180
  test_hooks_fsSafeTestHooks = hooks;
3099
3181
  }
3100
3182
 
3183
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
3184
+ function stringifyJsonDocument(value, replacer, space) {
3185
+ const text = JSON.stringify(value, replacer, space);
3186
+ if (typeof text !== "string") {
3187
+ throw new TypeError("value is not representable as a JSON document");
3188
+ }
3189
+ return text;
3190
+ }
3191
+
3101
3192
  ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
3102
3193
 
3103
3194
  const tempCleanupEntries = new Map();
@@ -3185,6 +3276,7 @@ async function serializePathWrite(key, run) {
3185
3276
 
3186
3277
 
3187
3278
 
3279
+
3188
3280
 
3189
3281
 
3190
3282
  function logWarn(message) {
@@ -3425,12 +3517,12 @@ class RootHandle {
3425
3517
  }
3426
3518
  async writeJson(relativePath, data, options = {}) {
3427
3519
  const { replacer, space, trailingNewline = true, ...writeOptions } = options;
3428
- const json = JSON.stringify(data, replacer, space);
3520
+ const json = stringifyJsonDocument(data, replacer, space);
3429
3521
  await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
3430
3522
  }
3431
3523
  async createJson(relativePath, data, options = {}) {
3432
3524
  const { replacer, space, trailingNewline = true, ...writeOptions } = options;
3433
- const json = JSON.stringify(data, replacer, space);
3525
+ const json = stringifyJsonDocument(data, replacer, space);
3434
3526
  await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
3435
3527
  }
3436
3528
  async copyIn(relativePath, sourcePath, options = {}) {
@@ -4215,6 +4307,9 @@ async function movePathFallback(root, params) {
4215
4307
  if (sourceStat.isFile() && sourceStat.nlink > 1) {
4216
4308
  throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
4217
4309
  }
4310
+ if (!params.overwrite && sourceStat.isDirectory()) {
4311
+ throw new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
4312
+ }
4218
4313
  if (!params.overwrite) {
4219
4314
  try {
4220
4315
  await promises_.lstat(target.resolved);
@@ -46023,7 +46118,7 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
46023
46118
  // module factories are used so entry inlining is disabled
46024
46119
  // startup
46025
46120
  // Load entry module and return exports
46026
- var __webpack_exports__ = __webpack_require__(2202);
46121
+ var __webpack_exports__ = __webpack_require__(4403);
46027
46122
  var __webpack_exports__DEFAULT_LINT_RULES = __webpack_exports__.Kd;
46028
46123
  var __webpack_exports__LintValidationError = __webpack_exports__.F5;
46029
46124
  var __webpack_exports__createFormatter = __webpack_exports__.xi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lousy-agents/lint",
3
- "version": "5.14.7",
3
+ "version": "5.14.9",
4
4
  "description": "Programmatic lint API for validating AI coding assistant configurations — skills, agents, hooks, and instructions",
5
5
  "type": "module",
6
6
  "repository": {