@lousy-agents/agent-shell 5.17.8 → 5.17.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 +404 -151
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire as __rspack_createRequire } from "node:module";
|
|
3
3
|
const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
|
|
4
4
|
var __webpack_modules__ = ({
|
|
5
|
-
|
|
5
|
+
953(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
6
6
|
|
|
7
7
|
;// CONCATENATED MODULE: external "node:child_process"
|
|
8
8
|
const external_node_child_process_namespaceObject = __rspack_createRequire_require("node:child_process");
|
|
@@ -811,15 +811,11 @@ function canFallbackFromPythonError(error) {
|
|
|
811
811
|
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
812
812
|
import base64, errno, json, os, secrets, stat, sys
|
|
813
813
|
DIR_FLAGS = os.O_RDONLY
|
|
814
|
-
if hasattr(os, "O_DIRECTORY"):
|
|
815
|
-
|
|
816
|
-
if hasattr(os, "O_NOFOLLOW"):
|
|
817
|
-
DIR_FLAGS |= os.O_NOFOLLOW
|
|
814
|
+
if hasattr(os, "O_DIRECTORY"): DIR_FLAGS |= os.O_DIRECTORY
|
|
815
|
+
if hasattr(os, "O_NOFOLLOW"): DIR_FLAGS |= os.O_NOFOLLOW
|
|
818
816
|
READ_FLAGS = os.O_RDONLY
|
|
819
|
-
if hasattr(os, "O_NONBLOCK"):
|
|
820
|
-
|
|
821
|
-
if hasattr(os, "O_NOFOLLOW"):
|
|
822
|
-
READ_FLAGS |= os.O_NOFOLLOW
|
|
817
|
+
if hasattr(os, "O_NONBLOCK"): READ_FLAGS |= os.O_NONBLOCK
|
|
818
|
+
if hasattr(os, "O_NOFOLLOW"): READ_FLAGS |= os.O_NOFOLLOW
|
|
823
819
|
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
824
820
|
if hasattr(os, "O_NOFOLLOW"):
|
|
825
821
|
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
@@ -899,6 +895,10 @@ def write_all(fd, data):
|
|
|
899
895
|
if written <= 0:
|
|
900
896
|
raise OSError(errno.EIO, "short write")
|
|
901
897
|
view = view[written:]
|
|
898
|
+
def fsync_best_effort(fd):
|
|
899
|
+
try: os.fsync(fd)
|
|
900
|
+
except OSError as error:
|
|
901
|
+
if error.errno != errno.EPERM: raise
|
|
902
902
|
def link_unsupported(exc):
|
|
903
903
|
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
904
904
|
return getattr(exc, "errno", None) in unsupported
|
|
@@ -1104,13 +1104,13 @@ def write_path(root_fd, payload):
|
|
|
1104
1104
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
1105
1105
|
os.fchmod(temp_fd, mode)
|
|
1106
1106
|
write_all(temp_fd, data)
|
|
1107
|
-
|
|
1107
|
+
fsync_best_effort(temp_fd)
|
|
1108
1108
|
temp_stat = os.fstat(temp_fd)
|
|
1109
1109
|
os.close(temp_fd)
|
|
1110
1110
|
temp_fd = None
|
|
1111
1111
|
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
1112
1112
|
temp_name = None
|
|
1113
|
-
|
|
1113
|
+
fsync_best_effort(parent_fd)
|
|
1114
1114
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
1115
1115
|
finally:
|
|
1116
1116
|
if temp_fd is not None:
|
|
@@ -1537,29 +1537,34 @@ async function runPinnedPathHelper(params) {
|
|
|
1537
1537
|
}
|
|
1538
1538
|
}
|
|
1539
1539
|
|
|
1540
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
1541
|
-
|
|
1540
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
1542
1541
|
|
|
1543
1542
|
|
|
1544
1543
|
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1544
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
|
|
1545
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS = SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES * 8;
|
|
1546
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX = "\t".repeat(8);
|
|
1547
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_PATTERN = new RegExp(`\\n(${SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX}[ \\t]{${SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS}})\\n$`);
|
|
1548
|
+
function createSidecarLockOwnershipToken() {
|
|
1549
|
+
let token = SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX;
|
|
1550
|
+
for (const byte of (0,external_node_crypto_namespaceObject.randomBytes)(SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES)) {
|
|
1551
|
+
for (let bit = 7; bit >= 0; bit -= 1) {
|
|
1552
|
+
token += byte & (1 << bit) ? "\t" : " ";
|
|
1553
|
+
}
|
|
1550
1554
|
}
|
|
1551
|
-
return
|
|
1555
|
+
return token;
|
|
1552
1556
|
}
|
|
1553
|
-
function
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1557
|
+
function readSidecarLockOwnershipToken(raw) {
|
|
1558
|
+
return SIDECAR_LOCK_OWNERSHIP_TOKEN_PATTERN.exec(raw)?.[1];
|
|
1559
|
+
}
|
|
1560
|
+
function serializeSidecarLockPayload(payload) {
|
|
1561
|
+
const ownershipToken = createSidecarLockOwnershipToken();
|
|
1562
|
+
return {
|
|
1563
|
+
raw: `${JSON.stringify(payload, null, 2)}\n${ownershipToken}\n`,
|
|
1564
|
+
ownershipToken,
|
|
1565
|
+
};
|
|
1561
1566
|
}
|
|
1562
|
-
async function
|
|
1567
|
+
async function readSidecarLockSnapshot(lockPath) {
|
|
1563
1568
|
try {
|
|
1564
1569
|
const stat = await promises_namespaceObject.lstat(lockPath);
|
|
1565
1570
|
const raw = await promises_namespaceObject.readFile(lockPath, "utf8");
|
|
@@ -1581,7 +1586,15 @@ async function readLockSnapshot(lockPath) {
|
|
|
1581
1586
|
throw err;
|
|
1582
1587
|
}
|
|
1583
1588
|
}
|
|
1584
|
-
function
|
|
1589
|
+
function sidecarLockSnapshotMatches(current, observed) {
|
|
1590
|
+
if (observed.ownershipToken !== undefined) {
|
|
1591
|
+
return (current.stat?.isFile() === true &&
|
|
1592
|
+
current.raw !== undefined &&
|
|
1593
|
+
observed.raw !== undefined &&
|
|
1594
|
+
readSidecarLockOwnershipToken(current.raw) === observed.ownershipToken &&
|
|
1595
|
+
readSidecarLockOwnershipToken(observed.raw) === observed.ownershipToken &&
|
|
1596
|
+
current.raw === observed.raw);
|
|
1597
|
+
}
|
|
1585
1598
|
if (observed.stat && current.stat && !file_identity_sameFileIdentity(observed.stat, current.stat)) {
|
|
1586
1599
|
return false;
|
|
1587
1600
|
}
|
|
@@ -1590,30 +1603,54 @@ function snapshotMatches(current, observed) {
|
|
|
1590
1603
|
}
|
|
1591
1604
|
return observed.stat !== undefined && current.stat !== undefined;
|
|
1592
1605
|
}
|
|
1593
|
-
async function
|
|
1594
|
-
const current = await
|
|
1595
|
-
if (!current || !observed) {
|
|
1596
|
-
return false;
|
|
1597
|
-
}
|
|
1598
|
-
if (!snapshotMatches(current, observed)) {
|
|
1599
|
-
// The lock changed after we decided it was stale. Leave the fresh holder's
|
|
1600
|
-
// file alone; deleting by path here would break mutual exclusion.
|
|
1606
|
+
async function removeSidecarLockIfUnchanged(lockPath, observed) {
|
|
1607
|
+
const current = await readSidecarLockSnapshot(lockPath);
|
|
1608
|
+
if (!current || !observed || !sidecarLockSnapshotMatches(current, observed)) {
|
|
1601
1609
|
return false;
|
|
1602
1610
|
}
|
|
1603
1611
|
await promises_namespaceObject.rm(lockPath, { force: true }).catch(() => undefined);
|
|
1604
1612
|
return true;
|
|
1605
1613
|
}
|
|
1606
|
-
async function
|
|
1607
|
-
const current = await
|
|
1608
|
-
return !!current && !!observed &&
|
|
1614
|
+
async function sidecarLockSnapshotStillPresent(lockPath, observed) {
|
|
1615
|
+
const current = await readSidecarLockSnapshot(lockPath);
|
|
1616
|
+
return !!current && !!observed && sidecarLockSnapshotMatches(current, observed);
|
|
1609
1617
|
}
|
|
1610
|
-
async function
|
|
1611
|
-
|
|
1612
|
-
|
|
1618
|
+
async function sidecarReclaimGuardExists(pathname) {
|
|
1619
|
+
try {
|
|
1620
|
+
await promises_namespaceObject.lstat(pathname);
|
|
1621
|
+
return true;
|
|
1622
|
+
}
|
|
1623
|
+
catch (err) {
|
|
1624
|
+
if (err.code === "ENOENT") {
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
throw err;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
async function tryAcquireSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
|
|
1631
|
+
try {
|
|
1632
|
+
await promises_namespaceObject.mkdir(reclaimGuardPath);
|
|
1633
|
+
reclaimGuards.add(reclaimGuardPath);
|
|
1634
|
+
return true;
|
|
1635
|
+
}
|
|
1636
|
+
catch (err) {
|
|
1637
|
+
if (err.code === "EEXIST") {
|
|
1638
|
+
return false;
|
|
1639
|
+
}
|
|
1640
|
+
throw err;
|
|
1613
1641
|
}
|
|
1614
|
-
|
|
1642
|
+
}
|
|
1643
|
+
async function releaseSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
|
|
1644
|
+
await promises_namespaceObject.rmdir(reclaimGuardPath);
|
|
1645
|
+
reclaimGuards.delete(reclaimGuardPath);
|
|
1646
|
+
}
|
|
1647
|
+
async function removeStaleSidecarLockIfAllowed(params) {
|
|
1648
|
+
if (!params.shouldRemoveStaleLock || params.snapshot.raw === undefined) {
|
|
1615
1649
|
return "not-approved";
|
|
1616
1650
|
}
|
|
1651
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot))) {
|
|
1652
|
+
return "changed";
|
|
1653
|
+
}
|
|
1617
1654
|
if (!(await params.shouldRemoveStaleLock({
|
|
1618
1655
|
lockPath: params.lockPath,
|
|
1619
1656
|
normalizedTargetPath: params.normalizedTargetPath,
|
|
@@ -1622,32 +1659,95 @@ async function removeStaleLockIfAllowed(params) {
|
|
|
1622
1659
|
}))) {
|
|
1623
1660
|
return "not-approved";
|
|
1624
1661
|
}
|
|
1625
|
-
|
|
1626
|
-
if (!current || !snapshotMatches(current, params.snapshot)) {
|
|
1662
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot))) {
|
|
1627
1663
|
return "changed";
|
|
1628
1664
|
}
|
|
1629
1665
|
try {
|
|
1630
|
-
await promises_namespaceObject.rm(params.lockPath
|
|
1666
|
+
await promises_namespaceObject.rm(params.lockPath);
|
|
1667
|
+
return "removed";
|
|
1631
1668
|
}
|
|
1632
1669
|
catch (err) {
|
|
1633
1670
|
if (err.code === "ENOENT") {
|
|
1634
1671
|
return "changed";
|
|
1635
1672
|
}
|
|
1636
|
-
|
|
1673
|
+
throw err;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
|
|
1682
|
+
|
|
1683
|
+
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
1684
|
+
function getGlobalManagers() {
|
|
1685
|
+
const globalWithState = globalThis;
|
|
1686
|
+
if (!globalWithState[GLOBAL_STATE_KEY]) {
|
|
1687
|
+
globalWithState[GLOBAL_STATE_KEY] = new Map();
|
|
1688
|
+
}
|
|
1689
|
+
return globalWithState[GLOBAL_STATE_KEY];
|
|
1690
|
+
}
|
|
1691
|
+
function resolveManagerState(key) {
|
|
1692
|
+
const managers = getGlobalManagers();
|
|
1693
|
+
let state = managers.get(key);
|
|
1694
|
+
if (!state) {
|
|
1695
|
+
state = {
|
|
1696
|
+
cleanupRegistered: false,
|
|
1697
|
+
held: new Map(),
|
|
1698
|
+
reclaimCleanupRegistered: false,
|
|
1699
|
+
reclaimGuards: new Set(),
|
|
1700
|
+
};
|
|
1701
|
+
managers.set(key, state);
|
|
1702
|
+
}
|
|
1703
|
+
else {
|
|
1704
|
+
// The global manager symbol is shared across package copies and hot reloads.
|
|
1705
|
+
// Backfill state created by fs-safe versions that predate reclaim guards.
|
|
1706
|
+
state.reclaimCleanupRegistered ??= false;
|
|
1707
|
+
state.reclaimGuards ??= new Set();
|
|
1637
1708
|
}
|
|
1638
|
-
return
|
|
1709
|
+
return state;
|
|
1639
1710
|
}
|
|
1640
1711
|
function snapshotMatchesSync(lockPath, observed) {
|
|
1712
|
+
let fd;
|
|
1641
1713
|
try {
|
|
1642
|
-
const
|
|
1643
|
-
if (
|
|
1714
|
+
const beforeStat = external_node_fs_namespaceObject.lstatSync(lockPath);
|
|
1715
|
+
if (!beforeStat.isFile()) {
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
const openFlags = external_node_fs_namespaceObject.constants.O_RDONLY |
|
|
1719
|
+
(process.platform !== "win32" && typeof external_node_fs_namespaceObject.constants.O_NOFOLLOW === "number"
|
|
1720
|
+
? external_node_fs_namespaceObject.constants.O_NOFOLLOW
|
|
1721
|
+
: 0) |
|
|
1722
|
+
(typeof external_node_fs_namespaceObject.constants.O_NONBLOCK === "number" ? external_node_fs_namespaceObject.constants.O_NONBLOCK : 0);
|
|
1723
|
+
fd = external_node_fs_namespaceObject.openSync(lockPath, openFlags);
|
|
1724
|
+
const openedStat = external_node_fs_namespaceObject.fstatSync(fd);
|
|
1725
|
+
if (!openedStat.isFile()) {
|
|
1644
1726
|
return false;
|
|
1645
1727
|
}
|
|
1646
|
-
|
|
1728
|
+
if (observed.raw !== undefined && openedStat.size !== Buffer.byteLength(observed.raw)) {
|
|
1729
|
+
return false;
|
|
1730
|
+
}
|
|
1731
|
+
const raw = external_node_fs_namespaceObject.readFileSync(fd, "utf8");
|
|
1732
|
+
const afterStat = external_node_fs_namespaceObject.lstatSync(lockPath);
|
|
1733
|
+
if (!afterStat.isFile() || !file_identity_sameFileIdentity(beforeStat, afterStat)) {
|
|
1734
|
+
return false;
|
|
1735
|
+
}
|
|
1736
|
+
return sidecarLockSnapshotMatches({ raw, payload: null, stat: afterStat }, observed);
|
|
1647
1737
|
}
|
|
1648
1738
|
catch {
|
|
1649
1739
|
return false;
|
|
1650
1740
|
}
|
|
1741
|
+
finally {
|
|
1742
|
+
if (fd !== undefined) {
|
|
1743
|
+
try {
|
|
1744
|
+
external_node_fs_namespaceObject.closeSync(fd);
|
|
1745
|
+
}
|
|
1746
|
+
catch {
|
|
1747
|
+
// Best-effort process-exit cleanup.
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1651
1751
|
}
|
|
1652
1752
|
async function resolveNormalizedTargetPath(targetPath) {
|
|
1653
1753
|
const resolved = external_node_path_namespaceObject.resolve(targetPath);
|
|
@@ -1682,6 +1782,17 @@ async function defaultShouldReclaim(params) {
|
|
|
1682
1782
|
return true;
|
|
1683
1783
|
}
|
|
1684
1784
|
}
|
|
1785
|
+
function releaseAllReclaimGuardsSync(state) {
|
|
1786
|
+
for (const reclaimGuardPath of state.reclaimGuards) {
|
|
1787
|
+
try {
|
|
1788
|
+
external_node_fs_namespaceObject.rmdirSync(reclaimGuardPath);
|
|
1789
|
+
state.reclaimGuards.delete(reclaimGuardPath);
|
|
1790
|
+
}
|
|
1791
|
+
catch {
|
|
1792
|
+
// Best-effort process-exit cleanup. A surviving guard fails closed.
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1685
1796
|
function releaseAllLocksSync(state) {
|
|
1686
1797
|
for (const [normalizedTargetPath, held] of state.held) {
|
|
1687
1798
|
void held.handle.close().catch(() => undefined);
|
|
@@ -1695,6 +1806,7 @@ function releaseAllLocksSync(state) {
|
|
|
1695
1806
|
}
|
|
1696
1807
|
state.held.delete(normalizedTargetPath);
|
|
1697
1808
|
}
|
|
1809
|
+
releaseAllReclaimGuardsSync(state);
|
|
1698
1810
|
}
|
|
1699
1811
|
async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
1700
1812
|
const current = state.held.get(normalizedTargetPath);
|
|
@@ -1717,7 +1829,7 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
1717
1829
|
state.held.delete(normalizedTargetPath);
|
|
1718
1830
|
held.releasePromise = (async () => {
|
|
1719
1831
|
await held.handle.close().catch(() => undefined);
|
|
1720
|
-
await
|
|
1832
|
+
await removeSidecarLockIfUnchanged(held.lockPath, held.snapshot);
|
|
1721
1833
|
})();
|
|
1722
1834
|
try {
|
|
1723
1835
|
await held.releasePromise;
|
|
@@ -1730,11 +1842,16 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
1730
1842
|
function createSidecarLockManager(key) {
|
|
1731
1843
|
const state = resolveManagerState(key);
|
|
1732
1844
|
function ensureExitCleanupRegistered() {
|
|
1733
|
-
if (state.cleanupRegistered) {
|
|
1845
|
+
if (!state.cleanupRegistered) {
|
|
1846
|
+
state.cleanupRegistered = true;
|
|
1847
|
+
state.reclaimCleanupRegistered = true;
|
|
1848
|
+
process.on("exit", () => releaseAllLocksSync(state));
|
|
1734
1849
|
return;
|
|
1735
1850
|
}
|
|
1736
|
-
state.
|
|
1737
|
-
|
|
1851
|
+
if (!state.reclaimCleanupRegistered) {
|
|
1852
|
+
state.reclaimCleanupRegistered = true;
|
|
1853
|
+
process.on("exit", () => releaseAllReclaimGuardsSync(state));
|
|
1854
|
+
}
|
|
1738
1855
|
}
|
|
1739
1856
|
async function acquire(options) {
|
|
1740
1857
|
ensureExitCleanupRegistered();
|
|
@@ -1754,108 +1871,146 @@ function createSidecarLockManager(key) {
|
|
|
1754
1871
|
const startedAt = Date.now();
|
|
1755
1872
|
const retry = options.retry ?? {};
|
|
1756
1873
|
const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
|
|
1874
|
+
const reclaimGuardPath = `${lockPath}.reclaim`;
|
|
1875
|
+
let ownsReclaimGuard = false;
|
|
1757
1876
|
let attempt = 0;
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
const createdHeld = {
|
|
1767
|
-
count: 1,
|
|
1768
|
-
handle,
|
|
1769
|
-
lockPath,
|
|
1770
|
-
snapshot,
|
|
1771
|
-
acquiredAt: Date.now(),
|
|
1772
|
-
metadata: options.metadata ?? {},
|
|
1773
|
-
};
|
|
1774
|
-
state.held.set(normalizedTargetPath, createdHeld);
|
|
1775
|
-
const release = () => releaseHeldLock(state, normalizedTargetPath, createdHeld).then(() => undefined);
|
|
1776
|
-
return {
|
|
1877
|
+
const waitForRetry = async () => {
|
|
1878
|
+
const elapsed = Date.now() - startedAt;
|
|
1879
|
+
if ((options.timeoutMs !== undefined &&
|
|
1880
|
+
options.timeoutMs !== Number.POSITIVE_INFINITY &&
|
|
1881
|
+
elapsed >= options.timeoutMs) ||
|
|
1882
|
+
(maxRetries !== undefined && attempt >= maxRetries)) {
|
|
1883
|
+
throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
1884
|
+
code: "file_lock_timeout",
|
|
1777
1885
|
lockPath,
|
|
1778
1886
|
normalizedTargetPath,
|
|
1779
|
-
|
|
1780
|
-
[Symbol.asyncDispose]: release,
|
|
1781
|
-
};
|
|
1887
|
+
});
|
|
1782
1888
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
state.held.delete(normalizedTargetPath);
|
|
1795
|
-
}
|
|
1796
|
-
// If payload serialization/write fails, the file may be empty or
|
|
1797
|
-
// partial JSON, so remove while our exclusive handle is still open.
|
|
1798
|
-
await promises_namespaceObject.rm(lockPath, { force: true }).catch(() => undefined);
|
|
1799
|
-
await handle.close().catch(() => undefined);
|
|
1800
|
-
// Windows can refuse removing an open file; retry after close but
|
|
1801
|
-
// only if the path still points at the file identity we created.
|
|
1802
|
-
await removeLockIfUnchanged(lockPath, failedSnapshot);
|
|
1803
|
-
}
|
|
1804
|
-
if (err.code !== "EEXIST") {
|
|
1805
|
-
throw err;
|
|
1806
|
-
}
|
|
1807
|
-
const nowMs = Date.now();
|
|
1808
|
-
const snapshot = await readLockSnapshot(lockPath);
|
|
1809
|
-
if (!snapshot) {
|
|
1889
|
+
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
1890
|
+
? Number.POSITIVE_INFINITY
|
|
1891
|
+
: Math.max(0, options.timeoutMs - elapsed);
|
|
1892
|
+
const delay = Math.min(computeDelayMs(retry, attempt), remaining);
|
|
1893
|
+
attempt += 1;
|
|
1894
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1895
|
+
};
|
|
1896
|
+
try {
|
|
1897
|
+
while (true) {
|
|
1898
|
+
if (!ownsReclaimGuard && (await sidecarReclaimGuardExists(reclaimGuardPath))) {
|
|
1899
|
+
await waitForRetry();
|
|
1810
1900
|
continue;
|
|
1811
1901
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
lockPath,
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1902
|
+
let handle = null;
|
|
1903
|
+
try {
|
|
1904
|
+
handle = await promises_namespaceObject.open(lockPath, "wx");
|
|
1905
|
+
const payload = await options.payload();
|
|
1906
|
+
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
1907
|
+
await handle.writeFile(raw, "utf8");
|
|
1908
|
+
const snapshot = { raw, payload, stat: await handle.stat(), ownershipToken };
|
|
1909
|
+
const createdHeld = {
|
|
1910
|
+
count: 1,
|
|
1911
|
+
handle,
|
|
1912
|
+
lockPath,
|
|
1913
|
+
snapshot,
|
|
1914
|
+
acquiredAt: Date.now(),
|
|
1915
|
+
metadata: options.metadata ?? {},
|
|
1916
|
+
};
|
|
1917
|
+
state.held.set(normalizedTargetPath, createdHeld);
|
|
1918
|
+
if (ownsReclaimGuard) {
|
|
1919
|
+
try {
|
|
1920
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
1921
|
+
ownsReclaimGuard = false;
|
|
1922
|
+
}
|
|
1923
|
+
catch (err) {
|
|
1924
|
+
await releaseHeldLock(state, normalizedTargetPath, createdHeld, { force: true });
|
|
1925
|
+
throw err;
|
|
1834
1926
|
}
|
|
1835
1927
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1928
|
+
const release = () => releaseHeldLock(state, normalizedTargetPath, createdHeld).then(() => undefined);
|
|
1929
|
+
return {
|
|
1838
1930
|
lockPath,
|
|
1839
1931
|
normalizedTargetPath,
|
|
1840
|
-
|
|
1932
|
+
release,
|
|
1933
|
+
[Symbol.asyncDispose]: release,
|
|
1934
|
+
};
|
|
1841
1935
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1936
|
+
catch (err) {
|
|
1937
|
+
if (handle) {
|
|
1938
|
+
const failedSnapshot = { payload: null };
|
|
1939
|
+
try {
|
|
1940
|
+
failedSnapshot.stat = await handle.stat();
|
|
1941
|
+
}
|
|
1942
|
+
catch {
|
|
1943
|
+
// Best-effort cleanup of a failed exclusive create.
|
|
1944
|
+
}
|
|
1945
|
+
const current = state.held.get(normalizedTargetPath);
|
|
1946
|
+
if (current?.handle === handle) {
|
|
1947
|
+
state.held.delete(normalizedTargetPath);
|
|
1948
|
+
}
|
|
1949
|
+
// If payload serialization/write fails, the file may be empty or
|
|
1950
|
+
// partial JSON, so remove while our exclusive handle is still open.
|
|
1951
|
+
await promises_namespaceObject.rm(lockPath, { force: true }).catch(() => undefined);
|
|
1952
|
+
await handle.close().catch(() => undefined);
|
|
1953
|
+
// Windows can refuse removing an open file; retry after close but
|
|
1954
|
+
// only if the path still points at the file identity we created.
|
|
1955
|
+
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot);
|
|
1956
|
+
}
|
|
1957
|
+
if (err.code !== "EEXIST") {
|
|
1958
|
+
throw err;
|
|
1959
|
+
}
|
|
1960
|
+
if (ownsReclaimGuard) {
|
|
1961
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
1962
|
+
ownsReclaimGuard = false;
|
|
1963
|
+
continue;
|
|
1964
|
+
}
|
|
1965
|
+
const nowMs = Date.now();
|
|
1966
|
+
const snapshot = await readSidecarLockSnapshot(lockPath);
|
|
1967
|
+
if (!snapshot) {
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
const shouldReclaim = options.shouldReclaim ?? defaultShouldReclaim;
|
|
1971
|
+
if (await shouldReclaim({
|
|
1849
1972
|
lockPath,
|
|
1850
1973
|
normalizedTargetPath,
|
|
1851
|
-
|
|
1974
|
+
payload: snapshot?.payload ?? null,
|
|
1975
|
+
staleMs: options.staleMs,
|
|
1976
|
+
nowMs,
|
|
1977
|
+
heldByThisProcess: state.held.has(normalizedTargetPath),
|
|
1978
|
+
})) {
|
|
1979
|
+
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot))) {
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
1983
|
+
if (staleRecovery === "remove-if-unchanged") {
|
|
1984
|
+
if (!(await tryAcquireSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath))) {
|
|
1985
|
+
await waitForRetry();
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
ownsReclaimGuard = true;
|
|
1989
|
+
const removal = await removeStaleSidecarLockIfAllowed({
|
|
1990
|
+
lockPath,
|
|
1991
|
+
normalizedTargetPath,
|
|
1992
|
+
snapshot,
|
|
1993
|
+
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
1994
|
+
});
|
|
1995
|
+
if (removal === "removed" || removal === "changed") {
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
1999
|
+
ownsReclaimGuard = false;
|
|
2000
|
+
}
|
|
2001
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
2002
|
+
code: "file_lock_stale",
|
|
2003
|
+
lockPath,
|
|
2004
|
+
normalizedTargetPath,
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
await waitForRetry();
|
|
1852
2008
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
finally {
|
|
2012
|
+
if (ownsReclaimGuard) {
|
|
2013
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath).catch(() => undefined);
|
|
1859
2014
|
}
|
|
1860
2015
|
}
|
|
1861
2016
|
}
|
|
@@ -1942,6 +2097,16 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
1942
2097
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
1943
2098
|
}
|
|
1944
2099
|
}
|
|
2100
|
+
async function syncFileBestEffort(handle) {
|
|
2101
|
+
try {
|
|
2102
|
+
await handle.sync();
|
|
2103
|
+
}
|
|
2104
|
+
catch (error) {
|
|
2105
|
+
if (error?.code !== "EPERM") {
|
|
2106
|
+
throw error;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1945
2110
|
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
1946
2111
|
let bytes = 0;
|
|
1947
2112
|
for await (const chunk of stream) {
|
|
@@ -2103,7 +2268,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
2103
2268
|
else {
|
|
2104
2269
|
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
2105
2270
|
}
|
|
2106
|
-
await handle
|
|
2271
|
+
await syncFileBestEffort(handle);
|
|
2107
2272
|
const stat = await handle.stat();
|
|
2108
2273
|
await handle.close().catch(() => undefined);
|
|
2109
2274
|
await syncDirectoryBestEffort(parentPath);
|
|
@@ -2148,7 +2313,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
2148
2313
|
throw new errors_FsSafeError("path-mismatch", "fallback temp path changed during write");
|
|
2149
2314
|
}
|
|
2150
2315
|
const expectedTempStat = tempStat;
|
|
2151
|
-
await handle
|
|
2316
|
+
await syncFileBestEffort(handle);
|
|
2152
2317
|
await handle.close().catch(() => undefined);
|
|
2153
2318
|
handle = undefined;
|
|
2154
2319
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
@@ -3057,16 +3222,104 @@ function assertNoUnsafeDeviceReadPath(filePath, options) {
|
|
|
3057
3222
|
}
|
|
3058
3223
|
}
|
|
3059
3224
|
|
|
3225
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
3226
|
+
|
|
3227
|
+
|
|
3228
|
+
const READ_CHUNK_BYTES = 64 * 1024;
|
|
3229
|
+
function assertMaxBytes(maxBytes) {
|
|
3230
|
+
if (maxBytes === Number.POSITIVE_INFINITY) {
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
3234
|
+
throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
function createScratchBuffer(maxBytes) {
|
|
3238
|
+
const initialReadBytes = Number.isFinite(maxBytes)
|
|
3239
|
+
? Math.min(READ_CHUNK_BYTES, maxBytes + 1)
|
|
3240
|
+
: READ_CHUNK_BYTES;
|
|
3241
|
+
return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
|
|
3242
|
+
}
|
|
3243
|
+
function nextReadLength(total, maxBytes, capacity) {
|
|
3244
|
+
return Number.isFinite(maxBytes)
|
|
3245
|
+
? Math.min(capacity, maxBytes - total + 1)
|
|
3246
|
+
: capacity;
|
|
3247
|
+
}
|
|
3248
|
+
function appendChunk(params) {
|
|
3249
|
+
const total = params.total + params.bytesRead;
|
|
3250
|
+
if (total > params.maxBytes) {
|
|
3251
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got at least ${total})`);
|
|
3252
|
+
}
|
|
3253
|
+
params.chunks.push(Buffer.from(params.scratch.subarray(0, params.bytesRead)));
|
|
3254
|
+
return total;
|
|
3255
|
+
}
|
|
3256
|
+
async function readBoundedAsync(maxBytes, readChunk) {
|
|
3257
|
+
assertMaxBytes(maxBytes);
|
|
3258
|
+
const chunks = [];
|
|
3259
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
3260
|
+
let total = 0;
|
|
3261
|
+
while (true) {
|
|
3262
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
3263
|
+
const bytesRead = await readChunk(scratch, length);
|
|
3264
|
+
if (bytesRead === 0) {
|
|
3265
|
+
return Buffer.concat(chunks, total);
|
|
3266
|
+
}
|
|
3267
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
/**
|
|
3271
|
+
* Reads from the handle's current offset without closing it. A bounded read
|
|
3272
|
+
* consumes at most maxBytes + 1 bytes so growth after an earlier stat cannot
|
|
3273
|
+
* force an unbounded allocation.
|
|
3274
|
+
*/
|
|
3275
|
+
async function readFileHandleBounded(handle, maxBytes) {
|
|
3276
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
3277
|
+
return (await handle.read(scratch, 0, length, null)).bytesRead;
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3280
|
+
function readDescriptorChunk(fd, scratch, length) {
|
|
3281
|
+
return new Promise((resolve, reject) => {
|
|
3282
|
+
fs.read(fd, scratch, 0, length, null, (error, bytesRead) => {
|
|
3283
|
+
if (error) {
|
|
3284
|
+
reject(error);
|
|
3285
|
+
return;
|
|
3286
|
+
}
|
|
3287
|
+
resolve(bytesRead);
|
|
3288
|
+
});
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
/** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
3292
|
+
async function readFileDescriptorBounded(fd, maxBytes) {
|
|
3293
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
3294
|
+
return await readDescriptorChunk(fd, scratch, length);
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
3298
|
+
function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
3299
|
+
assertMaxBytes(maxBytes);
|
|
3300
|
+
const chunks = [];
|
|
3301
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
3302
|
+
let total = 0;
|
|
3303
|
+
while (true) {
|
|
3304
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
3305
|
+
const bytesRead = fs.readSync(fd, scratch, 0, length, null);
|
|
3306
|
+
if (bytesRead === 0) {
|
|
3307
|
+
return Buffer.concat(chunks, total);
|
|
3308
|
+
}
|
|
3309
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3060
3313
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/read-opened-file.js
|
|
3061
3314
|
|
|
3315
|
+
|
|
3062
3316
|
async function read_opened_file_readOpenedFileSafely(params) {
|
|
3063
3317
|
if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
|
|
3064
3318
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
|
|
3065
3319
|
}
|
|
3066
|
-
const buffer =
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
}
|
|
3320
|
+
const buffer = params.maxBytes === undefined
|
|
3321
|
+
? await params.opened.handle.readFile()
|
|
3322
|
+
: await readFileHandleBounded(params.opened.handle, params.maxBytes);
|
|
3070
3323
|
return {
|
|
3071
3324
|
buffer,
|
|
3072
3325
|
realPath: params.opened.realPath,
|
|
@@ -15713,4 +15966,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
15713
15966
|
// module factories are used so entry inlining is disabled
|
|
15714
15967
|
// startup
|
|
15715
15968
|
// Load entry module and return exports
|
|
15716
|
-
var __webpack_exports__ = __webpack_require__(
|
|
15969
|
+
var __webpack_exports__ = __webpack_require__(953);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lousy-agents/agent-shell",
|
|
3
|
-
"version": "5.17.
|
|
3
|
+
"version": "5.17.10",
|
|
4
4
|
"description": "A flight recorder for npm script execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"build": "rspack build --config rspack.config.ts && chmod +x dist/index.js"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@openclaw/fs-safe": "0.4.
|
|
39
|
+
"@openclaw/fs-safe": "0.4.5",
|
|
40
40
|
"zod": "4.4.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@github/copilot-sdk": "1.0.
|
|
43
|
+
"@github/copilot-sdk": "1.0.8"
|
|
44
44
|
},
|
|
45
45
|
"peerDependenciesMeta": {
|
|
46
46
|
"@github/copilot-sdk": {
|