@lousy-agents/agent-shell 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.
- package/dist/index.js +143 -48
- package/package.json +2 -2
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
|
+
145(__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");
|
|
@@ -340,7 +340,7 @@ function splitSafeRelativePath(relativePath) {
|
|
|
340
340
|
function resolveSafeRelativePath(rootDir, relativePath) {
|
|
341
341
|
const root = path.resolve(rootDir);
|
|
342
342
|
const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
|
|
343
|
-
if (
|
|
343
|
+
if (!path_isPathInside(root, target)) {
|
|
344
344
|
throw new FsSafeError("outside-workspace", "relative path escapes root");
|
|
345
345
|
}
|
|
346
346
|
return target;
|
|
@@ -673,6 +673,85 @@ def reject_unsafe_endpoint(st):
|
|
|
673
673
|
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
674
674
|
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
675
675
|
|
|
676
|
+
def copy_bytes(source_fd, dest_fd):
|
|
677
|
+
while True:
|
|
678
|
+
chunk = os.read(source_fd, 65536)
|
|
679
|
+
if not chunk:
|
|
680
|
+
break
|
|
681
|
+
view = memoryview(chunk)
|
|
682
|
+
while view:
|
|
683
|
+
written = os.write(dest_fd, view)
|
|
684
|
+
if written <= 0:
|
|
685
|
+
raise OSError(errno.EIO, "short write")
|
|
686
|
+
view = view[written:]
|
|
687
|
+
|
|
688
|
+
def write_all(fd, data):
|
|
689
|
+
view = memoryview(data)
|
|
690
|
+
while view:
|
|
691
|
+
written = os.write(fd, view)
|
|
692
|
+
if written <= 0:
|
|
693
|
+
raise OSError(errno.EIO, "short write")
|
|
694
|
+
view = view[written:]
|
|
695
|
+
|
|
696
|
+
def link_unsupported(exc):
|
|
697
|
+
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
698
|
+
return getattr(exc, "errno", None) in unsupported
|
|
699
|
+
|
|
700
|
+
def link_no_replace(name, new_name, source_fd, target_fd):
|
|
701
|
+
linked = False
|
|
702
|
+
try:
|
|
703
|
+
os.link(name, new_name, src_dir_fd=source_fd, dst_dir_fd=target_fd, follow_symlinks=False)
|
|
704
|
+
linked = True
|
|
705
|
+
os.unlink(name, dir_fd=source_fd)
|
|
706
|
+
except Exception:
|
|
707
|
+
if linked:
|
|
708
|
+
try: os.unlink(new_name, dir_fd=target_fd)
|
|
709
|
+
except FileNotFoundError: pass
|
|
710
|
+
raise
|
|
711
|
+
os.fsync(source_fd)
|
|
712
|
+
if source_fd != target_fd:
|
|
713
|
+
os.fsync(target_fd)
|
|
714
|
+
|
|
715
|
+
def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
|
|
716
|
+
source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
|
|
717
|
+
dest_fd = None
|
|
718
|
+
success = False
|
|
719
|
+
try:
|
|
720
|
+
if expected is not None:
|
|
721
|
+
source_stat = os.fstat(source_fd)
|
|
722
|
+
if source_stat.st_dev != expected.st_dev or source_stat.st_ino != expected.st_ino:
|
|
723
|
+
raise RuntimeError("fs-safe-source-mismatch")
|
|
724
|
+
dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
|
|
725
|
+
copy_bytes(source_fd, dest_fd)
|
|
726
|
+
os.fsync(dest_fd)
|
|
727
|
+
success = True
|
|
728
|
+
finally:
|
|
729
|
+
os.close(source_fd)
|
|
730
|
+
if dest_fd is not None:
|
|
731
|
+
os.close(dest_fd)
|
|
732
|
+
if dest_fd is not None and not success:
|
|
733
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
734
|
+
except FileNotFoundError: pass
|
|
735
|
+
if unlink_source:
|
|
736
|
+
try:
|
|
737
|
+
os.unlink(source_name, dir_fd=source_parent_fd)
|
|
738
|
+
except Exception:
|
|
739
|
+
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
740
|
+
except FileNotFoundError: pass
|
|
741
|
+
raise
|
|
742
|
+
|
|
743
|
+
def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode):
|
|
744
|
+
if overwrite:
|
|
745
|
+
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
746
|
+
else:
|
|
747
|
+
try:
|
|
748
|
+
os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
|
|
749
|
+
os.unlink(temp_name, dir_fd=parent_fd)
|
|
750
|
+
except OSError as exc:
|
|
751
|
+
if not link_unsupported(exc):
|
|
752
|
+
raise
|
|
753
|
+
copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, unlink_source=True)
|
|
754
|
+
|
|
676
755
|
def stat_path(root_fd, payload):
|
|
677
756
|
relative = payload.get("relativePath", "")
|
|
678
757
|
segments = split_relative(relative)
|
|
@@ -747,7 +826,18 @@ def rename_path(root_fd, payload):
|
|
|
747
826
|
try:
|
|
748
827
|
from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
|
|
749
828
|
reject_unsafe_endpoint(from_stat)
|
|
750
|
-
|
|
829
|
+
overwrite = payload.get("overwrite", True)
|
|
830
|
+
if not overwrite and stat.S_ISREG(from_stat.st_mode):
|
|
831
|
+
try:
|
|
832
|
+
link_no_replace(from_base, to_base, from_parent_fd, to_parent_fd)
|
|
833
|
+
except OSError as exc:
|
|
834
|
+
if not link_unsupported(exc):
|
|
835
|
+
raise
|
|
836
|
+
copy_file_no_replace(from_parent_fd, from_base, to_parent_fd, to_base, stat.S_IMODE(from_stat.st_mode), from_stat, True)
|
|
837
|
+
return None
|
|
838
|
+
if not overwrite and stat.S_ISDIR(from_stat.st_mode):
|
|
839
|
+
raise RuntimeError("fs-safe-directory-noreplace-unsupported")
|
|
840
|
+
if not overwrite:
|
|
751
841
|
try:
|
|
752
842
|
os.lstat(to_base, dir_fd=to_parent_fd)
|
|
753
843
|
raise FileExistsError(errno.EEXIST, "destination exists", to_base)
|
|
@@ -792,16 +882,11 @@ def write_path(root_fd, payload):
|
|
|
792
882
|
except FileNotFoundError:
|
|
793
883
|
pass
|
|
794
884
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
795
|
-
|
|
796
|
-
while view:
|
|
797
|
-
written = os.write(temp_fd, view)
|
|
798
|
-
if written <= 0:
|
|
799
|
-
raise OSError(errno.EIO, "short write")
|
|
800
|
-
view = view[written:]
|
|
885
|
+
write_all(temp_fd, data)
|
|
801
886
|
os.fsync(temp_fd)
|
|
802
887
|
os.close(temp_fd)
|
|
803
888
|
temp_fd = None
|
|
804
|
-
|
|
889
|
+
commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
|
|
805
890
|
temp_name = None
|
|
806
891
|
os.fsync(parent_fd)
|
|
807
892
|
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
@@ -834,12 +919,6 @@ def copy_path(root_fd, payload):
|
|
|
834
919
|
if max_bytes >= 0 and source_stat.st_size > max_bytes:
|
|
835
920
|
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
836
921
|
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
837
|
-
if not overwrite:
|
|
838
|
-
try:
|
|
839
|
-
os.lstat(basename, dir_fd=parent_fd)
|
|
840
|
-
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
841
|
-
except FileNotFoundError:
|
|
842
|
-
pass
|
|
843
922
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
844
923
|
written_bytes = 0
|
|
845
924
|
while True:
|
|
@@ -858,7 +937,7 @@ def copy_path(root_fd, payload):
|
|
|
858
937
|
os.fsync(temp_fd)
|
|
859
938
|
os.close(temp_fd)
|
|
860
939
|
temp_fd = None
|
|
861
|
-
|
|
940
|
+
commit_temp_file(parent_fd, temp_name, basename, overwrite, mode)
|
|
862
941
|
temp_name = None
|
|
863
942
|
os.fsync(parent_fd)
|
|
864
943
|
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
@@ -982,6 +1061,9 @@ function mapWorkerError(response) {
|
|
|
982
1061
|
if (message.includes("fs-safe-source-mismatch")) {
|
|
983
1062
|
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
984
1063
|
}
|
|
1064
|
+
if (message.includes("fs-safe-directory-noreplace-unsupported")) {
|
|
1065
|
+
return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
1066
|
+
}
|
|
985
1067
|
if (code === "FileNotFoundError" || errno === 2) {
|
|
986
1068
|
return new errors_FsSafeError("not-found", "file not found");
|
|
987
1069
|
}
|
|
@@ -999,19 +1081,19 @@ function mapWorkerError(response) {
|
|
|
999
1081
|
}
|
|
1000
1082
|
return new errors_FsSafeError("helper-failed", message);
|
|
1001
1083
|
}
|
|
1002
|
-
function rejectPending(error) {
|
|
1003
|
-
if (!worker) {
|
|
1084
|
+
function rejectPending(error, targetWorker = worker) {
|
|
1085
|
+
if (!targetWorker || worker !== targetWorker) {
|
|
1004
1086
|
return;
|
|
1005
1087
|
}
|
|
1006
|
-
setWorkerRef(
|
|
1007
|
-
for (const pending of
|
|
1088
|
+
setWorkerRef(targetWorker, false);
|
|
1089
|
+
for (const pending of targetWorker.pending.values()) {
|
|
1008
1090
|
pending.reject(error);
|
|
1009
1091
|
}
|
|
1010
|
-
|
|
1092
|
+
targetWorker.pending.clear();
|
|
1011
1093
|
worker = null;
|
|
1012
1094
|
}
|
|
1013
|
-
function handleWorkerLine(line) {
|
|
1014
|
-
if (
|
|
1095
|
+
function handleWorkerLine(currentWorker, line) {
|
|
1096
|
+
if (worker !== currentWorker || !line.trim()) {
|
|
1015
1097
|
return;
|
|
1016
1098
|
}
|
|
1017
1099
|
let decoded;
|
|
@@ -1019,11 +1101,11 @@ function handleWorkerLine(line) {
|
|
|
1019
1101
|
decoded = JSON.parse(line);
|
|
1020
1102
|
}
|
|
1021
1103
|
catch {
|
|
1022
|
-
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
|
|
1104
|
+
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`), currentWorker);
|
|
1023
1105
|
return;
|
|
1024
1106
|
}
|
|
1025
1107
|
if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
|
|
1026
|
-
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
|
|
1108
|
+
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"), currentWorker);
|
|
1027
1109
|
return;
|
|
1028
1110
|
}
|
|
1029
1111
|
const response = decoded;
|
|
@@ -1031,13 +1113,13 @@ function handleWorkerLine(line) {
|
|
|
1031
1113
|
if (id === undefined) {
|
|
1032
1114
|
return;
|
|
1033
1115
|
}
|
|
1034
|
-
const pending =
|
|
1116
|
+
const pending = currentWorker.pending.get(id);
|
|
1035
1117
|
if (!pending) {
|
|
1036
1118
|
return;
|
|
1037
1119
|
}
|
|
1038
|
-
|
|
1039
|
-
if (
|
|
1040
|
-
setWorkerRef(
|
|
1120
|
+
currentWorker.pending.delete(id);
|
|
1121
|
+
if (currentWorker.pending.size === 0) {
|
|
1122
|
+
setWorkerRef(currentWorker, false);
|
|
1041
1123
|
}
|
|
1042
1124
|
if (response.ok === true) {
|
|
1043
1125
|
pending.resolve(response.result);
|
|
@@ -1053,28 +1135,28 @@ function getWorker() {
|
|
|
1053
1135
|
const child = (0,external_node_child_process_namespaceObject.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
|
|
1054
1136
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1055
1137
|
});
|
|
1056
|
-
|
|
1138
|
+
const currentWorker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
|
|
1139
|
+
worker = currentWorker;
|
|
1057
1140
|
child.stdout.setEncoding("utf8");
|
|
1058
1141
|
child.stderr.setEncoding("utf8");
|
|
1059
1142
|
child.stdout.on("data", (chunk) => {
|
|
1060
|
-
|
|
1061
|
-
if (!current) {
|
|
1143
|
+
if (worker !== currentWorker) {
|
|
1062
1144
|
return;
|
|
1063
1145
|
}
|
|
1064
|
-
|
|
1146
|
+
currentWorker.stdoutBuffer += chunk;
|
|
1065
1147
|
for (;;) {
|
|
1066
|
-
const newline =
|
|
1148
|
+
const newline = currentWorker.stdoutBuffer.indexOf("\n");
|
|
1067
1149
|
if (newline < 0) {
|
|
1068
1150
|
break;
|
|
1069
1151
|
}
|
|
1070
|
-
const line =
|
|
1071
|
-
|
|
1072
|
-
handleWorkerLine(line);
|
|
1152
|
+
const line = currentWorker.stdoutBuffer.slice(0, newline);
|
|
1153
|
+
currentWorker.stdoutBuffer = currentWorker.stdoutBuffer.slice(newline + 1);
|
|
1154
|
+
handleWorkerLine(currentWorker, line);
|
|
1073
1155
|
}
|
|
1074
1156
|
});
|
|
1075
1157
|
child.stderr.on("data", (chunk) => {
|
|
1076
|
-
if (worker) {
|
|
1077
|
-
|
|
1158
|
+
if (worker === currentWorker) {
|
|
1159
|
+
currentWorker.stderr = `${currentWorker.stderr}${chunk}`.slice(-4096);
|
|
1078
1160
|
}
|
|
1079
1161
|
});
|
|
1080
1162
|
child.once("error", (error) => {
|
|
@@ -1083,17 +1165,17 @@ function getWorker() {
|
|
|
1083
1165
|
: error instanceof Error
|
|
1084
1166
|
? error
|
|
1085
1167
|
: new Error(String(error));
|
|
1086
|
-
rejectPending(mapped);
|
|
1168
|
+
rejectPending(mapped, currentWorker);
|
|
1087
1169
|
});
|
|
1088
1170
|
child.once("close", (code, signal) => {
|
|
1089
|
-
const stderr =
|
|
1090
|
-
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
|
|
1171
|
+
const stderr = currentWorker.stderr.trim();
|
|
1172
|
+
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`), currentWorker);
|
|
1091
1173
|
});
|
|
1092
1174
|
process.once("exit", () => {
|
|
1093
1175
|
child.kill("SIGTERM");
|
|
1094
1176
|
});
|
|
1095
|
-
setWorkerRef(
|
|
1096
|
-
return
|
|
1177
|
+
setWorkerRef(currentWorker, false);
|
|
1178
|
+
return currentWorker;
|
|
1097
1179
|
}
|
|
1098
1180
|
function setRefable(value, ref) {
|
|
1099
1181
|
if (!value) {
|
|
@@ -2340,6 +2422,15 @@ function __setFsSafeTestHooksForTest(hooks) {
|
|
|
2340
2422
|
test_hooks_fsSafeTestHooks = hooks;
|
|
2341
2423
|
}
|
|
2342
2424
|
|
|
2425
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
|
|
2426
|
+
function stringifyJsonDocument(value, replacer, space) {
|
|
2427
|
+
const text = JSON.stringify(value, replacer, space);
|
|
2428
|
+
if (typeof text !== "string") {
|
|
2429
|
+
throw new TypeError("value is not representable as a JSON document");
|
|
2430
|
+
}
|
|
2431
|
+
return text;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2343
2434
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
|
|
2344
2435
|
|
|
2345
2436
|
const tempCleanupEntries = new Map();
|
|
@@ -2427,6 +2518,7 @@ async function serializePathWrite(key, run) {
|
|
|
2427
2518
|
|
|
2428
2519
|
|
|
2429
2520
|
|
|
2521
|
+
|
|
2430
2522
|
|
|
2431
2523
|
|
|
2432
2524
|
function logWarn(message) {
|
|
@@ -2667,12 +2759,12 @@ class RootHandle {
|
|
|
2667
2759
|
}
|
|
2668
2760
|
async writeJson(relativePath, data, options = {}) {
|
|
2669
2761
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
2670
|
-
const json =
|
|
2762
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
2671
2763
|
await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
2672
2764
|
}
|
|
2673
2765
|
async createJson(relativePath, data, options = {}) {
|
|
2674
2766
|
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
2675
|
-
const json =
|
|
2767
|
+
const json = stringifyJsonDocument(data, replacer, space);
|
|
2676
2768
|
await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
2677
2769
|
}
|
|
2678
2770
|
async copyIn(relativePath, sourcePath, options = {}) {
|
|
@@ -3457,6 +3549,9 @@ async function movePathFallback(root, params) {
|
|
|
3457
3549
|
if (sourceStat.isFile() && sourceStat.nlink > 1) {
|
|
3458
3550
|
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
3459
3551
|
}
|
|
3552
|
+
if (!params.overwrite && sourceStat.isDirectory()) {
|
|
3553
|
+
throw new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
3554
|
+
}
|
|
3460
3555
|
if (!params.overwrite) {
|
|
3461
3556
|
try {
|
|
3462
3557
|
await promises_namespaceObject.lstat(target.resolved);
|
|
@@ -14782,4 +14877,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
14782
14877
|
// module factories are used so entry inlining is disabled
|
|
14783
14878
|
// startup
|
|
14784
14879
|
// Load entry module and return exports
|
|
14785
|
-
var __webpack_exports__ = __webpack_require__(
|
|
14880
|
+
var __webpack_exports__ = __webpack_require__(145);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lousy-agents/agent-shell",
|
|
3
|
-
"version": "5.14.
|
|
3
|
+
"version": "5.14.9",
|
|
4
4
|
"description": "A flight recorder for npm script execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"build": "rspack build --config rspack.config.ts && chmod +x dist/index.js"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@openclaw/fs-safe": "0.2.
|
|
39
|
+
"@openclaw/fs-safe": "0.2.5",
|
|
40
40
|
"zod": "4.4.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|