@lousy-agents/lint 5.14.1 → 5.14.2

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 +3798 -126
  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
- 5144(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
569
+ 2202(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
570
570
 
571
571
  // EXPORTS
572
572
  __webpack_require__.d(__webpack_exports__, {
@@ -753,8 +753,6 @@ __webpack_require__.d(constructs_namespaceObject, {
753
753
  }
754
754
  }
755
755
 
756
- // EXTERNAL MODULE: external "node:fs/promises"
757
- var promises_ = __webpack_require__(1455);
758
756
  // EXTERNAL MODULE: external "node:path"
759
757
  var external_node_path_ = __webpack_require__(6760);
760
758
  // EXTERNAL MODULE: ../../node_modules/yaml/dist/index.js
@@ -777,73 +775,3690 @@ var dist = __webpack_require__(3519);
777
775
  reason: "missing"
778
776
  };
779
777
  }
780
- let endIndex = -1;
781
- for(let i = 1; i < lines.length; i++){
782
- if (lines[i]?.trim() === "---") {
783
- endIndex = i;
784
- break;
778
+ let endIndex = -1;
779
+ for(let i = 1; i < lines.length; i++){
780
+ if (lines[i]?.trim() === "---") {
781
+ endIndex = i;
782
+ break;
783
+ }
784
+ }
785
+ if (endIndex === -1) {
786
+ return {
787
+ ok: false,
788
+ reason: "missing"
789
+ };
790
+ }
791
+ const yamlContent = lines.slice(1, endIndex).join("\n");
792
+ let data;
793
+ try {
794
+ const parsed = (0,dist/* .parse */.qg)(yamlContent, {
795
+ maxAliasCount: 0
796
+ });
797
+ data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
798
+ } catch (error) {
799
+ const detail = error instanceof Error ? error.message : "Unknown YAML parse error";
800
+ return {
801
+ ok: false,
802
+ reason: "invalid",
803
+ detail
804
+ };
805
+ }
806
+ const fieldLines = new Map();
807
+ for(let i = 1; i < endIndex; i++){
808
+ const match = lines[i]?.match(/^([^\s:][^:]*?):(?:\s|$)/);
809
+ if (match?.[1]) {
810
+ fieldLines.set(match[1], i + 1);
811
+ }
812
+ }
813
+ return {
814
+ ok: true,
815
+ data: {
816
+ data: data ?? {},
817
+ fieldLines,
818
+ frontmatterStartLine: 1
819
+ }
820
+ };
821
+ }
822
+ /**
823
+ * Parses YAML frontmatter from markdown content.
824
+ *
825
+ * Returns `null` when the content has no opening `---`, no closing `---`,
826
+ * or contains invalid YAML.
827
+ */ function parseFrontmatter(content) {
828
+ const result = parseFrontmatterWithError(content);
829
+ return result.ok ? result.data : null;
830
+ }
831
+
832
+ // EXTERNAL MODULE: external "node:fs"
833
+ var external_node_fs_ = __webpack_require__(3024);
834
+ // EXTERNAL MODULE: external "node:fs/promises"
835
+ var promises_ = __webpack_require__(1455);
836
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/errors.js
837
+ const OPERATIONAL_CODES = new Set([
838
+ "helper-failed",
839
+ "helper-unavailable",
840
+ "permission-unverified",
841
+ "timeout",
842
+ "unsupported-platform",
843
+ ]);
844
+ function categorizeFsSafeError(code) {
845
+ return OPERATIONAL_CODES.has(code) ? "operational" : "policy";
846
+ }
847
+ class errors_FsSafeError extends Error {
848
+ code;
849
+ category;
850
+ constructor(code, message, options = {}) {
851
+ super(message, options);
852
+ this.name = "FsSafeError";
853
+ this.code = code;
854
+ this.category = categorizeFsSafeError(code);
855
+ }
856
+ }
857
+
858
+ // EXTERNAL MODULE: external "node:crypto"
859
+ var external_node_crypto_ = __webpack_require__(7598);
860
+ // EXTERNAL MODULE: external "node:stream/promises"
861
+ var external_node_stream_promises_ = __webpack_require__(6466);
862
+ // EXTERNAL MODULE: external "node:stream"
863
+ var external_node_stream_ = __webpack_require__(7075);
864
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read-stream.js
865
+
866
+
867
+ function createMaxBytesTransform(maxBytes) {
868
+ let bytes = 0;
869
+ return new external_node_stream_.Transform({
870
+ transform(chunk, _encoding, callback) {
871
+ const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
872
+ bytes += buffer.byteLength;
873
+ if (bytes > maxBytes) {
874
+ callback(new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
875
+ return;
876
+ }
877
+ callback(null, buffer);
878
+ },
879
+ });
880
+ }
881
+ function createBoundedReadStream(opened, maxBytes) {
882
+ const stream = opened.handle.createReadStream();
883
+ return maxBytes === undefined ? stream : stream.pipe(createMaxBytesTransform(maxBytes));
884
+ }
885
+
886
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/file-identity.js
887
+ function isZero(value) {
888
+ return value === 0 || value === 0n;
889
+ }
890
+ function file_identity_sameFileIdentity(left, right, platform = process.platform) {
891
+ if (left.ino !== right.ino) {
892
+ return false;
893
+ }
894
+ // On Windows, path-based stat calls can report dev=0 while fd-based stat
895
+ // reports a real volume serial; treat either-side dev=0 as "unknown device".
896
+ if (left.dev === right.dev) {
897
+ return true;
898
+ }
899
+ return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
900
+ }
901
+
902
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/string-coerce.js
903
+ function readStringValue(value) {
904
+ return typeof value === "string" ? value : undefined;
905
+ }
906
+ function normalizeNullableString(value) {
907
+ if (typeof value !== "string") {
908
+ return null;
909
+ }
910
+ const trimmed = value.trim();
911
+ return trimmed ? trimmed : null;
912
+ }
913
+ function normalizeOptionalString(value) {
914
+ return normalizeNullableString(value) ?? undefined;
915
+ }
916
+ function normalizeStringifiedOptionalString(value) {
917
+ if (typeof value === "string") {
918
+ return normalizeOptionalString(value);
919
+ }
920
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
921
+ return normalizeOptionalString(String(value));
922
+ }
923
+ return undefined;
924
+ }
925
+ function normalizeOptionalLowercaseString(value) {
926
+ return normalizeOptionalString(value)?.toLowerCase();
927
+ }
928
+ function normalizeLowercaseStringOrEmpty(value) {
929
+ return normalizeOptionalLowercaseString(value) ?? "";
930
+ }
931
+ function normalizeFastMode(raw) {
932
+ if (typeof raw === "boolean") {
933
+ return raw;
934
+ }
935
+ if (!raw) {
936
+ return undefined;
937
+ }
938
+ const key = normalizeLowercaseStringOrEmpty(raw);
939
+ if (["off", "false", "no", "0", "disable", "disabled", "normal"].includes(key)) {
940
+ return false;
941
+ }
942
+ if (["on", "true", "yes", "1", "enable", "enabled", "fast"].includes(key)) {
943
+ return true;
944
+ }
945
+ return undefined;
946
+ }
947
+ function lowercasePreservingWhitespace(value) {
948
+ return value.toLowerCase();
949
+ }
950
+ function localeLowercasePreservingWhitespace(value) {
951
+ return value.toLocaleLowerCase();
952
+ }
953
+ function resolvePrimaryStringValue(value) {
954
+ if (typeof value === "string") {
955
+ return normalizeOptionalString(value);
956
+ }
957
+ if (!value || typeof value !== "object") {
958
+ return undefined;
959
+ }
960
+ return normalizeOptionalString(value.primary);
961
+ }
962
+ function normalizeOptionalThreadValue(value) {
963
+ if (typeof value === "number") {
964
+ return Number.isFinite(value) ? Math.trunc(value) : undefined;
965
+ }
966
+ return normalizeOptionalString(value);
967
+ }
968
+ function normalizeOptionalStringifiedId(value) {
969
+ const normalized = normalizeOptionalThreadValue(value);
970
+ return normalized == null ? undefined : String(normalized);
971
+ }
972
+ function hasNonEmptyString(value) {
973
+ return normalizeOptionalString(value) !== undefined;
974
+ }
975
+
976
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path.js
977
+
978
+
979
+
980
+
981
+ const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
982
+ const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
983
+ const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
984
+ function normalizeWindowsPathForComparison(input) {
985
+ let normalized = external_node_path_.win32.normalize(input);
986
+ if (normalized.startsWith("\\\\?\\")) {
987
+ normalized = normalized.slice(4);
988
+ if (normalized.toUpperCase().startsWith("UNC\\")) {
989
+ normalized = `\\\\${normalized.slice(4)}`;
990
+ }
991
+ }
992
+ return normalizeLowercaseStringOrEmpty(normalized.replaceAll("/", "\\"));
993
+ }
994
+ function isNodeError(value) {
995
+ return Boolean(value && typeof value === "object" && "code" in value);
996
+ }
997
+ function hasNodeErrorCode(value, code) {
998
+ return isNodeError(value) && value.code === code;
999
+ }
1000
+ function path_assertNoNulPathInput(filePath, message = "path contains a NUL byte") {
1001
+ if (filePath.includes("\0")) {
1002
+ throw new errors_FsSafeError("invalid-path", message);
1003
+ }
1004
+ }
1005
+ function path_isNotFoundPathError(value) {
1006
+ return isNodeError(value) && typeof value.code === "string" && NOT_FOUND_CODES.has(value.code);
1007
+ }
1008
+ function isSymlinkOpenError(value) {
1009
+ return isNodeError(value) && typeof value.code === "string" && SYMLINK_OPEN_CODES.has(value.code);
1010
+ }
1011
+ function path_isPathInside(root, target) {
1012
+ if (process.platform === "win32") {
1013
+ const rootForCompare = normalizeWindowsPathForComparison(external_node_path_.win32.resolve(root));
1014
+ const targetForCompare = normalizeWindowsPathForComparison(external_node_path_.win32.resolve(target));
1015
+ const relative = external_node_path_.win32.relative(rootForCompare, targetForCompare);
1016
+ const firstSegment = relative.split(external_node_path_.win32.sep)[0];
1017
+ return (relative === "" || (firstSegment !== ".." && !external_node_path_.win32.isAbsolute(relative)));
1018
+ }
1019
+ if (root.length > 0 &&
1020
+ root.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
1021
+ target.length >= root.length &&
1022
+ target.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
1023
+ !target.includes("/..") &&
1024
+ (target === root ||
1025
+ (target.startsWith(root) && target.charCodeAt(root.length) === POSIX_SEPARATOR_CHAR_CODE))) {
1026
+ return true;
1027
+ }
1028
+ const resolvedRoot = external_node_path_.resolve(root);
1029
+ const resolvedTarget = external_node_path_.resolve(target);
1030
+ const relative = external_node_path_.relative(resolvedRoot, resolvedTarget);
1031
+ const firstSegment = relative.split(external_node_path_.posix.sep)[0];
1032
+ return relative === "" || (firstSegment !== ".." && !external_node_path_.isAbsolute(relative));
1033
+ }
1034
+ function resolveSafeBaseDir(rootDir) {
1035
+ const resolved = path.resolve(rootDir);
1036
+ return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
1037
+ }
1038
+ function isWithinDir(rootDir, targetPath) {
1039
+ return path_isPathInside(rootDir, targetPath);
1040
+ }
1041
+ function safeRealpathSync(targetPath, cache) {
1042
+ const cached = cache?.get(targetPath);
1043
+ if (cached) {
1044
+ return cached;
1045
+ }
1046
+ try {
1047
+ const resolved = fs.realpathSync(targetPath);
1048
+ cache?.set(targetPath, resolved);
1049
+ cache?.set(resolved, resolved);
1050
+ return resolved;
1051
+ }
1052
+ catch {
1053
+ return null;
1054
+ }
1055
+ }
1056
+ function isPathInsideWithRealpath(basePath, candidatePath, opts) {
1057
+ if (!path_isPathInside(basePath, candidatePath)) {
1058
+ return false;
1059
+ }
1060
+ const baseReal = safeRealpathSync(basePath, opts?.cache);
1061
+ const candidateReal = safeRealpathSync(candidatePath, opts?.cache);
1062
+ if (!baseReal || !candidateReal) {
1063
+ return opts?.requireRealpath === false;
1064
+ }
1065
+ return path_isPathInside(baseReal, candidateReal);
1066
+ }
1067
+ function safeStatSync(targetPath) {
1068
+ try {
1069
+ return fs.statSync(targetPath);
1070
+ }
1071
+ catch {
1072
+ return null;
1073
+ }
1074
+ }
1075
+ function splitSafeRelativePath(relativePath) {
1076
+ if (relativePath.length === 0 || relativePath === ".") {
1077
+ return [];
1078
+ }
1079
+ path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
1080
+ if (relativePath.includes("\\")) {
1081
+ throw new FsSafeError("invalid-path", "relative path must use forward slashes");
1082
+ }
1083
+ if (path.posix.isAbsolute(relativePath) ||
1084
+ path.win32.isAbsolute(relativePath) ||
1085
+ relativePath.startsWith("//")) {
1086
+ throw new FsSafeError("invalid-path", "relative path must not be absolute");
1087
+ }
1088
+ const segments = relativePath.split("/").filter((segment) => segment.length > 0 && segment !== ".");
1089
+ for (const segment of segments) {
1090
+ if (segment === "..") {
1091
+ throw new FsSafeError("invalid-path", "relative path must not contain '..'");
1092
+ }
1093
+ }
1094
+ return segments;
1095
+ }
1096
+ function resolveSafeRelativePath(rootDir, relativePath) {
1097
+ const root = path.resolve(rootDir);
1098
+ const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
1099
+ if (target !== root && !target.startsWith(root + path.sep)) {
1100
+ throw new FsSafeError("outside-workspace", "relative path escapes root");
1101
+ }
1102
+ return target;
1103
+ }
1104
+
1105
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/directory-guard.js
1106
+
1107
+
1108
+
1109
+
1110
+
1111
+
1112
+ async function directory_guard_createAsyncDirectoryGuard(dir) {
1113
+ const stat = await promises_.lstat(dir);
1114
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
1115
+ throw new errors_FsSafeError("not-file", "directory component must be a directory");
1116
+ }
1117
+ return { dir, realPath: await promises_.realpath(dir), stat };
1118
+ }
1119
+ async function assertAsyncDirectoryGuard(guard) {
1120
+ const stat = await promises_.lstat(guard.dir);
1121
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
1122
+ throw new errors_FsSafeError("not-file", "directory component must be a directory");
1123
+ }
1124
+ if (!file_identity_sameFileIdentity(stat, guard.stat) || (await promises_.realpath(guard.dir)) !== guard.realPath) {
1125
+ throw new errors_FsSafeError("path-mismatch", "directory changed during operation");
1126
+ }
1127
+ }
1128
+ function directory_guard_createSyncDirectoryGuard(dir) {
1129
+ const stat = fsSync.lstatSync(dir);
1130
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
1131
+ throw new FsSafeError("not-file", "directory component must be a directory");
1132
+ }
1133
+ return { dir, realPath: fsSync.realpathSync(dir), stat };
1134
+ }
1135
+ function directory_guard_assertSyncDirectoryGuard(guard) {
1136
+ const stat = fsSync.lstatSync(guard.dir);
1137
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
1138
+ throw new FsSafeError("not-file", "directory component must be a directory");
1139
+ }
1140
+ if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync(guard.dir) !== guard.realPath) {
1141
+ throw new FsSafeError("path-mismatch", "directory changed during operation");
1142
+ }
1143
+ }
1144
+ async function directory_guard_createNearestExistingDirectoryGuard(rootReal, targetPath) {
1145
+ let current = external_node_path_.resolve(targetPath);
1146
+ const root = external_node_path_.resolve(rootReal);
1147
+ while (current !== root) {
1148
+ try {
1149
+ return await directory_guard_createAsyncDirectoryGuard(current);
1150
+ }
1151
+ catch (error) {
1152
+ if (!path_isNotFoundPathError(error)) {
1153
+ throw error;
1154
+ }
1155
+ current = external_node_path_.dirname(current);
1156
+ }
1157
+ }
1158
+ return await directory_guard_createAsyncDirectoryGuard(root);
1159
+ }
1160
+ function directory_guard_createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
1161
+ let current = path.resolve(targetPath);
1162
+ const root = path.resolve(rootReal);
1163
+ while (current !== root) {
1164
+ try {
1165
+ return directory_guard_createSyncDirectoryGuard(current);
1166
+ }
1167
+ catch (error) {
1168
+ if (!isNotFoundPathError(error)) {
1169
+ throw error;
1170
+ }
1171
+ current = path.dirname(current);
1172
+ }
1173
+ }
1174
+ return directory_guard_createSyncDirectoryGuard(root);
1175
+ }
1176
+
1177
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js
1178
+
1179
+
1180
+
1181
+
1182
+ function isSameOrChildPath(candidate, parent) {
1183
+ return candidate === parent || candidate.startsWith(`${parent}${external_node_path_.sep}`);
1184
+ }
1185
+ function isPathEscape(relativePath) {
1186
+ return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
1187
+ }
1188
+ async function mkdirPathComponentsWithGuards(params) {
1189
+ const root = external_node_path_.resolve(params.rootReal);
1190
+ const target = external_node_path_.resolve(params.targetPath);
1191
+ const relative = external_node_path_.relative(root, target);
1192
+ if (isPathEscape(relative)) {
1193
+ throw new errors_FsSafeError("outside-workspace", "directory is outside workspace root");
1194
+ }
1195
+ let current = root;
1196
+ for (const part of relative.split(external_node_path_.sep).filter(Boolean)) {
1197
+ const next = external_node_path_.join(current, part);
1198
+ const parentGuard = await directory_guard_createAsyncDirectoryGuard(current);
1199
+ await params.beforeComponent?.(next);
1200
+ await assertAsyncDirectoryGuard(parentGuard);
1201
+ try {
1202
+ await promises_.mkdir(next);
1203
+ }
1204
+ catch (error) {
1205
+ if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
1206
+ throw error;
1207
+ }
1208
+ }
1209
+ const stat = await promises_.lstat(next);
1210
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
1211
+ throw new errors_FsSafeError("not-file", "directory component must be a directory");
1212
+ }
1213
+ // Node's recursive mkdir follows symlinks in missing components. Build one
1214
+ // segment at a time and realpath-check each segment before descending.
1215
+ if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)), root)) {
1216
+ throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
1217
+ }
1218
+ await directory_guard_createAsyncDirectoryGuard(next);
1219
+ await assertAsyncDirectoryGuard(parentGuard);
1220
+ current = next;
1221
+ }
1222
+ }
1223
+
1224
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/guarded-mutation.js
1225
+
1226
+
1227
+
1228
+
1229
+ async function withAsyncDirectoryGuards(guards, mutate, options = {}) {
1230
+ for (const guard of guards) {
1231
+ await assertAsyncDirectoryGuard(guard);
1232
+ }
1233
+ const result = await mutate();
1234
+ if (options.verifyAfter !== false) {
1235
+ try {
1236
+ for (const guard of guards) {
1237
+ await assertAsyncDirectoryGuard(guard);
1238
+ }
1239
+ }
1240
+ catch (error) {
1241
+ if (options.onPostGuardFailure) {
1242
+ try {
1243
+ // The mutation may have returned an owned resource before the post-guard
1244
+ // check detected a swapped directory. Give callers one chance to close
1245
+ // handles without letting cleanup hide the boundary failure.
1246
+ await options.onPostGuardFailure(result, error);
1247
+ }
1248
+ catch {
1249
+ // Preserve the boundary failure. Cleanup is best-effort.
1250
+ }
1251
+ }
1252
+ throw error;
1253
+ }
1254
+ }
1255
+ return result;
1256
+ }
1257
+ function withSyncDirectoryGuards(guards, mutate, options = {}) {
1258
+ for (const guard of guards) {
1259
+ assertSyncDirectoryGuard(guard);
1260
+ }
1261
+ const result = mutate();
1262
+ if (options.verifyAfter !== false) {
1263
+ for (const guard of guards) {
1264
+ assertSyncDirectoryGuard(guard);
1265
+ }
1266
+ }
1267
+ return result;
1268
+ }
1269
+ async function guardedRename(params) {
1270
+ const sourceGuard = await createAsyncDirectoryGuard(path.dirname(params.from));
1271
+ const targetGuard = params.targetRoot
1272
+ ? await createNearestExistingDirectoryGuard(params.targetRoot, path.dirname(params.to))
1273
+ : await createAsyncDirectoryGuard(path.dirname(params.to));
1274
+ await withAsyncDirectoryGuards([sourceGuard, targetGuard], async () => {
1275
+ await fs.rename(params.from, params.to);
1276
+ }, { verifyAfter: params.verifyAfter });
1277
+ }
1278
+ function guardedRenameSync(params) {
1279
+ const sourceGuard = createSyncDirectoryGuard(path.dirname(params.from));
1280
+ const targetGuard = params.targetRoot
1281
+ ? createNearestExistingSyncDirectoryGuard(params.targetRoot, path.dirname(params.to))
1282
+ : createSyncDirectoryGuard(path.dirname(params.to));
1283
+ withSyncDirectoryGuards([sourceGuard, targetGuard], () => fsSync.renameSync(params.from, params.to), { verifyAfter: params.verifyAfter });
1284
+ }
1285
+ async function guardedRm(params) {
1286
+ const guard = await createAsyncDirectoryGuard(path.dirname(params.target));
1287
+ await withAsyncDirectoryGuards([guard], async () => {
1288
+ await fs.rm(params.target, {
1289
+ ...(params.recursive !== undefined ? { recursive: params.recursive } : {}),
1290
+ ...(params.force !== undefined ? { force: params.force } : {}),
1291
+ });
1292
+ }, { verifyAfter: params.verifyAfter });
1293
+ }
1294
+ function guardedRmSync(params) {
1295
+ const guard = createSyncDirectoryGuard(path.dirname(params.target));
1296
+ withSyncDirectoryGuards([guard], () => fsSync.rmSync(params.target, {
1297
+ ...(params.recursive !== undefined ? { recursive: params.recursive } : {}),
1298
+ ...(params.force !== undefined ? { force: params.force } : {}),
1299
+ }), { verifyAfter: params.verifyAfter });
1300
+ }
1301
+
1302
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-python-config.js
1303
+ let overrideConfig = {};
1304
+ function parseMode(value) {
1305
+ if (!value) {
1306
+ return undefined;
1307
+ }
1308
+ const normalized = value.trim().toLowerCase();
1309
+ if (normalized === "0" || normalized === "false" || normalized === "off" || normalized === "never") {
1310
+ return "off";
1311
+ }
1312
+ if (normalized === "1" || normalized === "true" || normalized === "on" || normalized === "auto") {
1313
+ return "auto";
1314
+ }
1315
+ if (normalized === "required" || normalized === "require") {
1316
+ return "require";
1317
+ }
1318
+ return undefined;
1319
+ }
1320
+ function configureFsSafePython(config) {
1321
+ overrideConfig = { ...overrideConfig, ...config };
1322
+ }
1323
+ function getFsSafePythonConfig() {
1324
+ return {
1325
+ mode: overrideConfig.mode ??
1326
+ parseMode(process.env.FS_SAFE_PYTHON_MODE) ??
1327
+ parseMode(process.env.OPENCLAW_FS_SAFE_PYTHON_MODE) ??
1328
+ "auto",
1329
+ pythonPath: overrideConfig.pythonPath ??
1330
+ process.env.FS_SAFE_PYTHON ??
1331
+ process.env.OPENCLAW_FS_SAFE_PYTHON ??
1332
+ process.env.OPENCLAW_PINNED_PYTHON ??
1333
+ process.env.OPENCLAW_PINNED_WRITE_PYTHON,
1334
+ };
1335
+ }
1336
+ function canFallbackFromPythonError(error) {
1337
+ return (getFsSafePythonConfig().mode !== "require" &&
1338
+ error instanceof Error &&
1339
+ "code" in error &&
1340
+ error.code === "helper-unavailable");
1341
+ }
1342
+
1343
+ // EXTERNAL MODULE: external "node:child_process"
1344
+ var external_node_child_process_ = __webpack_require__(1421);
1345
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-python.js
1346
+
1347
+
1348
+
1349
+
1350
+ const PINNED_PYTHON_WORKER_SOURCE = String.raw `
1351
+ import base64
1352
+ import errno
1353
+ import json
1354
+ import os
1355
+ import secrets
1356
+ import stat
1357
+ import sys
1358
+
1359
+ DIR_FLAGS = os.O_RDONLY
1360
+ if hasattr(os, "O_DIRECTORY"):
1361
+ DIR_FLAGS |= os.O_DIRECTORY
1362
+ if hasattr(os, "O_NOFOLLOW"):
1363
+ DIR_FLAGS |= os.O_NOFOLLOW
1364
+ READ_FLAGS = os.O_RDONLY
1365
+ if hasattr(os, "O_NOFOLLOW"):
1366
+ READ_FLAGS |= os.O_NOFOLLOW
1367
+ WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
1368
+ if hasattr(os, "O_NOFOLLOW"):
1369
+ WRITE_FLAGS |= os.O_NOFOLLOW
1370
+
1371
+ def split_relative(value):
1372
+ if value in ("", "."):
1373
+ return []
1374
+ if "\x00" in value or value.startswith("/") or value.startswith("//"):
1375
+ raise OSError(errno.EPERM, "invalid relative path")
1376
+ if value.startswith("..\\"):
1377
+ raise OSError(errno.EPERM, "path traversal is not allowed")
1378
+ parts = [part for part in value.split("/") if part and part != "."]
1379
+ for part in parts:
1380
+ if part == "..":
1381
+ raise OSError(errno.EPERM, "path traversal is not allowed")
1382
+ return parts
1383
+
1384
+ def open_dir(path_value, dir_fd=None):
1385
+ return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
1386
+
1387
+ def walk_dir(root_fd, segments, mkdir_enabled=False):
1388
+ current_fd = os.dup(root_fd)
1389
+ try:
1390
+ for segment in segments:
1391
+ try:
1392
+ next_fd = open_dir(segment, dir_fd=current_fd)
1393
+ except FileNotFoundError:
1394
+ if not mkdir_enabled:
1395
+ raise
1396
+ os.mkdir(segment, 0o777, dir_fd=current_fd)
1397
+ next_fd = open_dir(segment, dir_fd=current_fd)
1398
+ os.close(current_fd)
1399
+ current_fd = next_fd
1400
+ return current_fd
1401
+ except Exception:
1402
+ os.close(current_fd)
1403
+ raise
1404
+
1405
+ def parent_and_basename(root_fd, relative):
1406
+ segments = split_relative(relative)
1407
+ if not segments:
1408
+ raise OSError(errno.EPERM, "operation requires a non-root path")
1409
+ parent_fd = walk_dir(root_fd, segments[:-1])
1410
+ return parent_fd, segments[-1]
1411
+
1412
+ def encode_stat(st):
1413
+ mode = st.st_mode
1414
+ return {
1415
+ "dev": st.st_dev,
1416
+ "gid": st.st_gid,
1417
+ "ino": st.st_ino,
1418
+ "isDirectory": stat.S_ISDIR(mode),
1419
+ "isFile": stat.S_ISREG(mode),
1420
+ "isSymbolicLink": stat.S_ISLNK(mode),
1421
+ "mode": mode,
1422
+ "mtimeMs": st.st_mtime * 1000,
1423
+ "nlink": st.st_nlink,
1424
+ "size": st.st_size,
1425
+ "uid": st.st_uid,
1426
+ }
1427
+
1428
+ def reject_unsafe_endpoint(st):
1429
+ mode = st.st_mode
1430
+ if stat.S_ISLNK(mode):
1431
+ raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
1432
+ if stat.S_ISREG(mode) and st.st_nlink > 1:
1433
+ raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
1434
+
1435
+ def stat_path(root_fd, payload):
1436
+ relative = payload.get("relativePath", "")
1437
+ segments = split_relative(relative)
1438
+ if not segments:
1439
+ return encode_stat(os.fstat(root_fd))
1440
+ parent_fd, basename = parent_and_basename(root_fd, relative)
1441
+ try:
1442
+ st = os.lstat(basename, dir_fd=parent_fd)
1443
+ if payload.get("rejectSymlink", True) and stat.S_ISLNK(st.st_mode):
1444
+ raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
1445
+ return encode_stat(st)
1446
+ finally:
1447
+ os.close(parent_fd)
1448
+
1449
+ def readdir_path(root_fd, payload):
1450
+ dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
1451
+ try:
1452
+ names = sorted(os.listdir(dir_fd))
1453
+ if not payload.get("withFileTypes", False):
1454
+ return names
1455
+ entries = []
1456
+ for name in names:
1457
+ st = os.lstat(name, dir_fd=dir_fd)
1458
+ entry = encode_stat(st)
1459
+ entry["name"] = name
1460
+ entries.append(entry)
1461
+ return entries
1462
+ finally:
1463
+ os.close(dir_fd)
1464
+
1465
+ def mkdirp_path(root_fd, payload):
1466
+ dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
1467
+ os.close(dir_fd)
1468
+ return None
1469
+
1470
+ def remove_tree(parent_fd, basename):
1471
+ st = os.lstat(basename, dir_fd=parent_fd)
1472
+ if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
1473
+ dir_fd = open_dir(basename, dir_fd=parent_fd)
1474
+ try:
1475
+ for child in os.listdir(dir_fd):
1476
+ remove_tree(dir_fd, child)
1477
+ finally:
1478
+ os.close(dir_fd)
1479
+ os.rmdir(basename, dir_fd=parent_fd)
1480
+ else:
1481
+ os.unlink(basename, dir_fd=parent_fd)
1482
+
1483
+ def remove_path(root_fd, payload):
1484
+ parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
1485
+ try:
1486
+ try:
1487
+ st = os.lstat(basename, dir_fd=parent_fd)
1488
+ except FileNotFoundError:
1489
+ if payload.get("force", True):
1490
+ return None
1491
+ raise
1492
+ if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
1493
+ if payload.get("recursive", False):
1494
+ remove_tree(parent_fd, basename)
1495
+ else:
1496
+ os.rmdir(basename, dir_fd=parent_fd)
1497
+ else:
1498
+ os.unlink(basename, dir_fd=parent_fd)
1499
+ return None
1500
+ finally:
1501
+ os.close(parent_fd)
1502
+
1503
+ def rename_path(root_fd, payload):
1504
+ from_parent_fd, from_base = parent_and_basename(root_fd, payload["from"])
1505
+ to_parent_fd, to_base = parent_and_basename(root_fd, payload["to"])
1506
+ try:
1507
+ from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
1508
+ reject_unsafe_endpoint(from_stat)
1509
+ if not payload.get("overwrite", True):
1510
+ try:
1511
+ os.lstat(to_base, dir_fd=to_parent_fd)
1512
+ raise FileExistsError(errno.EEXIST, "destination exists", to_base)
1513
+ except FileNotFoundError:
1514
+ pass
1515
+ os.rename(from_base, to_base, src_dir_fd=from_parent_fd, dst_dir_fd=to_parent_fd)
1516
+ os.fsync(from_parent_fd)
1517
+ if from_parent_fd != to_parent_fd:
1518
+ os.fsync(to_parent_fd)
1519
+ return None
1520
+ finally:
1521
+ os.close(from_parent_fd)
1522
+ os.close(to_parent_fd)
1523
+
1524
+ def create_temp_file(parent_fd, basename, mode):
1525
+ prefix = "." + basename + "."
1526
+ for _ in range(128):
1527
+ candidate = prefix + secrets.token_hex(6) + ".tmp"
1528
+ try:
1529
+ fd = os.open(candidate, WRITE_FLAGS, mode, dir_fd=parent_fd)
1530
+ return candidate, fd
1531
+ except FileExistsError:
1532
+ continue
1533
+ raise RuntimeError("failed to allocate pinned temp file")
1534
+
1535
+ def write_path(root_fd, payload):
1536
+ parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
1537
+ temp_fd = None
1538
+ temp_name = None
1539
+ basename = payload["basename"]
1540
+ mode = int(payload.get("mode", 0o600))
1541
+ overwrite = bool(payload.get("overwrite", True))
1542
+ max_bytes = int(payload.get("maxBytes", -1))
1543
+ data = base64.b64decode(payload.get("base64", ""))
1544
+ try:
1545
+ if max_bytes >= 0 and len(data) > max_bytes:
1546
+ raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, len(data)))
1547
+ if not overwrite:
1548
+ try:
1549
+ os.lstat(basename, dir_fd=parent_fd)
1550
+ raise FileExistsError(errno.EEXIST, "destination exists", basename)
1551
+ except FileNotFoundError:
1552
+ pass
1553
+ temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
1554
+ view = memoryview(data)
1555
+ while view:
1556
+ written = os.write(temp_fd, view)
1557
+ if written <= 0:
1558
+ raise OSError(errno.EIO, "short write")
1559
+ view = view[written:]
1560
+ os.fsync(temp_fd)
1561
+ os.close(temp_fd)
1562
+ temp_fd = None
1563
+ os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
1564
+ temp_name = None
1565
+ os.fsync(parent_fd)
1566
+ result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
1567
+ return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
1568
+ finally:
1569
+ if temp_fd is not None:
1570
+ os.close(temp_fd)
1571
+ if temp_name is not None:
1572
+ try:
1573
+ os.unlink(temp_name, dir_fd=parent_fd)
1574
+ except FileNotFoundError:
1575
+ pass
1576
+ os.close(parent_fd)
1577
+
1578
+ def copy_path(root_fd, payload):
1579
+ source_fd = os.open(payload["sourcePath"], READ_FLAGS)
1580
+ parent_fd = None
1581
+ temp_fd = None
1582
+ temp_name = None
1583
+ try:
1584
+ source_stat = os.fstat(source_fd)
1585
+ if not stat.S_ISREG(source_stat.st_mode):
1586
+ raise RuntimeError("fs-safe-not-file")
1587
+ if source_stat.st_dev != int(payload["sourceDev"]) or source_stat.st_ino != int(payload["sourceIno"]):
1588
+ raise RuntimeError("fs-safe-source-mismatch")
1589
+ basename = payload["basename"]
1590
+ mode = int(payload.get("mode", 0o600))
1591
+ overwrite = bool(payload.get("overwrite", True))
1592
+ max_bytes = int(payload.get("maxBytes", -1))
1593
+ if max_bytes >= 0 and source_stat.st_size > max_bytes:
1594
+ raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
1595
+ parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
1596
+ if not overwrite:
1597
+ try:
1598
+ os.lstat(basename, dir_fd=parent_fd)
1599
+ raise FileExistsError(errno.EEXIST, "destination exists", basename)
1600
+ except FileNotFoundError:
1601
+ pass
1602
+ temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
1603
+ written_bytes = 0
1604
+ while True:
1605
+ chunk = os.read(source_fd, 65536)
1606
+ if not chunk:
1607
+ break
1608
+ written_bytes += len(chunk)
1609
+ if max_bytes >= 0 and written_bytes > max_bytes:
1610
+ raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, written_bytes))
1611
+ view = memoryview(chunk)
1612
+ while view:
1613
+ written = os.write(temp_fd, view)
1614
+ if written <= 0:
1615
+ raise OSError(errno.EIO, "short write")
1616
+ view = view[written:]
1617
+ os.fsync(temp_fd)
1618
+ os.close(temp_fd)
1619
+ temp_fd = None
1620
+ os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
1621
+ temp_name = None
1622
+ os.fsync(parent_fd)
1623
+ result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
1624
+ return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
1625
+ finally:
1626
+ os.close(source_fd)
1627
+ if temp_fd is not None:
1628
+ os.close(temp_fd)
1629
+ if temp_name is not None and parent_fd is not None:
1630
+ try:
1631
+ os.unlink(temp_name, dir_fd=parent_fd)
1632
+ except FileNotFoundError:
1633
+ pass
1634
+ if parent_fd is not None:
1635
+ os.close(parent_fd)
1636
+
1637
+ def run_operation(operation, root_path, payload):
1638
+ root_fd = open_dir(root_path)
1639
+ try:
1640
+ if operation == "stat":
1641
+ return stat_path(root_fd, payload)
1642
+ if operation == "readdir":
1643
+ return readdir_path(root_fd, payload)
1644
+ if operation == "mkdirp":
1645
+ return mkdirp_path(root_fd, payload)
1646
+ if operation == "remove":
1647
+ return remove_path(root_fd, payload)
1648
+ if operation == "rename":
1649
+ return rename_path(root_fd, payload)
1650
+ if operation == "write":
1651
+ return write_path(root_fd, payload)
1652
+ if operation == "copy":
1653
+ return copy_path(root_fd, payload)
1654
+ raise RuntimeError("unknown operation: " + operation)
1655
+ finally:
1656
+ os.close(root_fd)
1657
+
1658
+ for line in sys.stdin:
1659
+ try:
1660
+ request = json.loads(line)
1661
+ result = run_operation(request["operation"], request["rootPath"], request.get("payload") or {})
1662
+ response = {"id": request["id"], "ok": True, "result": result}
1663
+ except Exception as exc:
1664
+ response = {
1665
+ "id": request.get("id") if isinstance(locals().get("request"), dict) else None,
1666
+ "ok": False,
1667
+ "code": exc.__class__.__name__,
1668
+ "errno": getattr(exc, "errno", None),
1669
+ "message": str(exc),
1670
+ }
1671
+ print(json.dumps(response, separators=(",", ":")), flush=True)
1672
+ `;
1673
+ let nextRequestId = 1;
1674
+ let worker = null;
1675
+ function __resetPinnedPythonWorkerForTest() {
1676
+ const currentWorker = worker;
1677
+ worker = null;
1678
+ if (!currentWorker) {
1679
+ return;
1680
+ }
1681
+ currentWorker.pending.clear();
1682
+ currentWorker.child.kill("SIGTERM");
1683
+ }
1684
+ const PYTHON_CANDIDATE_DEFAULTS = [
1685
+ "/usr/bin/python3",
1686
+ "/opt/homebrew/bin/python3",
1687
+ "/usr/local/bin/python3",
1688
+ ];
1689
+ function canExecute(binPath) {
1690
+ try {
1691
+ external_node_fs_.accessSync(binPath, external_node_fs_.constants.X_OK);
1692
+ return true;
1693
+ }
1694
+ catch {
1695
+ return false;
1696
+ }
1697
+ }
1698
+ function resolvePython() {
1699
+ const configured = getFsSafePythonConfig().pythonPath;
1700
+ if (configured) {
1701
+ return configured;
1702
+ }
1703
+ for (const candidate of PYTHON_CANDIDATE_DEFAULTS) {
1704
+ if (canExecute(candidate)) {
1705
+ return candidate;
1706
+ }
1707
+ }
1708
+ return "python3";
1709
+ }
1710
+ function assertPinnedHelperSupported() {
1711
+ if (process.platform === "win32") {
1712
+ throw new errors_FsSafeError("unsupported-platform", "fd-relative pinned filesystem operations are not available on Windows");
1713
+ }
1714
+ if (getFsSafePythonConfig().mode === "off") {
1715
+ throw new errors_FsSafeError("helper-unavailable", "Python helper is disabled");
1716
+ }
1717
+ }
1718
+ function isSpawnUnavailable(error) {
1719
+ if (!(error instanceof Error)) {
1720
+ return false;
1721
+ }
1722
+ const maybeErrno = error;
1723
+ return (typeof maybeErrno.syscall === "string" &&
1724
+ maybeErrno.syscall.startsWith("spawn") &&
1725
+ ["EACCES", "ENOENT", "ENOEXEC"].includes(maybeErrno.code ?? ""));
1726
+ }
1727
+ function mapWorkerError(response) {
1728
+ const code = typeof response.code === "string" ? response.code : "";
1729
+ const errno = typeof response.errno === "number" ? response.errno : undefined;
1730
+ const message = typeof response.message === "string" && response.message
1731
+ ? response.message
1732
+ : "pinned helper failed";
1733
+ const tooLarge = message.match(/fs-safe-too-large:(\d+):(\d+)/);
1734
+ if (tooLarge) {
1735
+ const [, limit, got] = tooLarge;
1736
+ return new errors_FsSafeError("too-large", `file exceeds limit of ${limit} bytes (got at least ${got})`);
1737
+ }
1738
+ if (message.includes("fs-safe-not-file")) {
1739
+ return new errors_FsSafeError("not-file", "not a file");
1740
+ }
1741
+ if (message.includes("fs-safe-source-mismatch")) {
1742
+ return new errors_FsSafeError("path-mismatch", "source path changed during copy");
1743
+ }
1744
+ if (code === "FileNotFoundError" || errno === 2) {
1745
+ return new errors_FsSafeError("not-found", "file not found");
1746
+ }
1747
+ if (code === "FileExistsError" || errno === 17) {
1748
+ return new errors_FsSafeError("already-exists", message);
1749
+ }
1750
+ if (errno === 39) {
1751
+ return new errors_FsSafeError("not-empty", "directory is not empty");
1752
+ }
1753
+ if (errno === 1 || errno === 13 || errno === 21) {
1754
+ return new errors_FsSafeError("not-removable", "path is not removable under root");
1755
+ }
1756
+ if (code === "NotADirectoryError" || code === "OSError" || errno === 20 || errno === 40) {
1757
+ return new errors_FsSafeError("path-alias", message);
1758
+ }
1759
+ return new errors_FsSafeError("helper-failed", message);
1760
+ }
1761
+ function rejectPending(error) {
1762
+ if (!worker) {
1763
+ return;
1764
+ }
1765
+ setWorkerRef(worker, false);
1766
+ for (const pending of worker.pending.values()) {
1767
+ pending.reject(error);
1768
+ }
1769
+ worker.pending.clear();
1770
+ worker = null;
1771
+ }
1772
+ function handleWorkerLine(line) {
1773
+ if (!worker || !line.trim()) {
1774
+ return;
1775
+ }
1776
+ let decoded;
1777
+ try {
1778
+ decoded = JSON.parse(line);
1779
+ }
1780
+ catch {
1781
+ rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
1782
+ return;
1783
+ }
1784
+ if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
1785
+ rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
1786
+ return;
1787
+ }
1788
+ const response = decoded;
1789
+ const id = typeof response.id === "number" ? response.id : undefined;
1790
+ if (id === undefined) {
1791
+ return;
1792
+ }
1793
+ const pending = worker.pending.get(id);
1794
+ if (!pending) {
1795
+ return;
1796
+ }
1797
+ worker.pending.delete(id);
1798
+ if (worker.pending.size === 0) {
1799
+ setWorkerRef(worker, false);
1800
+ }
1801
+ if (response.ok === true) {
1802
+ pending.resolve(response.result);
1803
+ return;
1804
+ }
1805
+ pending.reject(mapWorkerError(decoded));
1806
+ }
1807
+ function getWorker() {
1808
+ assertPinnedHelperSupported();
1809
+ if (worker) {
1810
+ return worker;
1811
+ }
1812
+ const child = (0,external_node_child_process_.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
1813
+ stdio: ["pipe", "pipe", "pipe"],
1814
+ });
1815
+ worker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
1816
+ child.stdout.setEncoding("utf8");
1817
+ child.stderr.setEncoding("utf8");
1818
+ child.stdout.on("data", (chunk) => {
1819
+ const current = worker;
1820
+ if (!current) {
1821
+ return;
1822
+ }
1823
+ current.stdoutBuffer += chunk;
1824
+ for (;;) {
1825
+ const newline = current.stdoutBuffer.indexOf("\n");
1826
+ if (newline < 0) {
1827
+ break;
1828
+ }
1829
+ const line = current.stdoutBuffer.slice(0, newline);
1830
+ current.stdoutBuffer = current.stdoutBuffer.slice(newline + 1);
1831
+ handleWorkerLine(line);
1832
+ }
1833
+ });
1834
+ child.stderr.on("data", (chunk) => {
1835
+ if (worker) {
1836
+ worker.stderr = `${worker.stderr}${chunk}`.slice(-4096);
1837
+ }
1838
+ });
1839
+ child.once("error", (error) => {
1840
+ const mapped = isSpawnUnavailable(error)
1841
+ ? new errors_FsSafeError("helper-unavailable", "Python helper is unavailable", { cause: error })
1842
+ : error instanceof Error
1843
+ ? error
1844
+ : new Error(String(error));
1845
+ rejectPending(mapped);
1846
+ });
1847
+ child.once("close", (code, signal) => {
1848
+ const stderr = worker?.stderr.trim();
1849
+ rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
1850
+ });
1851
+ process.once("exit", () => {
1852
+ child.kill("SIGTERM");
1853
+ });
1854
+ setWorkerRef(worker, false);
1855
+ return worker;
1856
+ }
1857
+ function setRefable(value, ref) {
1858
+ if (!value) {
1859
+ return;
1860
+ }
1861
+ const method = ref ? "ref" : "unref";
1862
+ const refable = value;
1863
+ refable[method]?.();
1864
+ }
1865
+ function setWorkerRef(currentWorker, ref) {
1866
+ setRefable(currentWorker.child, ref);
1867
+ setRefable(currentWorker.child.stdin, ref);
1868
+ setRefable(currentWorker.child.stdout, ref);
1869
+ setRefable(currentWorker.child.stderr, ref);
1870
+ }
1871
+ async function runPinnedPythonOperation(params) {
1872
+ const requestId = nextRequestId++;
1873
+ const currentWorker = getWorker();
1874
+ if (typeof currentWorker.child.stdin?.write !== "function") {
1875
+ throw new errors_FsSafeError("helper-unavailable", "Python helper stdin is unavailable");
1876
+ }
1877
+ setWorkerRef(currentWorker, true);
1878
+ return await new Promise((resolve, reject) => {
1879
+ currentWorker.pending.set(requestId, {
1880
+ reject,
1881
+ resolve: (value) => resolve(value),
1882
+ });
1883
+ const request = JSON.stringify({
1884
+ id: requestId,
1885
+ operation: params.operation,
1886
+ rootPath: params.rootPath,
1887
+ payload: params.payload,
1888
+ });
1889
+ currentWorker.child.stdin.write(`${request}\n`, (error) => {
1890
+ if (error) {
1891
+ currentWorker.pending.delete(requestId);
1892
+ if (currentWorker.pending.size === 0) {
1893
+ setWorkerRef(currentWorker, false);
1894
+ }
1895
+ reject(error);
1896
+ }
1897
+ });
1898
+ });
1899
+ }
1900
+ function assertPinnedPythonOperationAvailable() {
1901
+ const currentWorker = getWorker();
1902
+ if (typeof currentWorker.child.stdin?.write !== "function") {
1903
+ throw new errors_FsSafeError("helper-unavailable", "Python helper stdin is unavailable");
1904
+ }
1905
+ }
1906
+ function validatePinnedOperationPayload(payload) {
1907
+ if (typeof payload.relativePath === "string") {
1908
+ validatePinnedRelativePath(payload.relativePath);
1909
+ }
1910
+ if (typeof payload.relativeParentPath === "string") {
1911
+ validatePinnedRelativePath(payload.relativeParentPath);
1912
+ }
1913
+ if (typeof payload.from === "string") {
1914
+ validatePinnedRelativePath(payload.from);
1915
+ }
1916
+ if (typeof payload.to === "string") {
1917
+ validatePinnedRelativePath(payload.to);
1918
+ }
1919
+ }
1920
+ function isPinnedHelperUnavailable(error) {
1921
+ return error instanceof Error &&
1922
+ "code" in error &&
1923
+ error.code === "helper-unavailable";
1924
+ }
1925
+ function validatePinnedRelativePath(relativePath) {
1926
+ if (relativePath.length === 0 || relativePath === ".") {
1927
+ return;
1928
+ }
1929
+ if (relativePath.includes("\0")) {
1930
+ throw new errors_FsSafeError("invalid-path", "relative path contains a NUL byte");
1931
+ }
1932
+ if (relativePath.startsWith("/") ||
1933
+ relativePath.startsWith("//") ||
1934
+ relativePath === ".." ||
1935
+ relativePath.startsWith("../") ||
1936
+ relativePath.startsWith("..\\")) {
1937
+ throw new errors_FsSafeError("invalid-path", "relative path must not escape root");
1938
+ }
1939
+ for (const segment of relativePath.split("/")) {
1940
+ if (segment === "..") {
1941
+ throw new errors_FsSafeError("invalid-path", "relative path must not contain '..'");
1942
+ }
1943
+ }
1944
+ }
1945
+
1946
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-helper.js
1947
+
1948
+
1949
+ async function runPinnedHelper(operation, rootDir, payload) {
1950
+ validatePinnedOperationPayload(payload);
1951
+ return await runPinnedPythonOperation({
1952
+ operation,
1953
+ rootPath: rootDir,
1954
+ payload,
1955
+ });
1956
+ }
1957
+ async function helperStat(rootDir, relativePath) {
1958
+ return await runPinnedHelper("stat", rootDir, { relativePath });
1959
+ }
1960
+ async function helperReaddir(rootDir, relativePath, withFileTypes) {
1961
+ return await runPinnedHelper("readdir", rootDir, {
1962
+ relativePath,
1963
+ withFileTypes,
1964
+ });
1965
+ }
1966
+
1967
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-path.js
1968
+
1969
+
1970
+
1971
+ function isPinnedPathHelperSpawnError(error) {
1972
+ return canFallbackFromPythonError(error);
1973
+ }
1974
+ async function runPinnedPathHelper(params) {
1975
+ try {
1976
+ await runPinnedHelper(params.operation, params.rootPath, {
1977
+ relativePath: params.relativePath,
1978
+ });
1979
+ }
1980
+ catch (error) {
1981
+ if (error instanceof errors_FsSafeError) {
1982
+ throw error;
1983
+ }
1984
+ throw new errors_FsSafeError("helper-failed", "pinned path helper failed", {
1985
+ cause: error instanceof Error ? error : undefined,
1986
+ });
1987
+ }
1988
+ }
1989
+
1990
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-write.js
1991
+
1992
+
1993
+
1994
+
1995
+
1996
+
1997
+
1998
+
1999
+
2000
+
2001
+
2002
+ function byteLength(input, encoding) {
2003
+ return typeof input === "string"
2004
+ ? Buffer.byteLength(input, encoding ?? "utf8")
2005
+ : input.byteLength;
2006
+ }
2007
+ function assertSafeBasename(basename) {
2008
+ if (!basename ||
2009
+ basename === "." ||
2010
+ basename === ".." ||
2011
+ basename.includes("/") ||
2012
+ basename.includes("\0")) {
2013
+ throw new errors_FsSafeError("invalid-path", "invalid target path");
2014
+ }
2015
+ }
2016
+ function assertWithinMaxBytes(bytes, maxBytes) {
2017
+ if (maxBytes !== undefined && bytes > maxBytes) {
2018
+ throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
2019
+ }
2020
+ }
2021
+ function pinned_write_createMaxBytesTransform(maxBytes) {
2022
+ if (maxBytes === undefined) {
2023
+ return undefined;
2024
+ }
2025
+ let bytes = 0;
2026
+ return new external_node_stream_.Transform({
2027
+ transform(chunk, _encoding, callback) {
2028
+ bytes += chunk.byteLength;
2029
+ if (bytes > maxBytes) {
2030
+ callback(new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
2031
+ return;
2032
+ }
2033
+ callback(null, chunk);
2034
+ },
2035
+ });
2036
+ }
2037
+ async function pipelineWithMaxBytes(stream, destination, maxBytes) {
2038
+ const limiter = pinned_write_createMaxBytesTransform(maxBytes);
2039
+ if (limiter) {
2040
+ await (0,external_node_stream_promises_.pipeline)(stream, limiter, destination);
2041
+ return;
2042
+ }
2043
+ await (0,external_node_stream_promises_.pipeline)(stream, destination);
2044
+ }
2045
+ async function inputToBase64(input, maxBytes) {
2046
+ if (input.kind === "buffer") {
2047
+ assertWithinMaxBytes(byteLength(input.data, input.encoding), maxBytes);
2048
+ return (typeof input.data === "string"
2049
+ ? Buffer.from(input.data, input.encoding ?? "utf8")
2050
+ : input.data).toString("base64");
2051
+ }
2052
+ const chunks = [];
2053
+ let bytes = 0;
2054
+ for await (const chunk of input.stream) {
2055
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2056
+ bytes += buffer.byteLength;
2057
+ assertWithinMaxBytes(bytes, maxBytes);
2058
+ chunks.push(buffer);
2059
+ }
2060
+ return Buffer.concat(chunks, bytes).toString("base64");
2061
+ }
2062
+ async function runPinnedWriteHelper(params) {
2063
+ assertSafeBasename(params.basename);
2064
+ validatePinnedOperationPayload({
2065
+ relativeParentPath: params.relativeParentPath,
2066
+ });
2067
+ if (getFsSafePythonConfig().mode === "off") {
2068
+ return await runPinnedWriteFallback(params);
2069
+ }
2070
+ if (params.input.kind === "stream") {
2071
+ try {
2072
+ assertPinnedPythonOperationAvailable();
2073
+ }
2074
+ catch (error) {
2075
+ if (canFallbackFromPythonError(error)) {
2076
+ return await runPinnedWriteFallback(params);
2077
+ }
2078
+ throw error;
2079
+ }
2080
+ }
2081
+ const payload = {
2082
+ base64: await inputToBase64(params.input, params.maxBytes),
2083
+ basename: params.basename,
2084
+ maxBytes: params.maxBytes ?? -1,
2085
+ mkdir: params.mkdir,
2086
+ mode: params.mode || 0o600,
2087
+ overwrite: params.overwrite !== false,
2088
+ relativeParentPath: params.relativeParentPath,
2089
+ };
2090
+ try {
2091
+ return await runPinnedPythonOperation({
2092
+ operation: "write",
2093
+ rootPath: params.rootPath,
2094
+ payload,
2095
+ });
2096
+ }
2097
+ catch (error) {
2098
+ if (canFallbackFromPythonError(error)) {
2099
+ return await runPinnedWriteFallback(params);
2100
+ }
2101
+ throw error;
2102
+ }
2103
+ }
2104
+ async function runPinnedCopyHelper(params) {
2105
+ assertSafeBasename(params.basename);
2106
+ validatePinnedOperationPayload({
2107
+ relativeParentPath: params.relativeParentPath,
2108
+ });
2109
+ return await runPinnedPythonOperation({
2110
+ operation: "copy",
2111
+ rootPath: params.rootPath,
2112
+ payload: {
2113
+ basename: params.basename,
2114
+ maxBytes: params.maxBytes ?? -1,
2115
+ mkdir: params.mkdir,
2116
+ mode: params.mode || 0o600,
2117
+ overwrite: params.overwrite !== false,
2118
+ relativeParentPath: params.relativeParentPath,
2119
+ sourceDev: params.sourceIdentity.dev,
2120
+ sourceIno: params.sourceIdentity.ino,
2121
+ sourcePath: params.sourcePath,
2122
+ },
2123
+ });
2124
+ }
2125
+ async function runPinnedWriteFallback(params) {
2126
+ const parentPath = params.relativeParentPath
2127
+ ? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
2128
+ : params.rootPath;
2129
+ const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
2130
+ if (params.mkdir) {
2131
+ await withAsyncDirectoryGuards([parentGuard], async () => {
2132
+ await promises_.mkdir(parentPath, { recursive: true });
2133
+ });
2134
+ }
2135
+ const targetPath = external_node_path_.join(parentPath, params.basename);
2136
+ if (params.overwrite === false) {
2137
+ 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), {
2138
+ onPostGuardFailure: async (openedHandle) => {
2139
+ // The parent failed verification, so targetPath may now resolve
2140
+ // somewhere else. Close the fd, but do not clean up by path.
2141
+ await openedHandle.close().catch(() => undefined);
2142
+ },
2143
+ });
2144
+ let created = true;
2145
+ try {
2146
+ if (params.input.kind === "buffer") {
2147
+ assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
2148
+ if (typeof params.input.data === "string") {
2149
+ await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
2150
+ }
2151
+ else {
2152
+ await handle.writeFile(params.input.data);
2153
+ }
2154
+ }
2155
+ else {
2156
+ await pipelineWithMaxBytes(params.input.stream, handle.createWriteStream(), params.maxBytes);
2157
+ }
2158
+ const stat = await handle.stat();
2159
+ created = false;
2160
+ return { dev: stat.dev, ino: stat.ino };
2161
+ }
2162
+ finally {
2163
+ await handle.close().catch(() => undefined);
2164
+ if (created) {
2165
+ await promises_.rm(targetPath, { force: true }).catch(() => undefined);
2166
+ }
2167
+ }
2168
+ }
2169
+ const tempPath = external_node_path_.join(parentPath, `.${params.basename}.${(0,external_node_crypto_.randomUUID)()}.fallback.tmp`);
2170
+ const tempFlags = external_node_fs_.constants.O_WRONLY |
2171
+ external_node_fs_.constants.O_CREAT |
2172
+ external_node_fs_.constants.O_EXCL |
2173
+ (process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants
2174
+ ? external_node_fs_.constants.O_NOFOLLOW
2175
+ : 0);
2176
+ let handle;
2177
+ let handleClosedByStream = false;
2178
+ try {
2179
+ handle = await promises_.open(tempPath, tempFlags, params.mode);
2180
+ if (params.input.kind === "buffer") {
2181
+ assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
2182
+ if (typeof params.input.data === "string") {
2183
+ await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
2184
+ }
2185
+ else {
2186
+ await handle.writeFile(params.input.data);
2187
+ }
2188
+ }
2189
+ else {
2190
+ const writable = handle.createWriteStream();
2191
+ writable.once("close", () => {
2192
+ handleClosedByStream = true;
2193
+ });
2194
+ await pipelineWithMaxBytes(params.input.stream, writable, params.maxBytes);
2195
+ }
2196
+ if (!handleClosedByStream) {
2197
+ await handle.close().catch(() => undefined);
2198
+ handle = undefined;
2199
+ }
2200
+ await withAsyncDirectoryGuards([parentGuard], async () => {
2201
+ await promises_.rename(tempPath, targetPath);
2202
+ });
2203
+ }
2204
+ catch (error) {
2205
+ if (handle && !handleClosedByStream) {
2206
+ await handle.close().catch(() => undefined);
2207
+ }
2208
+ await promises_.rm(tempPath, { force: true }).catch(() => undefined);
2209
+ throw error;
2210
+ }
2211
+ const stat = await promises_.stat(targetPath);
2212
+ return { dev: stat.dev, ino: stat.ino };
2213
+ }
2214
+
2215
+ // EXTERNAL MODULE: external "node:os"
2216
+ var external_node_os_ = __webpack_require__(8161);
2217
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path.js
2218
+
2219
+
2220
+
2221
+
2222
+
2223
+ const ROOT_PATH_ALIAS_POLICIES = {
2224
+ strict: Object.freeze({
2225
+ allowFinalSymlinkForUnlink: false,
2226
+ allowFinalHardlinkForUnlink: false,
2227
+ }),
2228
+ unlinkTarget: Object.freeze({
2229
+ allowFinalSymlinkForUnlink: true,
2230
+ allowFinalHardlinkForUnlink: true,
2231
+ }),
2232
+ };
2233
+ async function resolveRootPath(params) {
2234
+ const rootPath = external_node_path_.resolve(params.rootPath);
2235
+ const absolutePath = external_node_path_.resolve(params.absolutePath);
2236
+ const rootCanonicalPath = params.rootCanonicalPath
2237
+ ? external_node_path_.resolve(params.rootCanonicalPath)
2238
+ : await resolvePathViaExistingAncestor(rootPath);
2239
+ const context = createBoundaryResolutionContext({
2240
+ resolveParams: params,
2241
+ rootPath,
2242
+ absolutePath,
2243
+ rootCanonicalPath,
2244
+ outsideLexicalCanonicalPath: await resolveOutsideLexicalCanonicalPathAsync({
2245
+ rootPath,
2246
+ absolutePath,
2247
+ }),
2248
+ });
2249
+ const outsideResult = await resolveOutsideRootPathAsync({
2250
+ boundaryLabel: params.boundaryLabel,
2251
+ context,
2252
+ });
2253
+ if (outsideResult) {
2254
+ return outsideResult;
2255
+ }
2256
+ return resolveRootPathLexicalAsync({
2257
+ params,
2258
+ absolutePath: context.absolutePath,
2259
+ rootPath: context.rootPath,
2260
+ rootCanonicalPath: context.rootCanonicalPath,
2261
+ });
2262
+ }
2263
+ function resolveRootPathSync(params) {
2264
+ const rootPath = path.resolve(params.rootPath);
2265
+ const absolutePath = path.resolve(params.absolutePath);
2266
+ const rootCanonicalPath = params.rootCanonicalPath
2267
+ ? path.resolve(params.rootCanonicalPath)
2268
+ : resolvePathViaExistingAncestorSync(rootPath);
2269
+ const context = createBoundaryResolutionContext({
2270
+ resolveParams: params,
2271
+ rootPath,
2272
+ absolutePath,
2273
+ rootCanonicalPath,
2274
+ outsideLexicalCanonicalPath: resolveOutsideLexicalCanonicalPathSync({
2275
+ rootPath,
2276
+ absolutePath,
2277
+ }),
2278
+ });
2279
+ const outsideResult = resolveOutsideRootPathSync({
2280
+ boundaryLabel: params.boundaryLabel,
2281
+ context,
2282
+ });
2283
+ if (outsideResult) {
2284
+ return outsideResult;
2285
+ }
2286
+ return resolveRootPathLexicalSync({
2287
+ params,
2288
+ absolutePath: context.absolutePath,
2289
+ rootPath: context.rootPath,
2290
+ rootCanonicalPath: context.rootCanonicalPath,
2291
+ });
2292
+ }
2293
+ function isPromiseLike(value) {
2294
+ return Boolean(value &&
2295
+ (typeof value === "object" || typeof value === "function") &&
2296
+ "then" in value &&
2297
+ typeof value.then === "function");
2298
+ }
2299
+ function createLexicalTraversalState(params) {
2300
+ const relative = external_node_path_.relative(params.rootPath, params.absolutePath);
2301
+ return {
2302
+ segments: relative.split(external_node_path_.sep).filter(Boolean),
2303
+ allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
2304
+ canonicalCursor: params.rootCanonicalPath,
2305
+ lexicalCursor: params.rootPath,
2306
+ preserveFinalSymlink: false,
2307
+ };
2308
+ }
2309
+ function assertLexicalCursorInsideBoundary(params) {
2310
+ assertInsideBoundary({
2311
+ boundaryLabel: params.params.boundaryLabel,
2312
+ rootCanonicalPath: params.rootCanonicalPath,
2313
+ candidatePath: params.candidatePath,
2314
+ absolutePath: params.absolutePath,
2315
+ });
2316
+ }
2317
+ function applyMissingSuffixToCanonicalCursor(params) {
2318
+ const missingSuffix = params.state.segments.slice(params.missingFromIndex);
2319
+ params.state.canonicalCursor = external_node_path_.resolve(params.state.canonicalCursor, ...missingSuffix);
2320
+ assertLexicalCursorInsideBoundary({
2321
+ params: params.params,
2322
+ rootCanonicalPath: params.rootCanonicalPath,
2323
+ candidatePath: params.state.canonicalCursor,
2324
+ absolutePath: params.absolutePath,
2325
+ });
2326
+ }
2327
+ function advanceCanonicalCursorForSegment(params) {
2328
+ params.state.canonicalCursor = external_node_path_.resolve(params.state.canonicalCursor, params.segment);
2329
+ assertLexicalCursorInsideBoundary({
2330
+ params: params.params,
2331
+ rootCanonicalPath: params.rootCanonicalPath,
2332
+ candidatePath: params.state.canonicalCursor,
2333
+ absolutePath: params.absolutePath,
2334
+ });
2335
+ }
2336
+ function finalizeLexicalResolution(params) {
2337
+ assertLexicalCursorInsideBoundary({
2338
+ params: params.params,
2339
+ rootCanonicalPath: params.rootCanonicalPath,
2340
+ candidatePath: params.state.canonicalCursor,
2341
+ absolutePath: params.absolutePath,
2342
+ });
2343
+ return buildResolvedRootPath({
2344
+ absolutePath: params.absolutePath,
2345
+ canonicalPath: params.state.canonicalCursor,
2346
+ rootPath: params.rootPath,
2347
+ rootCanonicalPath: params.rootCanonicalPath,
2348
+ kind: params.kind,
2349
+ });
2350
+ }
2351
+ function handleLexicalLstatFailure(params) {
2352
+ if (!path_isNotFoundPathError(params.error)) {
2353
+ return false;
2354
+ }
2355
+ applyMissingSuffixToCanonicalCursor({
2356
+ state: params.state,
2357
+ missingFromIndex: params.missingFromIndex,
2358
+ rootCanonicalPath: params.rootCanonicalPath,
2359
+ params: params.resolveParams,
2360
+ absolutePath: params.absolutePath,
2361
+ });
2362
+ return true;
2363
+ }
2364
+ function handleLexicalStatReadFailure(params) {
2365
+ if (handleLexicalLstatFailure({
2366
+ error: params.error,
2367
+ state: params.state,
2368
+ missingFromIndex: params.missingFromIndex,
2369
+ rootCanonicalPath: params.rootCanonicalPath,
2370
+ resolveParams: params.resolveParams,
2371
+ absolutePath: params.absolutePath,
2372
+ })) {
2373
+ return null;
2374
+ }
2375
+ throw params.error;
2376
+ }
2377
+ function handleLexicalStatDisposition(params) {
2378
+ if (!params.isSymbolicLink) {
2379
+ advanceCanonicalCursorForSegment({
2380
+ state: params.state,
2381
+ segment: params.segment,
2382
+ rootCanonicalPath: params.rootCanonicalPath,
2383
+ params: params.resolveParams,
2384
+ absolutePath: params.absolutePath,
2385
+ });
2386
+ return "continue";
2387
+ }
2388
+ if (params.state.allowFinalSymlink && params.isLast) {
2389
+ params.state.preserveFinalSymlink = true;
2390
+ advanceCanonicalCursorForSegment({
2391
+ state: params.state,
2392
+ segment: params.segment,
2393
+ rootCanonicalPath: params.rootCanonicalPath,
2394
+ params: params.resolveParams,
2395
+ absolutePath: params.absolutePath,
2396
+ });
2397
+ return "break";
2398
+ }
2399
+ return "resolve-link";
2400
+ }
2401
+ function applyResolvedSymlinkHop(params) {
2402
+ if (!path_isPathInside(params.rootCanonicalPath, params.linkCanonical)) {
2403
+ throw symlinkEscapeError({
2404
+ boundaryLabel: params.boundaryLabel,
2405
+ rootCanonicalPath: params.rootCanonicalPath,
2406
+ symlinkPath: params.state.lexicalCursor,
2407
+ });
2408
+ }
2409
+ params.state.canonicalCursor = params.linkCanonical;
2410
+ params.state.lexicalCursor = params.linkCanonical;
2411
+ }
2412
+ function readLexicalStat(params) {
2413
+ try {
2414
+ const stat = params.read(params.state.lexicalCursor);
2415
+ if (isPromiseLike(stat)) {
2416
+ return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure({ ...params, error }));
2417
+ }
2418
+ return stat;
2419
+ }
2420
+ catch (error) {
2421
+ return handleLexicalStatReadFailure({ ...params, error });
2422
+ }
2423
+ }
2424
+ function resolveAndApplySymlinkHop(params) {
2425
+ const linkCanonical = params.resolveLinkCanonical(params.state.lexicalCursor);
2426
+ if (isPromiseLike(linkCanonical)) {
2427
+ return Promise.resolve(linkCanonical).then((value) => applyResolvedSymlinkHop({
2428
+ state: params.state,
2429
+ linkCanonical: value,
2430
+ rootCanonicalPath: params.rootCanonicalPath,
2431
+ boundaryLabel: params.boundaryLabel,
2432
+ }));
2433
+ }
2434
+ applyResolvedSymlinkHop({
2435
+ state: params.state,
2436
+ linkCanonical,
2437
+ rootCanonicalPath: params.rootCanonicalPath,
2438
+ boundaryLabel: params.boundaryLabel,
2439
+ });
2440
+ }
2441
+ function* iterateLexicalTraversal(state) {
2442
+ for (let idx = 0; idx < state.segments.length; idx += 1) {
2443
+ const segment = state.segments[idx] ?? "";
2444
+ const isLast = idx === state.segments.length - 1;
2445
+ state.lexicalCursor = external_node_path_.join(state.lexicalCursor, segment);
2446
+ yield { idx, segment, isLast };
2447
+ }
2448
+ }
2449
+ async function resolveRootPathLexicalAsync(params) {
2450
+ const state = createLexicalTraversalState(params);
2451
+ const sharedStepParams = {
2452
+ state,
2453
+ rootCanonicalPath: params.rootCanonicalPath,
2454
+ resolveParams: params.params,
2455
+ absolutePath: params.absolutePath,
2456
+ };
2457
+ for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
2458
+ const stat = await readLexicalStat({
2459
+ ...sharedStepParams,
2460
+ missingFromIndex: idx,
2461
+ read: (cursor) => promises_.lstat(cursor),
2462
+ });
2463
+ if (!stat) {
2464
+ break;
2465
+ }
2466
+ const disposition = handleLexicalStatDisposition({
2467
+ ...sharedStepParams,
2468
+ isSymbolicLink: stat.isSymbolicLink(),
2469
+ segment,
2470
+ isLast,
2471
+ });
2472
+ if (disposition === "continue") {
2473
+ continue;
2474
+ }
2475
+ if (disposition === "break") {
2476
+ break;
2477
+ }
2478
+ await resolveAndApplySymlinkHop({
2479
+ state,
2480
+ rootCanonicalPath: params.rootCanonicalPath,
2481
+ boundaryLabel: params.params.boundaryLabel,
2482
+ resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor),
2483
+ });
2484
+ }
2485
+ const kind = await getPathKind(params.absolutePath, state.preserveFinalSymlink);
2486
+ return finalizeLexicalResolution({
2487
+ ...params,
2488
+ state,
2489
+ kind,
2490
+ });
2491
+ }
2492
+ function resolveRootPathLexicalSync(params) {
2493
+ const state = createLexicalTraversalState(params);
2494
+ for (let idx = 0; idx < state.segments.length; idx += 1) {
2495
+ const segment = state.segments[idx] ?? "";
2496
+ const isLast = idx === state.segments.length - 1;
2497
+ state.lexicalCursor = path.join(state.lexicalCursor, segment);
2498
+ const maybeStat = readLexicalStat({
2499
+ state,
2500
+ missingFromIndex: idx,
2501
+ rootCanonicalPath: params.rootCanonicalPath,
2502
+ resolveParams: params.params,
2503
+ absolutePath: params.absolutePath,
2504
+ read: (cursor) => fs.lstatSync(cursor),
2505
+ });
2506
+ if (isPromiseLike(maybeStat)) {
2507
+ throw new Error("Unexpected async lexical stat");
2508
+ }
2509
+ const stat = maybeStat;
2510
+ if (!stat) {
2511
+ break;
2512
+ }
2513
+ const disposition = handleLexicalStatDisposition({
2514
+ state,
2515
+ isSymbolicLink: stat.isSymbolicLink(),
2516
+ segment,
2517
+ isLast,
2518
+ rootCanonicalPath: params.rootCanonicalPath,
2519
+ resolveParams: params.params,
2520
+ absolutePath: params.absolutePath,
2521
+ });
2522
+ if (disposition === "continue") {
2523
+ continue;
2524
+ }
2525
+ if (disposition === "break") {
2526
+ break;
2527
+ }
2528
+ const maybeApplied = resolveAndApplySymlinkHop({
2529
+ state,
2530
+ rootCanonicalPath: params.rootCanonicalPath,
2531
+ boundaryLabel: params.params.boundaryLabel,
2532
+ resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor),
2533
+ });
2534
+ if (isPromiseLike(maybeApplied)) {
2535
+ throw new Error("Unexpected async symlink resolution");
2536
+ }
2537
+ }
2538
+ const kind = getPathKindSync(params.absolutePath, state.preserveFinalSymlink);
2539
+ return finalizeLexicalResolution({
2540
+ ...params,
2541
+ state,
2542
+ kind,
2543
+ });
2544
+ }
2545
+ function resolveCanonicalOutsideLexicalPath(params) {
2546
+ return params.outsideLexicalCanonicalPath ?? params.absolutePath;
2547
+ }
2548
+ function createBoundaryResolutionContext(params) {
2549
+ const lexicalInside = path_isPathInside(params.rootPath, params.absolutePath);
2550
+ const canonicalOutsideLexicalPath = resolveCanonicalOutsideLexicalPath({
2551
+ absolutePath: params.absolutePath,
2552
+ outsideLexicalCanonicalPath: params.outsideLexicalCanonicalPath,
2553
+ });
2554
+ assertLexicalBoundaryOrCanonicalAlias({
2555
+ skipLexicalRootCheck: params.resolveParams.skipLexicalRootCheck,
2556
+ lexicalInside,
2557
+ canonicalOutsideLexicalPath,
2558
+ rootCanonicalPath: params.rootCanonicalPath,
2559
+ boundaryLabel: params.resolveParams.boundaryLabel,
2560
+ rootPath: params.rootPath,
2561
+ absolutePath: params.absolutePath,
2562
+ });
2563
+ return {
2564
+ rootPath: params.rootPath,
2565
+ absolutePath: params.absolutePath,
2566
+ rootCanonicalPath: params.rootCanonicalPath,
2567
+ lexicalInside,
2568
+ canonicalOutsideLexicalPath,
2569
+ };
2570
+ }
2571
+ async function resolveOutsideRootPathAsync(params) {
2572
+ if (params.context.lexicalInside) {
2573
+ return null;
2574
+ }
2575
+ const kind = await getPathKind(params.context.absolutePath, false);
2576
+ return buildOutsideRootPathFromContext({
2577
+ boundaryLabel: params.boundaryLabel,
2578
+ context: params.context,
2579
+ kind,
2580
+ });
2581
+ }
2582
+ function resolveOutsideRootPathSync(params) {
2583
+ if (params.context.lexicalInside) {
2584
+ return null;
2585
+ }
2586
+ const kind = getPathKindSync(params.context.absolutePath, false);
2587
+ return buildOutsideRootPathFromContext({
2588
+ boundaryLabel: params.boundaryLabel,
2589
+ context: params.context,
2590
+ kind,
2591
+ });
2592
+ }
2593
+ function buildOutsideRootPathFromContext(params) {
2594
+ return buildOutsideLexicalRootPath({
2595
+ boundaryLabel: params.boundaryLabel,
2596
+ rootCanonicalPath: params.context.rootCanonicalPath,
2597
+ absolutePath: params.context.absolutePath,
2598
+ canonicalOutsideLexicalPath: params.context.canonicalOutsideLexicalPath,
2599
+ rootPath: params.context.rootPath,
2600
+ kind: params.kind,
2601
+ });
2602
+ }
2603
+ async function resolveOutsideLexicalCanonicalPathAsync(params) {
2604
+ if (path_isPathInside(params.rootPath, params.absolutePath)) {
2605
+ return undefined;
2606
+ }
2607
+ return await resolvePathViaExistingAncestor(params.absolutePath);
2608
+ }
2609
+ function resolveOutsideLexicalCanonicalPathSync(params) {
2610
+ if (isPathInside(params.rootPath, params.absolutePath)) {
2611
+ return undefined;
2612
+ }
2613
+ return resolvePathViaExistingAncestorSync(params.absolutePath);
2614
+ }
2615
+ function buildOutsideLexicalRootPath(params) {
2616
+ assertInsideBoundary({
2617
+ boundaryLabel: params.boundaryLabel,
2618
+ rootCanonicalPath: params.rootCanonicalPath,
2619
+ candidatePath: params.canonicalOutsideLexicalPath,
2620
+ absolutePath: params.absolutePath,
2621
+ });
2622
+ return buildResolvedRootPath({
2623
+ absolutePath: params.absolutePath,
2624
+ canonicalPath: params.canonicalOutsideLexicalPath,
2625
+ rootPath: params.rootPath,
2626
+ rootCanonicalPath: params.rootCanonicalPath,
2627
+ kind: params.kind,
2628
+ });
2629
+ }
2630
+ function assertLexicalBoundaryOrCanonicalAlias(params) {
2631
+ if (params.skipLexicalRootCheck || params.lexicalInside) {
2632
+ return;
2633
+ }
2634
+ if (path_isPathInside(params.rootCanonicalPath, params.canonicalOutsideLexicalPath)) {
2635
+ return;
2636
+ }
2637
+ throw pathEscapeError({
2638
+ boundaryLabel: params.boundaryLabel,
2639
+ rootPath: params.rootPath,
2640
+ absolutePath: params.absolutePath,
2641
+ });
2642
+ }
2643
+ function buildResolvedRootPath(params) {
2644
+ return {
2645
+ absolutePath: params.absolutePath,
2646
+ canonicalPath: params.canonicalPath,
2647
+ rootPath: params.rootPath,
2648
+ rootCanonicalPath: params.rootCanonicalPath,
2649
+ relativePath: relativeInsideRoot(params.rootCanonicalPath, params.canonicalPath),
2650
+ exists: params.kind.exists,
2651
+ kind: params.kind.kind,
2652
+ };
2653
+ }
2654
+ async function resolvePathViaExistingAncestor(targetPath) {
2655
+ const normalized = external_node_path_.resolve(targetPath);
2656
+ let cursor = normalized;
2657
+ const missingSuffix = [];
2658
+ while (!isFilesystemRoot(cursor) && !(await pathExists(cursor))) {
2659
+ missingSuffix.unshift(external_node_path_.basename(cursor));
2660
+ const parent = external_node_path_.dirname(cursor);
2661
+ if (parent === cursor) {
2662
+ break;
2663
+ }
2664
+ cursor = parent;
2665
+ }
2666
+ if (!(await pathExists(cursor))) {
2667
+ return normalized;
2668
+ }
2669
+ try {
2670
+ const resolvedAncestor = external_node_path_.resolve(await promises_.realpath(cursor));
2671
+ if (missingSuffix.length === 0) {
2672
+ return resolvedAncestor;
2673
+ }
2674
+ return external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
2675
+ }
2676
+ catch {
2677
+ return normalized;
2678
+ }
2679
+ }
2680
+ function resolvePathViaExistingAncestorSync(targetPath) {
2681
+ const normalized = path.resolve(targetPath);
2682
+ let cursor = normalized;
2683
+ const missingSuffix = [];
2684
+ while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
2685
+ missingSuffix.unshift(path.basename(cursor));
2686
+ const parent = path.dirname(cursor);
2687
+ if (parent === cursor) {
2688
+ break;
2689
+ }
2690
+ cursor = parent;
2691
+ }
2692
+ if (!fs.existsSync(cursor)) {
2693
+ return normalized;
2694
+ }
2695
+ try {
2696
+ // Keep sync behavior aligned with async (`fsp.realpath`) to avoid
2697
+ // platform-specific canonical alias drift (notably on Windows).
2698
+ const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
2699
+ if (missingSuffix.length === 0) {
2700
+ return resolvedAncestor;
2701
+ }
2702
+ return path.resolve(resolvedAncestor, ...missingSuffix);
2703
+ }
2704
+ catch {
2705
+ return normalized;
2706
+ }
2707
+ }
2708
+ async function getPathKind(absolutePath, preserveFinalSymlink) {
2709
+ try {
2710
+ const stat = preserveFinalSymlink
2711
+ ? await promises_.lstat(absolutePath)
2712
+ : await promises_.stat(absolutePath);
2713
+ return { exists: true, kind: toResolvedKind(stat) };
2714
+ }
2715
+ catch (error) {
2716
+ if (path_isNotFoundPathError(error)) {
2717
+ return { exists: false, kind: "missing" };
2718
+ }
2719
+ throw error;
2720
+ }
2721
+ }
2722
+ function getPathKindSync(absolutePath, preserveFinalSymlink) {
2723
+ try {
2724
+ const stat = preserveFinalSymlink ? fs.lstatSync(absolutePath) : fs.statSync(absolutePath);
2725
+ return { exists: true, kind: toResolvedKind(stat) };
2726
+ }
2727
+ catch (error) {
2728
+ if (isNotFoundPathError(error)) {
2729
+ return { exists: false, kind: "missing" };
2730
+ }
2731
+ throw error;
2732
+ }
2733
+ }
2734
+ function toResolvedKind(stat) {
2735
+ if (stat.isFile()) {
2736
+ return "file";
2737
+ }
2738
+ if (stat.isDirectory()) {
2739
+ return "directory";
2740
+ }
2741
+ if (stat.isSymbolicLink()) {
2742
+ return "symlink";
2743
+ }
2744
+ return "other";
2745
+ }
2746
+ function relativeInsideRoot(rootPath, targetPath) {
2747
+ const relative = external_node_path_.relative(external_node_path_.resolve(rootPath), external_node_path_.resolve(targetPath));
2748
+ if (!relative || relative === ".") {
2749
+ return "";
2750
+ }
2751
+ if (relative.startsWith("..") || external_node_path_.isAbsolute(relative)) {
2752
+ return "";
2753
+ }
2754
+ return relative;
2755
+ }
2756
+ function assertInsideBoundary(params) {
2757
+ if (path_isPathInside(params.rootCanonicalPath, params.candidatePath)) {
2758
+ return;
2759
+ }
2760
+ throw new Error(`Path resolves outside ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.absolutePath)}`);
2761
+ }
2762
+ function pathEscapeError(params) {
2763
+ return new Error(`Path escapes ${params.boundaryLabel} (${shortPath(params.rootPath)}): ${shortPath(params.absolutePath)}`);
2764
+ }
2765
+ function symlinkEscapeError(params) {
2766
+ return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
2767
+ }
2768
+ function shortPath(value) {
2769
+ const home = external_node_os_.homedir();
2770
+ if (value.startsWith(home)) {
2771
+ return `~${value.slice(home.length)}`;
2772
+ }
2773
+ return value;
2774
+ }
2775
+ function isFilesystemRoot(candidate) {
2776
+ return external_node_path_.parse(candidate).root === candidate;
2777
+ }
2778
+ async function pathExists(targetPath) {
2779
+ try {
2780
+ await promises_.lstat(targetPath);
2781
+ return true;
2782
+ }
2783
+ catch (error) {
2784
+ if (path_isNotFoundPathError(error)) {
2785
+ return false;
2786
+ }
2787
+ throw error;
2788
+ }
2789
+ }
2790
+ async function resolveSymlinkHopPath(symlinkPath) {
2791
+ try {
2792
+ return external_node_path_.resolve(await promises_.realpath(symlinkPath));
2793
+ }
2794
+ catch (error) {
2795
+ if (!path_isNotFoundPathError(error)) {
2796
+ throw error;
2797
+ }
2798
+ const linkTarget = await promises_.readlink(symlinkPath);
2799
+ const linkAbsolute = external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget);
2800
+ return resolvePathViaExistingAncestor(linkAbsolute);
2801
+ }
2802
+ }
2803
+ function resolveSymlinkHopPathSync(symlinkPath) {
2804
+ try {
2805
+ return path.resolve(fs.realpathSync(symlinkPath));
2806
+ }
2807
+ catch (error) {
2808
+ if (!isNotFoundPathError(error)) {
2809
+ throw error;
2810
+ }
2811
+ const linkTarget = fs.readlinkSync(symlinkPath);
2812
+ const linkAbsolute = path.resolve(path.dirname(symlinkPath), linkTarget);
2813
+ return resolvePathViaExistingAncestorSync(linkAbsolute);
2814
+ }
2815
+ }
2816
+
2817
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-policy.js
2818
+
2819
+
2820
+
2821
+
2822
+ const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
2823
+ async function assertNoPathAliasEscape(params) {
2824
+ const resolved = await resolveRootPath({
2825
+ absolutePath: params.absolutePath,
2826
+ rootPath: params.rootPath,
2827
+ boundaryLabel: params.boundaryLabel,
2828
+ policy: params.policy,
2829
+ });
2830
+ const allowFinalSymlink = params.policy?.allowFinalSymlinkForUnlink === true;
2831
+ if (allowFinalSymlink && resolved.kind === "symlink") {
2832
+ return;
2833
+ }
2834
+ await assertNoHardlinkedFinalPath({
2835
+ filePath: resolved.absolutePath,
2836
+ root: resolved.rootPath,
2837
+ boundaryLabel: params.boundaryLabel,
2838
+ allowFinalHardlinkForUnlink: params.policy?.allowFinalHardlinkForUnlink,
2839
+ });
2840
+ }
2841
+ async function assertNoHardlinkedFinalPath(params) {
2842
+ if (params.allowFinalHardlinkForUnlink) {
2843
+ return;
2844
+ }
2845
+ let stat;
2846
+ try {
2847
+ stat = await promises_.stat(params.filePath);
2848
+ }
2849
+ catch (err) {
2850
+ if (path_isNotFoundPathError(err)) {
2851
+ return;
2852
+ }
2853
+ throw err;
2854
+ }
2855
+ if (!stat.isFile()) {
2856
+ return;
2857
+ }
2858
+ if (stat.nlink > 1) {
2859
+ throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${path_policy_shortPath(params.root)}): ${path_policy_shortPath(params.filePath)}`);
2860
+ }
2861
+ }
2862
+ function path_policy_shortPath(value) {
2863
+ if (value.startsWith(external_node_os_.homedir())) {
2864
+ return `~${value.slice(external_node_os_.homedir().length)}`;
2865
+ }
2866
+ return value;
2867
+ }
2868
+
2869
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-stat.js
2870
+ function pathStatFromStats(stat) {
2871
+ return {
2872
+ dev: Number(stat.dev),
2873
+ gid: Number(stat.gid),
2874
+ ino: Number(stat.ino),
2875
+ isDirectory: stat.isDirectory(),
2876
+ isFile: stat.isFile(),
2877
+ isSymbolicLink: stat.isSymbolicLink(),
2878
+ mode: stat.mode,
2879
+ mtimeMs: stat.mtimeMs,
2880
+ nlink: stat.nlink,
2881
+ size: stat.size,
2882
+ uid: stat.uid,
2883
+ };
2884
+ }
2885
+
2886
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/home-dir.js
2887
+
2888
+
2889
+
2890
+ function normalize(value) {
2891
+ const trimmed = normalizeOptionalString(value);
2892
+ if (!trimmed) {
2893
+ return undefined;
2894
+ }
2895
+ if (trimmed === "undefined" || trimmed === "null") {
2896
+ return undefined;
2897
+ }
2898
+ return trimmed;
2899
+ }
2900
+ function resolveEffectiveHomeDir(env = process.env, homedir = external_node_os_.homedir) {
2901
+ const raw = resolveRawHomeDir(env, homedir);
2902
+ return raw ? external_node_path_.resolve(raw) : undefined;
2903
+ }
2904
+ function resolveOsHomeDir(env = process.env, homedir = os.homedir) {
2905
+ const raw = resolveRawOsHomeDir(env, homedir);
2906
+ return raw ? path.resolve(raw) : undefined;
2907
+ }
2908
+ function resolveRawHomeDir(env, homedir) {
2909
+ const explicitHome = normalize(env.OPENCLAW_HOME);
2910
+ if (!explicitHome) {
2911
+ return resolveRawOsHomeDir(env, homedir);
2912
+ }
2913
+ const segments = external_node_path_.normalize(explicitHome).split(external_node_path_.sep);
2914
+ if (segments[0] !== "~") {
2915
+ return explicitHome;
2916
+ }
2917
+ // OPENCLAW_HOME starts with "~"; expand against the os home dir. Fall
2918
+ // back to undefined when there is no os home to expand against rather
2919
+ // than returning a raw "~"-prefixed path the caller cannot use.
2920
+ const fallbackHome = resolveRawOsHomeDir(env, homedir);
2921
+ if (!fallbackHome) {
2922
+ return undefined;
2923
+ }
2924
+ return expandHomePrefix(explicitHome, { home: fallbackHome });
2925
+ }
2926
+ function resolveRawOsHomeDir(env, homedir) {
2927
+ const envHome = normalize(env.HOME);
2928
+ if (envHome) {
2929
+ return envHome;
2930
+ }
2931
+ const userProfile = normalize(env.USERPROFILE);
2932
+ if (userProfile) {
2933
+ return userProfile;
2934
+ }
2935
+ return normalizeSafe(homedir);
2936
+ }
2937
+ function normalizeSafe(homedir) {
2938
+ try {
2939
+ return normalize(homedir());
2940
+ }
2941
+ catch {
2942
+ return undefined;
2943
+ }
2944
+ }
2945
+ function resolveRequiredHomeDir(env = process.env, homedir = os.homedir) {
2946
+ return resolveEffectiveHomeDir(env, homedir) ?? path.resolve(process.cwd());
2947
+ }
2948
+ function resolveRequiredOsHomeDir(env = process.env, homedir = os.homedir) {
2949
+ return resolveOsHomeDir(env, homedir) ?? path.resolve(process.cwd());
2950
+ }
2951
+ function expandHomePrefix(input, opts) {
2952
+ const segments = external_node_path_.normalize(input).split(external_node_path_.sep);
2953
+ if (segments[0] !== "~") {
2954
+ return input;
2955
+ }
2956
+ const home = normalize(opts?.home) ??
2957
+ resolveEffectiveHomeDir(opts?.env ?? process.env, opts?.homedir ?? external_node_os_.homedir);
2958
+ if (!home) {
2959
+ return input;
2960
+ }
2961
+ return external_node_path_.join(home, ...segments.slice(1));
2962
+ }
2963
+ function resolveHomeRelativePath(input, opts) {
2964
+ if (!input) {
2965
+ return input;
2966
+ }
2967
+ const segments = path.normalize(input).split(path.sep);
2968
+ if (segments[0] !== "~") {
2969
+ return path.resolve(input);
2970
+ }
2971
+ const expanded = expandHomePrefix(input, {
2972
+ home: resolveRequiredHomeDir(opts?.env ?? process.env, opts?.homedir ?? os.homedir),
2973
+ env: opts?.env,
2974
+ homedir: opts?.homedir,
2975
+ });
2976
+ return path.resolve(expanded);
2977
+ }
2978
+ function resolveUserPath(input, optsOrEnv, homedir) {
2979
+ const opts = optsOrEnv && ("env" in optsOrEnv || "homedir" in optsOrEnv)
2980
+ ? optsOrEnv
2981
+ : { env: optsOrEnv, homedir };
2982
+ return resolveHomeRelativePath(input, opts);
2983
+ }
2984
+ function resolveOsHomeRelativePath(input, opts) {
2985
+ if (!input) {
2986
+ return input;
2987
+ }
2988
+ const segments = path.normalize(input).split(path.sep);
2989
+ if (segments[0] !== "~") {
2990
+ return path.resolve(input);
2991
+ }
2992
+ const expanded = expandHomePrefix(input, {
2993
+ home: resolveRequiredOsHomeDir(opts?.env ?? process.env, opts?.homedir ?? os.homedir),
2994
+ env: opts?.env,
2995
+ homedir: opts?.homedir,
2996
+ });
2997
+ return path.resolve(expanded);
2998
+ }
2999
+
3000
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-context.js
3001
+
3002
+
3003
+
3004
+
3005
+
3006
+
3007
+ const ensureTrailingSep = (value) => value.endsWith(external_node_path_.sep) ? value : value + external_node_path_.sep;
3008
+ function assertValidRootRelativePath(relativePath) {
3009
+ path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
3010
+ }
3011
+ let cachedHomePath;
3012
+ async function expandRelativePathWithHome(relativePath) {
3013
+ const rawHome = process.env.HOME || process.env.USERPROFILE || external_node_os_.homedir();
3014
+ if (cachedHomePath?.raw !== rawHome) {
3015
+ let realHome = rawHome;
3016
+ try {
3017
+ realHome = await promises_.realpath(rawHome);
3018
+ }
3019
+ catch {
3020
+ // If the home dir cannot be canonicalized, keep lexical expansion behavior.
3021
+ }
3022
+ cachedHomePath = { raw: rawHome, real: realHome };
3023
+ }
3024
+ return expandHomePrefix(relativePath, { home: cachedHomePath.real });
3025
+ }
3026
+ async function resolveRootContext(rootDir) {
3027
+ path_assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
3028
+ let rootReal;
3029
+ try {
3030
+ rootReal = await promises_.realpath(rootDir);
3031
+ const rootStat = await promises_.stat(rootReal);
3032
+ if (!rootStat.isDirectory()) {
3033
+ throw new errors_FsSafeError("invalid-path", "root dir is not a directory");
3034
+ }
3035
+ }
3036
+ catch (err) {
3037
+ if (err instanceof errors_FsSafeError) {
3038
+ throw err;
3039
+ }
3040
+ if (path_isNotFoundPathError(err)) {
3041
+ throw new errors_FsSafeError("not-found", "root dir not found");
3042
+ }
3043
+ throw err;
3044
+ }
3045
+ return {
3046
+ rootDir: external_node_path_.resolve(rootDir),
3047
+ rootReal,
3048
+ rootWithSep: ensureTrailingSep(rootReal),
3049
+ };
3050
+ }
3051
+ async function resolvePathInRoot(root, relativePath) {
3052
+ assertValidRootRelativePath(relativePath);
3053
+ const expanded = await expandRelativePathWithHome(relativePath);
3054
+ const resolved = external_node_path_.resolve(root.rootWithSep, expanded);
3055
+ if (!path_isPathInside(root.rootWithSep, resolved)) {
3056
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
3057
+ }
3058
+ return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved };
3059
+ }
3060
+ async function resolvePathWithinRoot(params) {
3061
+ return await resolvePathInRoot(await resolveRootContext(params.rootDir), params.relativePath);
3062
+ }
3063
+
3064
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-errors.js
3065
+
3066
+
3067
+ function isAlreadyExistsError(error) {
3068
+ return hasNodeErrorCode(error, "EEXIST") || /File exists|EEXIST/i.test(String(error));
3069
+ }
3070
+ function normalizePinnedWriteError(error) {
3071
+ if (error instanceof errors_FsSafeError) {
3072
+ return error;
3073
+ }
3074
+ return new errors_FsSafeError("invalid-path", "path is not a regular file under root", {
3075
+ cause: error instanceof Error ? error : undefined,
3076
+ });
3077
+ }
3078
+ function normalizePinnedPathError(error) {
3079
+ if (error instanceof errors_FsSafeError) {
3080
+ return error;
3081
+ }
3082
+ return new errors_FsSafeError("path-alias", "path is not under root", {
3083
+ cause: error instanceof Error ? error : undefined,
3084
+ });
3085
+ }
3086
+
3087
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
3088
+ let test_hooks_fsSafeTestHooks;
3089
+ function allowFsSafeTestHooks() {
3090
+ return false || process.env.VITEST === "true";
3091
+ }
3092
+ function getFsSafeTestHooks() {
3093
+ return test_hooks_fsSafeTestHooks;
3094
+ }
3095
+ function __setFsSafeTestHooksForTest(hooks) {
3096
+ if (hooks && !allowFsSafeTestHooks()) {
3097
+ throw new Error("__setFsSafeTestHooksForTest is only available in tests");
3098
+ }
3099
+ test_hooks_fsSafeTestHooks = hooks;
3100
+ }
3101
+
3102
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
3103
+
3104
+ const tempCleanupEntries = new Map();
3105
+ let cleanupRegistered = false;
3106
+ function cleanupRegisteredTempPathsSync() {
3107
+ for (const entry of tempCleanupEntries.values()) {
3108
+ try {
3109
+ external_node_fs_.rmSync(entry.path, { force: true, recursive: entry.recursive });
3110
+ }
3111
+ catch {
3112
+ // Process-exit cleanup is best-effort.
3113
+ }
3114
+ }
3115
+ tempCleanupEntries.clear();
3116
+ }
3117
+ function registerTempPathForExit(tempPath, options) {
3118
+ if (!cleanupRegistered) {
3119
+ cleanupRegistered = true;
3120
+ process.once("exit", cleanupRegisteredTempPathsSync);
3121
+ }
3122
+ tempCleanupEntries.set(tempPath, {
3123
+ path: tempPath,
3124
+ recursive: options?.recursive === true,
3125
+ });
3126
+ return () => {
3127
+ tempCleanupEntries.delete(tempPath);
3128
+ };
3129
+ }
3130
+ function __cleanupRegisteredTempPathsForTest() {
3131
+ cleanupRegisteredTempPathsSync();
3132
+ }
3133
+ function __cleanupRegisteredTempPathForTest(tempPath) {
3134
+ const entry = tempCleanupEntries.get(tempPath);
3135
+ if (!entry) {
3136
+ return;
3137
+ }
3138
+ try {
3139
+ fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
3140
+ }
3141
+ finally {
3142
+ tempCleanupEntries.delete(tempPath);
3143
+ }
3144
+ }
3145
+
3146
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/write-queue.js
3147
+ const writeQueues = new Map();
3148
+ async function serializePathWrite(key, run) {
3149
+ const previous = writeQueues.get(key) ?? Promise.resolve();
3150
+ const task = (async () => {
3151
+ await previous.catch(() => undefined);
3152
+ return await run();
3153
+ })();
3154
+ const done = task.then(() => undefined, () => undefined);
3155
+ writeQueues.set(key, done);
3156
+ try {
3157
+ return await task;
3158
+ }
3159
+ finally {
3160
+ if (writeQueues.get(key) === done) {
3161
+ writeQueues.delete(key);
3162
+ }
3163
+ }
3164
+ }
3165
+
3166
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-impl.js
3167
+
3168
+
3169
+
3170
+
3171
+
3172
+
3173
+
3174
+
3175
+
3176
+
3177
+
3178
+
3179
+
3180
+
3181
+
3182
+
3183
+
3184
+
3185
+
3186
+
3187
+
3188
+
3189
+
3190
+
3191
+ function logWarn(message) {
3192
+ if (process.env.FS_SAFE_DEBUG_WARNINGS === "1") {
3193
+ console.warn(message);
3194
+ }
3195
+ }
3196
+ const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants;
3197
+ const NONBLOCK_OPEN_FLAG = "O_NONBLOCK" in external_node_fs_.constants ? external_node_fs_.constants.O_NONBLOCK : 0;
3198
+ const OPEN_READ_FLAGS = external_node_fs_.constants.O_RDONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
3199
+ const OPEN_READ_NONBLOCK_FLAGS = OPEN_READ_FLAGS | NONBLOCK_OPEN_FLAG;
3200
+ const OPEN_READ_FOLLOW_FLAGS = external_node_fs_.constants.O_RDONLY;
3201
+ const OPEN_READ_FOLLOW_NONBLOCK_FLAGS = OPEN_READ_FOLLOW_FLAGS | NONBLOCK_OPEN_FLAG;
3202
+ const OPEN_WRITE_EXISTING_FLAGS = external_node_fs_.constants.O_WRONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
3203
+ const OPEN_WRITE_CREATE_FLAGS = external_node_fs_.constants.O_WRONLY |
3204
+ external_node_fs_.constants.O_CREAT |
3205
+ external_node_fs_.constants.O_EXCL |
3206
+ (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
3207
+ const OPEN_APPEND_EXISTING_FLAGS = external_node_fs_.constants.O_RDWR | external_node_fs_.constants.O_APPEND | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
3208
+ const OPEN_APPEND_CREATE_FLAGS = external_node_fs_.constants.O_RDWR |
3209
+ external_node_fs_.constants.O_APPEND |
3210
+ external_node_fs_.constants.O_CREAT |
3211
+ external_node_fs_.constants.O_EXCL |
3212
+ (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
3213
+ const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
3214
+ function closeHandleForDispose(handle) {
3215
+ return handle.close().catch(() => undefined);
3216
+ }
3217
+ function openResult(params) {
3218
+ return {
3219
+ handle: params.handle,
3220
+ realPath: params.realPath,
3221
+ stat: params.stat,
3222
+ [Symbol.asyncDispose]: async () => {
3223
+ await closeHandleForDispose(params.handle);
3224
+ },
3225
+ };
3226
+ }
3227
+ async function openVerifiedLocalFile(filePath, options) {
3228
+ const fsSafeTestHooks = getFsSafeTestHooks();
3229
+ // Reject directories before opening so we never surface EISDIR to callers (e.g. tool
3230
+ // results that get sent to messaging channels). See openclaw/openclaw#31186.
3231
+ try {
3232
+ const preStat = await promises_.lstat(filePath);
3233
+ if (preStat.isDirectory()) {
3234
+ throw new errors_FsSafeError("not-file", "not a file");
785
3235
  }
3236
+ await fsSafeTestHooks?.afterPreOpenLstat?.(filePath);
786
3237
  }
787
- if (endIndex === -1) {
3238
+ catch (err) {
3239
+ if (err instanceof errors_FsSafeError) {
3240
+ throw err;
3241
+ }
3242
+ // ENOENT and other lstat errors: fall through and let fs.open handle.
3243
+ }
3244
+ let handle;
3245
+ try {
3246
+ const openFlags = options?.symlinks === "follow-within-root"
3247
+ ? options?.nonBlockingRead
3248
+ ? OPEN_READ_FOLLOW_NONBLOCK_FLAGS
3249
+ : OPEN_READ_FOLLOW_FLAGS
3250
+ : options?.nonBlockingRead
3251
+ ? OPEN_READ_NONBLOCK_FLAGS
3252
+ : OPEN_READ_FLAGS;
3253
+ await fsSafeTestHooks?.beforeOpen?.(filePath, openFlags);
3254
+ handle = await promises_.open(filePath, openFlags);
3255
+ try {
3256
+ await fsSafeTestHooks?.afterOpen?.(filePath, handle);
3257
+ }
3258
+ catch (err) {
3259
+ await handle.close().catch(() => { });
3260
+ throw err;
3261
+ }
3262
+ }
3263
+ catch (err) {
3264
+ if (path_isNotFoundPathError(err)) {
3265
+ throw new errors_FsSafeError("not-found", "file not found");
3266
+ }
3267
+ if (isSymlinkOpenError(err)) {
3268
+ throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
3269
+ }
3270
+ // Defensive: if open still throws EISDIR (e.g. race), sanitize so it never leaks.
3271
+ if (hasNodeErrorCode(err, "EISDIR")) {
3272
+ throw new errors_FsSafeError("not-file", "not a file");
3273
+ }
3274
+ throw err;
3275
+ }
3276
+ try {
3277
+ const stat = await handle.stat();
3278
+ if (!stat.isFile()) {
3279
+ throw new errors_FsSafeError("not-file", "not a file");
3280
+ }
3281
+ if (options?.hardlinks === "reject" && stat.nlink > 1) {
3282
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
3283
+ }
3284
+ if (options?.symlinks === "follow-within-root") {
3285
+ const pathStat = await promises_.stat(filePath);
3286
+ if (!file_identity_sameFileIdentity(stat, pathStat)) {
3287
+ throw new errors_FsSafeError("path-mismatch", "path changed during read");
3288
+ }
3289
+ }
3290
+ else {
3291
+ const pathStat = await promises_.lstat(filePath);
3292
+ if (pathStat.isSymbolicLink()) {
3293
+ throw new errors_FsSafeError("symlink", "symlink not allowed");
3294
+ }
3295
+ if (!file_identity_sameFileIdentity(stat, pathStat)) {
3296
+ throw new errors_FsSafeError("path-mismatch", "path changed during read");
3297
+ }
3298
+ }
3299
+ const realPath = await resolveOpenedFileRealPathForHandle(handle, filePath);
3300
+ const realStat = await promises_.stat(realPath);
3301
+ if (options?.hardlinks === "reject" && realStat.nlink > 1) {
3302
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
3303
+ }
3304
+ if (!file_identity_sameFileIdentity(stat, realStat)) {
3305
+ throw new errors_FsSafeError("path-mismatch", "path mismatch");
3306
+ }
3307
+ return openResult({ handle, realPath, stat });
3308
+ }
3309
+ catch (err) {
3310
+ await handle.close().catch(() => { });
3311
+ if (err instanceof errors_FsSafeError) {
3312
+ throw err;
3313
+ }
3314
+ if (path_isNotFoundPathError(err)) {
3315
+ throw new errors_FsSafeError("not-found", "file not found");
3316
+ }
3317
+ throw err;
3318
+ }
3319
+ }
3320
+ class RootHandle {
3321
+ rootDir;
3322
+ rootReal;
3323
+ rootWithSep;
3324
+ defaults;
3325
+ constructor(context, defaults = {}) {
3326
+ this.rootDir = context.rootDir;
3327
+ this.rootReal = context.rootReal;
3328
+ this.rootWithSep = context.rootWithSep;
3329
+ this.defaults = defaults;
3330
+ }
3331
+ get context() {
788
3332
  return {
789
- ok: false,
790
- reason: "missing"
3333
+ rootDir: this.rootDir,
3334
+ rootReal: this.rootReal,
3335
+ rootWithSep: this.rootWithSep,
791
3336
  };
792
3337
  }
793
- const yamlContent = lines.slice(1, endIndex).join("\n");
794
- let data;
3338
+ async resolve(relativePath) {
3339
+ return (await resolvePathInRoot(this.context, relativePath)).resolved;
3340
+ }
3341
+ async open(relativePath, options = {}) {
3342
+ return await openFileInRoot(this.context, {
3343
+ relativePath,
3344
+ ...readDefaults(this.defaults),
3345
+ ...options,
3346
+ });
3347
+ }
3348
+ async read(relativePath, options = {}) {
3349
+ return await readFileInRoot(this.context, {
3350
+ relativePath,
3351
+ ...readDefaults(this.defaults),
3352
+ ...options,
3353
+ });
3354
+ }
3355
+ async readBytes(relativePath, options = {}) {
3356
+ return (await this.read(relativePath, options)).buffer;
3357
+ }
3358
+ async readText(relativePath, options = {}) {
3359
+ const { encoding = "utf8", ...readOptions } = options;
3360
+ return (await this.read(relativePath, readOptions)).buffer.toString(encoding);
3361
+ }
3362
+ async readJson(relativePath, options = {}) {
3363
+ return JSON.parse(await this.readText(relativePath, options));
3364
+ }
3365
+ async readAbsolute(filePath, options = {}) {
3366
+ return await readPathInRoot(this.context, {
3367
+ filePath,
3368
+ ...readDefaults(this.defaults),
3369
+ ...options,
3370
+ });
3371
+ }
3372
+ reader(options = {}) {
3373
+ return async (filePath) => {
3374
+ return (await this.readAbsolute(filePath, options)).buffer;
3375
+ };
3376
+ }
3377
+ async openWritable(relativePath, options = {}) {
3378
+ const writeMode = options.writeMode ?? "replace";
3379
+ return await openWritableFileInRoot(this.context, {
3380
+ relativePath,
3381
+ mkdir: this.defaults.mkdir,
3382
+ mode: this.defaults.mode,
3383
+ ...options,
3384
+ append: writeMode === "append",
3385
+ truncateExisting: writeMode === "replace",
3386
+ });
3387
+ }
3388
+ async append(relativePath, data, options = {}) {
3389
+ await appendFileInRoot(this.context, {
3390
+ relativePath,
3391
+ data,
3392
+ mkdir: this.defaults.mkdir,
3393
+ mode: this.defaults.mode,
3394
+ ...options,
3395
+ });
3396
+ }
3397
+ async remove(relativePath) {
3398
+ assertValidRootRelativePath(relativePath);
3399
+ await removePathInRoot(this.context, relativePath);
3400
+ }
3401
+ async mkdir(relativePath) {
3402
+ assertValidRootRelativePath(relativePath);
3403
+ await mkdirPathInRoot(this.context, { relativePath });
3404
+ }
3405
+ async ensureRoot() {
3406
+ await mkdirPathInRoot(this.context, { relativePath: "", allowRoot: true });
3407
+ }
3408
+ async write(relativePath, data, options = {}) {
3409
+ await writeFileInRoot(this.context, {
3410
+ relativePath,
3411
+ data,
3412
+ mkdir: this.defaults.mkdir,
3413
+ mode: this.defaults.mode,
3414
+ ...options,
3415
+ });
3416
+ }
3417
+ async create(relativePath, data, options = {}) {
3418
+ await writeFileInRoot(this.context, {
3419
+ relativePath,
3420
+ data,
3421
+ mkdir: this.defaults.mkdir,
3422
+ mode: this.defaults.mode,
3423
+ ...options,
3424
+ overwrite: false,
3425
+ });
3426
+ }
3427
+ async writeJson(relativePath, data, options = {}) {
3428
+ const { replacer, space, trailingNewline = true, ...writeOptions } = options;
3429
+ const json = JSON.stringify(data, replacer, space);
3430
+ await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
3431
+ }
3432
+ async createJson(relativePath, data, options = {}) {
3433
+ const { replacer, space, trailingNewline = true, ...writeOptions } = options;
3434
+ const json = JSON.stringify(data, replacer, space);
3435
+ await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
3436
+ }
3437
+ async copyIn(relativePath, sourcePath, options = {}) {
3438
+ assertValidRootRelativePath(relativePath);
3439
+ await copyFileInRoot(this.context, {
3440
+ sourcePath,
3441
+ relativePath,
3442
+ maxBytes: this.defaults.maxBytes,
3443
+ mkdir: this.defaults.mkdir,
3444
+ mode: this.defaults.mode,
3445
+ ...options,
3446
+ });
3447
+ }
3448
+ async exists(relativePath) {
3449
+ try {
3450
+ await this.stat(relativePath);
3451
+ return true;
3452
+ }
3453
+ catch (err) {
3454
+ if (err instanceof errors_FsSafeError && err.code === "not-found") {
3455
+ return false;
3456
+ }
3457
+ throw err;
3458
+ }
3459
+ }
3460
+ async stat(relativePath) {
3461
+ assertValidRootRelativePath(relativePath);
3462
+ try {
3463
+ return await helperStat(this.rootReal, relativePath);
3464
+ }
3465
+ catch (error) {
3466
+ if (canFallbackFromPythonError(error)) {
3467
+ return await statPathFallback(this.context, relativePath);
3468
+ }
3469
+ throw error;
3470
+ }
3471
+ }
3472
+ async list(relativePath, options = {}) {
3473
+ assertValidRootRelativePath(relativePath);
3474
+ try {
3475
+ return options.withFileTypes === true
3476
+ ? await helperReaddir(this.rootReal, relativePath, true)
3477
+ : await helperReaddir(this.rootReal, relativePath, false);
3478
+ }
3479
+ catch (error) {
3480
+ if (canFallbackFromPythonError(error)) {
3481
+ return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
3482
+ }
3483
+ throw error;
3484
+ }
3485
+ }
3486
+ async move(fromRelative, toRelative, options = {}) {
3487
+ assertValidRootRelativePath(fromRelative);
3488
+ assertValidRootRelativePath(toRelative);
3489
+ try {
3490
+ await runPinnedHelper("rename", this.rootReal, {
3491
+ from: fromRelative,
3492
+ overwrite: options.overwrite ?? false,
3493
+ to: toRelative,
3494
+ });
3495
+ }
3496
+ catch (error) {
3497
+ if (canFallbackFromPythonError(error)) {
3498
+ await movePathFallback(this.context, {
3499
+ fromRelative,
3500
+ overwrite: options.overwrite ?? false,
3501
+ toRelative,
3502
+ });
3503
+ return;
3504
+ }
3505
+ throw error;
3506
+ }
3507
+ }
3508
+ }
3509
+ function readDefaults(defaults) {
3510
+ return {
3511
+ hardlinks: defaults.hardlinks,
3512
+ maxBytes: defaults.maxBytes ?? DEFAULT_ROOT_MAX_BYTES,
3513
+ nonBlockingRead: defaults.nonBlockingRead,
3514
+ symlinks: defaults.symlinks,
3515
+ };
3516
+ }
3517
+ async function root_impl_root(rootDir, defaults = {}) {
3518
+ return new RootHandle(await resolveRootContext(rootDir), defaults);
3519
+ }
3520
+ async function openFileInRoot(root, params) {
3521
+ const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
3522
+ let opened;
795
3523
  try {
796
- const parsed = (0,dist/* .parse */.qg)(yamlContent, {
797
- maxAliasCount: 0
3524
+ opened = await openVerifiedLocalFile(resolved, {
3525
+ nonBlockingRead: params.nonBlockingRead,
3526
+ symlinks: params.symlinks,
798
3527
  });
799
- data = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
800
- } catch (error) {
801
- const detail = error instanceof Error ? error.message : "Unknown YAML parse error";
3528
+ }
3529
+ catch (err) {
3530
+ if (err instanceof errors_FsSafeError) {
3531
+ throw err;
3532
+ }
3533
+ throw err;
3534
+ }
3535
+ if (params.hardlinks !== "allow" && opened.stat.nlink > 1) {
3536
+ await opened.handle.close().catch(() => { });
3537
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
3538
+ }
3539
+ if (!path_isPathInside(rootWithSep, opened.realPath)) {
3540
+ await opened.handle.close().catch(() => { });
3541
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
3542
+ }
3543
+ return opened;
3544
+ }
3545
+ async function readFileInRoot(root, params) {
3546
+ const opened = await openFileInRoot(root, params);
3547
+ try {
3548
+ return await readOpenedFileSafely({ opened, maxBytes: params.maxBytes });
3549
+ }
3550
+ finally {
3551
+ await opened.handle.close().catch(() => { });
3552
+ }
3553
+ }
3554
+ async function readPathInRoot(root, params) {
3555
+ const rootDir = root.rootDir;
3556
+ const candidatePath = external_node_path_.isAbsolute(params.filePath)
3557
+ ? external_node_path_.resolve(params.filePath)
3558
+ : external_node_path_.resolve(rootDir, params.filePath);
3559
+ const relativePath = external_node_path_.relative(rootDir, candidatePath);
3560
+ return await readFileInRoot(root, {
3561
+ relativePath,
3562
+ hardlinks: params.hardlinks,
3563
+ maxBytes: params.maxBytes,
3564
+ nonBlockingRead: params.nonBlockingRead,
3565
+ symlinks: params.symlinks,
3566
+ });
3567
+ }
3568
+ async function readLocalFileSafely(params) {
3569
+ const opened = await openLocalFileSafely({ filePath: params.filePath });
3570
+ try {
3571
+ return await readOpenedFileSafely({ opened, maxBytes: params.maxBytes });
3572
+ }
3573
+ finally {
3574
+ await opened.handle.close().catch(() => { });
3575
+ }
3576
+ }
3577
+ async function openLocalFileSafely(params) {
3578
+ assertNoNulPathInput(params.filePath, "file path contains a NUL byte");
3579
+ return await openVerifiedLocalFile(params.filePath);
3580
+ }
3581
+ async function readOpenedFileSafely(params) {
3582
+ if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
3583
+ throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
3584
+ }
3585
+ const buffer = await params.opened.handle.readFile();
3586
+ if (params.maxBytes !== undefined && buffer.byteLength > params.maxBytes) {
3587
+ throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${buffer.byteLength})`);
3588
+ }
3589
+ return {
3590
+ buffer,
3591
+ realPath: params.opened.realPath,
3592
+ stat: params.opened.stat,
3593
+ };
3594
+ }
3595
+ function emitWriteBoundaryWarning(reason) {
3596
+ logWarn(`security: fs-safe write boundary warning (${reason})`);
3597
+ }
3598
+ function buildAtomicWriteTempPath(targetPath) {
3599
+ const dir = external_node_path_.dirname(targetPath);
3600
+ const base = external_node_path_.basename(targetPath);
3601
+ return external_node_path_.join(dir, `.${base}.${process.pid}.${(0,external_node_crypto_.randomUUID)()}.tmp`);
3602
+ }
3603
+ function rootWriteQueueKey(root, relativePath) {
3604
+ return `${root.rootReal}\0${relativePath}`;
3605
+ }
3606
+ async function writeTempFileForAtomicReplace(params) {
3607
+ const tempHandle = await promises_.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
3608
+ try {
3609
+ if (typeof params.data === "string") {
3610
+ await tempHandle.writeFile(params.data, params.encoding ?? "utf8");
3611
+ }
3612
+ else {
3613
+ await tempHandle.writeFile(params.data);
3614
+ }
3615
+ return await tempHandle.stat();
3616
+ }
3617
+ finally {
3618
+ await tempHandle.close().catch(() => { });
3619
+ }
3620
+ }
3621
+ async function verifyAtomicWriteResult(params) {
3622
+ const opened = await openVerifiedLocalFile(params.targetPath, { hardlinks: "reject" });
3623
+ try {
3624
+ if (!file_identity_sameFileIdentity(opened.stat, params.expectedIdentity)) {
3625
+ throw new errors_FsSafeError("path-mismatch", "path changed during write");
3626
+ }
3627
+ if (!path_isPathInside(params.root.rootWithSep, opened.realPath)) {
3628
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
3629
+ }
3630
+ }
3631
+ finally {
3632
+ await opened.handle.close().catch(() => { });
3633
+ }
3634
+ }
3635
+ async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
3636
+ const handleStat = await handle.stat();
3637
+ const fdCandidates = process.platform === "linux"
3638
+ ? [`/proc/self/fd/${handle.fd}`, `/dev/fd/${handle.fd}`]
3639
+ : process.platform === "win32"
3640
+ ? []
3641
+ : [`/dev/fd/${handle.fd}`];
3642
+ for (const fdPath of fdCandidates) {
3643
+ try {
3644
+ const fdRealPath = await promises_.realpath(fdPath);
3645
+ const fdRealStat = await promises_.stat(fdRealPath);
3646
+ if (file_identity_sameFileIdentity(handleStat, fdRealStat)) {
3647
+ return fdRealPath;
3648
+ }
3649
+ }
3650
+ catch {
3651
+ // try next fd path
3652
+ }
3653
+ }
3654
+ try {
3655
+ const ioRealPath = await promises_.realpath(ioPath);
3656
+ const ioRealStat = await promises_.stat(ioRealPath);
3657
+ if (file_identity_sameFileIdentity(handleStat, ioRealStat)) {
3658
+ return ioRealPath;
3659
+ }
3660
+ }
3661
+ catch (err) {
3662
+ if (!path_isNotFoundPathError(err)) {
3663
+ throw err;
3664
+ }
3665
+ }
3666
+ const parentResolved = await resolveOpenedFileRealPathFromParent(handleStat, ioPath);
3667
+ if (parentResolved) {
3668
+ return parentResolved;
3669
+ }
3670
+ throw new errors_FsSafeError("path-mismatch", "unable to resolve opened file path");
3671
+ }
3672
+ async function resolveOpenedFileRealPathFromParent(handleStat, ioPath) {
3673
+ let parentReal;
3674
+ try {
3675
+ parentReal = await promises_.realpath(external_node_path_.dirname(ioPath));
3676
+ }
3677
+ catch (err) {
3678
+ if (path_isNotFoundPathError(err)) {
3679
+ return null;
3680
+ }
3681
+ throw err;
3682
+ }
3683
+ let entries;
3684
+ try {
3685
+ entries = await promises_.readdir(parentReal);
3686
+ }
3687
+ catch (err) {
3688
+ if (path_isNotFoundPathError(err)) {
3689
+ return null;
3690
+ }
3691
+ throw err;
3692
+ }
3693
+ for (const entry of entries.toSorted()) {
3694
+ const candidatePath = external_node_path_.join(parentReal, entry);
3695
+ try {
3696
+ const candidateStat = await promises_.lstat(candidatePath);
3697
+ if (candidateStat.isFile() && file_identity_sameFileIdentity(handleStat, candidateStat)) {
3698
+ return await promises_.realpath(candidatePath);
3699
+ }
3700
+ }
3701
+ catch (err) {
3702
+ if (!path_isNotFoundPathError(err)) {
3703
+ throw err;
3704
+ }
3705
+ }
3706
+ }
3707
+ return null;
3708
+ }
3709
+ async function openWritableFileInRoot(root, params) {
3710
+ const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
3711
+ try {
3712
+ await assertNoPathAliasEscape({
3713
+ absolutePath: resolved,
3714
+ rootPath: rootReal,
3715
+ boundaryLabel: "root",
3716
+ });
3717
+ }
3718
+ catch (err) {
3719
+ throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
3720
+ }
3721
+ if (params.mkdir !== false) {
3722
+ const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(rootReal, external_node_path_.dirname(resolved));
3723
+ await withAsyncDirectoryGuards([parentGuard], async () => {
3724
+ await promises_.mkdir(external_node_path_.dirname(resolved), { recursive: true });
3725
+ });
3726
+ }
3727
+ let ioPath = resolved;
3728
+ try {
3729
+ const resolvedRealPath = await promises_.realpath(resolved);
3730
+ if (!path_isPathInside(rootWithSep, resolvedRealPath)) {
3731
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
3732
+ }
3733
+ ioPath = resolvedRealPath;
3734
+ }
3735
+ catch (err) {
3736
+ if (err instanceof errors_FsSafeError) {
3737
+ throw err;
3738
+ }
3739
+ if (!path_isNotFoundPathError(err)) {
3740
+ throw err;
3741
+ }
3742
+ }
3743
+ const mode = params.mode ?? 0o600;
3744
+ let handle;
3745
+ let createdForWrite = false;
3746
+ const existingFlags = params.append ? OPEN_APPEND_EXISTING_FLAGS : OPEN_WRITE_EXISTING_FLAGS;
3747
+ const createFlags = params.append ? OPEN_APPEND_CREATE_FLAGS : OPEN_WRITE_CREATE_FLAGS;
3748
+ try {
3749
+ try {
3750
+ handle = await promises_.open(ioPath, existingFlags, mode);
3751
+ }
3752
+ catch (err) {
3753
+ if (!path_isNotFoundPathError(err)) {
3754
+ throw err;
3755
+ }
3756
+ handle = await promises_.open(ioPath, createFlags, mode);
3757
+ createdForWrite = true;
3758
+ }
3759
+ }
3760
+ catch (err) {
3761
+ if (path_isNotFoundPathError(err)) {
3762
+ throw new errors_FsSafeError("not-found", "file not found");
3763
+ }
3764
+ if (isSymlinkOpenError(err)) {
3765
+ throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
3766
+ }
3767
+ if (hasNodeErrorCode(err, "EISDIR")) {
3768
+ throw new errors_FsSafeError("not-file", "not a file", { cause: err });
3769
+ }
3770
+ throw err;
3771
+ }
3772
+ let realPathForCleanup = null;
3773
+ try {
3774
+ const stat = await handle.stat();
3775
+ if (!stat.isFile()) {
3776
+ throw new errors_FsSafeError("invalid-path", "path is not a regular file under root");
3777
+ }
3778
+ if (stat.nlink > 1) {
3779
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
3780
+ }
3781
+ try {
3782
+ const lstat = await promises_.lstat(ioPath);
3783
+ if (lstat.isSymbolicLink() || !lstat.isFile()) {
3784
+ throw new errors_FsSafeError(lstat.isSymbolicLink() ? "symlink" : "not-file", "path is not a regular file under root");
3785
+ }
3786
+ if (!file_identity_sameFileIdentity(stat, lstat)) {
3787
+ throw new errors_FsSafeError("path-mismatch", "path changed during write");
3788
+ }
3789
+ }
3790
+ catch (err) {
3791
+ if (!path_isNotFoundPathError(err)) {
3792
+ throw err;
3793
+ }
3794
+ }
3795
+ const realPath = await resolveOpenedFileRealPathForHandle(handle, ioPath);
3796
+ realPathForCleanup = realPath;
3797
+ const realStat = await promises_.stat(realPath);
3798
+ if (!file_identity_sameFileIdentity(stat, realStat)) {
3799
+ throw new errors_FsSafeError("path-mismatch", "path mismatch");
3800
+ }
3801
+ if (realStat.nlink > 1) {
3802
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
3803
+ }
3804
+ if (!path_isPathInside(rootWithSep, realPath)) {
3805
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
3806
+ }
3807
+ // Truncate only after boundary and identity checks complete. This avoids
3808
+ // irreversible side effects if a symlink target changes before validation.
3809
+ if (params.append !== true && params.truncateExisting !== false && !createdForWrite) {
3810
+ await handle.truncate(0);
3811
+ }
802
3812
  return {
803
- ok: false,
804
- reason: "invalid",
805
- detail
3813
+ handle,
3814
+ createdForWrite,
3815
+ realPath,
3816
+ stat,
3817
+ [Symbol.asyncDispose]: async () => {
3818
+ await closeHandleForDispose(handle);
3819
+ },
806
3820
  };
807
3821
  }
808
- const fieldLines = new Map();
809
- for(let i = 1; i < endIndex; i++){
810
- const match = lines[i]?.match(/^([^\s:][^:]*?):(?:\s|$)/);
811
- if (match?.[1]) {
812
- fieldLines.set(match[1], i + 1);
3822
+ catch (err) {
3823
+ const cleanupCreatedPath = createdForWrite && err instanceof errors_FsSafeError;
3824
+ const cleanupPath = realPathForCleanup ?? ioPath;
3825
+ await handle.close().catch(() => { });
3826
+ if (cleanupCreatedPath) {
3827
+ await promises_.rm(cleanupPath, { force: true }).catch(() => { });
813
3828
  }
3829
+ throw err;
814
3830
  }
815
- return {
816
- ok: true,
817
- data: {
818
- data: data ?? {},
819
- fieldLines,
820
- frontmatterStartLine: 1
3831
+ }
3832
+ async function appendFileInRoot(root, params) {
3833
+ const target = await openWritableFileInRoot(root, {
3834
+ relativePath: params.relativePath,
3835
+ mkdir: params.mkdir,
3836
+ mode: params.mode,
3837
+ truncateExisting: false,
3838
+ append: true,
3839
+ });
3840
+ try {
3841
+ let prefix = "";
3842
+ if (params.prependNewlineIfNeeded === true &&
3843
+ !target.createdForWrite &&
3844
+ target.stat.size > 0 &&
3845
+ ((typeof params.data === "string" && !params.data.startsWith("\n")) ||
3846
+ (Buffer.isBuffer(params.data) && params.data.length > 0 && params.data[0] !== 0x0a))) {
3847
+ const lastByte = Buffer.alloc(1);
3848
+ const { bytesRead } = await target.handle.read(lastByte, 0, 1, target.stat.size - 1);
3849
+ if (bytesRead === 1 && lastByte[0] !== 0x0a) {
3850
+ prefix = "\n";
3851
+ }
3852
+ }
3853
+ if (typeof params.data === "string") {
3854
+ await target.handle.appendFile(`${prefix}${params.data}`, params.encoding ?? "utf8");
3855
+ return;
3856
+ }
3857
+ const payload = prefix.length > 0 ? Buffer.concat([Buffer.from(prefix, "utf8"), params.data]) : params.data;
3858
+ await target.handle.appendFile(payload);
3859
+ }
3860
+ finally {
3861
+ await target.handle.close().catch(() => { });
3862
+ }
3863
+ }
3864
+ async function removePathInRoot(root, relativePath) {
3865
+ const resolved = await resolvePinnedRemovePathInRoot(root, relativePath);
3866
+ if (process.platform === "win32") {
3867
+ await removePathFallback(resolved);
3868
+ return;
3869
+ }
3870
+ try {
3871
+ await runPinnedPathHelper({
3872
+ operation: "remove",
3873
+ rootPath: resolved.rootReal,
3874
+ relativePath: resolved.relativePosix,
3875
+ });
3876
+ }
3877
+ catch (error) {
3878
+ if (isPinnedPathHelperSpawnError(error)) {
3879
+ await removePathFallback(resolved);
3880
+ return;
3881
+ }
3882
+ throw normalizePinnedPathError(error);
3883
+ }
3884
+ }
3885
+ async function mkdirPathInRoot(root, params) {
3886
+ const resolved = await resolvePinnedPathInRoot(root, params);
3887
+ if (process.platform === "win32") {
3888
+ await mkdirPathFallback(resolved);
3889
+ return;
3890
+ }
3891
+ try {
3892
+ await runPinnedPathHelper({
3893
+ operation: "mkdirp",
3894
+ rootPath: resolved.rootReal,
3895
+ relativePath: resolved.relativePosix,
3896
+ });
3897
+ }
3898
+ catch (error) {
3899
+ if (isPinnedPathHelperSpawnError(error)) {
3900
+ await mkdirPathFallback(resolved);
3901
+ return;
3902
+ }
3903
+ throw normalizePinnedPathError(error);
3904
+ }
3905
+ }
3906
+ async function writeFileInRoot(root, params) {
3907
+ if (process.platform === "win32") {
3908
+ await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
3909
+ await writeFileFallback(root, params);
3910
+ });
3911
+ return;
3912
+ }
3913
+ const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
3914
+ await serializePathWrite(pinned.targetPath, async () => {
3915
+ let identity;
3916
+ try {
3917
+ identity = await runPinnedWriteHelper({
3918
+ rootPath: pinned.rootReal,
3919
+ relativeParentPath: pinned.relativeParentPath,
3920
+ basename: pinned.basename,
3921
+ mkdir: params.mkdir !== false,
3922
+ mode: params.mode ?? pinned.mode,
3923
+ overwrite: params.overwrite,
3924
+ input: {
3925
+ kind: "buffer",
3926
+ data: params.data,
3927
+ encoding: params.encoding,
3928
+ },
3929
+ });
3930
+ }
3931
+ catch (error) {
3932
+ if (params.overwrite === false && isAlreadyExistsError(error)) {
3933
+ throw new errors_FsSafeError("already-exists", "file already exists", {
3934
+ cause: error instanceof Error ? error : undefined,
3935
+ });
3936
+ }
3937
+ throw normalizePinnedWriteError(error);
3938
+ }
3939
+ try {
3940
+ await verifyAtomicWriteResult({
3941
+ root,
3942
+ targetPath: pinned.targetPath,
3943
+ expectedIdentity: identity,
3944
+ });
3945
+ }
3946
+ catch (err) {
3947
+ emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
3948
+ throw err;
3949
+ }
3950
+ });
3951
+ }
3952
+ async function copyFileInRoot(root, params) {
3953
+ assertValidRootRelativePath(params.relativePath);
3954
+ path_assertNoNulPathInput(params.sourcePath, "source path contains a NUL byte");
3955
+ const source = await openVerifiedLocalFile(params.sourcePath, {
3956
+ hardlinks: params.sourceHardlinks,
3957
+ });
3958
+ if (params.maxBytes !== undefined && source.stat.size > params.maxBytes) {
3959
+ await source.handle.close().catch(() => { });
3960
+ throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
3961
+ }
3962
+ try {
3963
+ if (process.platform === "win32") {
3964
+ await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
3965
+ await copyFileFallback(root, params, source);
3966
+ });
3967
+ return;
3968
+ }
3969
+ const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
3970
+ await serializePathWrite(pinned.targetPath, async () => {
3971
+ let identity;
3972
+ try {
3973
+ if (getFsSafePythonConfig().mode === "off") {
3974
+ await copyFileFallback(root, params, source);
3975
+ return;
3976
+ }
3977
+ identity = await runPinnedCopyHelper({
3978
+ rootPath: pinned.rootReal,
3979
+ relativeParentPath: pinned.relativeParentPath,
3980
+ basename: pinned.basename,
3981
+ mkdir: params.mkdir !== false,
3982
+ mode: pinned.mode,
3983
+ overwrite: true,
3984
+ maxBytes: params.maxBytes,
3985
+ sourcePath: source.realPath,
3986
+ sourceIdentity: { dev: source.stat.dev, ino: source.stat.ino },
3987
+ });
3988
+ }
3989
+ catch (error) {
3990
+ if (canFallbackFromPythonError(error)) {
3991
+ await copyFileFallback(root, params, source);
3992
+ return;
3993
+ }
3994
+ throw normalizePinnedWriteError(error);
3995
+ }
3996
+ try {
3997
+ await verifyAtomicWriteResult({
3998
+ root,
3999
+ targetPath: pinned.targetPath,
4000
+ expectedIdentity: identity,
4001
+ });
4002
+ }
4003
+ catch (err) {
4004
+ emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
4005
+ throw err;
4006
+ }
4007
+ });
4008
+ }
4009
+ finally {
4010
+ await source.handle.close().catch(() => { });
4011
+ }
4012
+ }
4013
+ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode) {
4014
+ const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath);
4015
+ try {
4016
+ await assertNoPathAliasEscape({
4017
+ absolutePath: resolved,
4018
+ rootPath: rootReal,
4019
+ boundaryLabel: "root",
4020
+ });
4021
+ }
4022
+ catch (err) {
4023
+ throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
4024
+ }
4025
+ // resolvePathInRoot already enforces isPathInside, so any actual escape
4026
+ // is rejected upstream.
4027
+ const relativeResolved = external_node_path_.relative(rootReal, resolved);
4028
+ if (external_node_path_.isAbsolute(relativeResolved)) {
4029
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
4030
+ }
4031
+ const relativePosix = relativeResolved
4032
+ ? relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep)
4033
+ : "";
4034
+ const basename = external_node_path_.posix.basename(relativePosix);
4035
+ if (!basename || basename === "." || basename === "/") {
4036
+ throw new errors_FsSafeError("invalid-path", "invalid target path");
4037
+ }
4038
+ let mode = requestedMode ?? 0o600;
4039
+ try {
4040
+ const opened = await openFileInRoot(root, {
4041
+ relativePath,
4042
+ hardlinks: "reject",
4043
+ nonBlockingRead: true,
4044
+ });
4045
+ try {
4046
+ mode = requestedMode ?? (opened.stat.mode & 0o777);
4047
+ if (!path_isPathInside(rootWithSep, opened.realPath)) {
4048
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
4049
+ }
4050
+ }
4051
+ finally {
4052
+ await opened.handle.close().catch(() => { });
821
4053
  }
4054
+ }
4055
+ catch (err) {
4056
+ if (!(err instanceof errors_FsSafeError) || err.code !== "not-found") {
4057
+ throw err;
4058
+ }
4059
+ }
4060
+ return {
4061
+ rootReal,
4062
+ targetPath: resolved,
4063
+ relativeParentPath: external_node_path_.posix.dirname(relativePosix) === "." ? "" : external_node_path_.posix.dirname(relativePosix),
4064
+ basename,
4065
+ mode: mode || 0o600,
822
4066
  };
823
4067
  }
824
- /**
825
- * Parses YAML frontmatter from markdown content.
826
- *
827
- * Returns `null` when the content has no opening `---`, no closing `---`,
828
- * or contains invalid YAML.
829
- */ function parseFrontmatter(content) {
830
- const result = parseFrontmatterWithError(content);
831
- return result.ok ? result.data : null;
4068
+ async function resolvePinnedPathInRoot(root, params) {
4069
+ return await resolvePinnedOperationPathInRoot(root, {
4070
+ allowRoot: params.allowRoot,
4071
+ relativePath: params.relativePath,
4072
+ policy: PATH_ALIAS_POLICIES.strict,
4073
+ });
4074
+ }
4075
+ async function resolvePinnedRemovePathInRoot(root, relativePath) {
4076
+ return await resolvePinnedOperationPathInRoot(root, {
4077
+ relativePath,
4078
+ policy: PATH_ALIAS_POLICIES.unlinkTarget,
4079
+ });
4080
+ }
4081
+ async function resolvePinnedOperationPathInRoot(root, params) {
4082
+ const resolved = await resolvePinnedRootPathInRoot(root, {
4083
+ relativePath: params.relativePath,
4084
+ policy: params.policy,
4085
+ });
4086
+ const relativeResolved = external_node_path_.relative(resolved.rootReal, resolved.canonicalPath);
4087
+ if ((relativeResolved === "" || relativeResolved === ".") && params.allowRoot === true) {
4088
+ return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix: "" };
4089
+ }
4090
+ const firstSegment = relativeResolved.split(external_node_path_.sep)[0];
4091
+ if (relativeResolved === "" ||
4092
+ relativeResolved === "." ||
4093
+ firstSegment === ".." ||
4094
+ external_node_path_.isAbsolute(relativeResolved)) {
4095
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
4096
+ }
4097
+ const relativePosix = relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep);
4098
+ if (!path_isPathInside(resolved.rootWithSep, resolved.canonicalPath)) {
4099
+ throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
4100
+ }
4101
+ return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix };
4102
+ }
4103
+ async function resolvePinnedRootPathInRoot(root, params) {
4104
+ const rootReal = root.rootReal;
4105
+ let resolved;
4106
+ try {
4107
+ resolved = await resolveRootPath({
4108
+ absolutePath: external_node_path_.resolve(rootReal, await expandRelativePathWithHome(params.relativePath)),
4109
+ rootPath: rootReal,
4110
+ rootCanonicalPath: rootReal,
4111
+ boundaryLabel: "root",
4112
+ policy: params.policy,
4113
+ });
4114
+ }
4115
+ catch (err) {
4116
+ throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
4117
+ }
4118
+ const rootWithSep = ensureTrailingSep(resolved.rootCanonicalPath);
4119
+ return {
4120
+ rootReal: resolved.rootCanonicalPath,
4121
+ rootWithSep,
4122
+ canonicalPath: resolved.canonicalPath,
4123
+ };
4124
+ }
4125
+ async function removePathFallback(resolved) {
4126
+ const guard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(resolved.resolved));
4127
+ await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("remove", resolved.resolved);
4128
+ await assertAsyncDirectoryGuard(guard);
4129
+ await ((await promises_.lstat(resolved.resolved)).isDirectory() ? promises_.rmdir(resolved.resolved) : promises_.rm(resolved.resolved));
4130
+ await assertAsyncDirectoryGuard(guard).catch(() => undefined);
4131
+ }
4132
+ async function mkdirPathFallback(resolved) {
4133
+ await mkdirPathComponentsWithGuards({
4134
+ rootReal: resolved.rootReal, targetPath: resolved.resolved,
4135
+ beforeComponent: async (componentPath) => await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("mkdir", componentPath),
4136
+ });
4137
+ }
4138
+ async function statPathFallback(root, relativePath) {
4139
+ const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
4140
+ try {
4141
+ return pathStatFromStats(await promises_.lstat(resolved.resolved));
4142
+ }
4143
+ catch (error) {
4144
+ if (path_isNotFoundPathError(error)) {
4145
+ throw new errors_FsSafeError("not-found", "file not found", {
4146
+ cause: error instanceof Error ? error : undefined,
4147
+ });
4148
+ }
4149
+ throw error;
4150
+ }
4151
+ }
4152
+ async function listPathFallback(root, relativePath, withFileTypes) {
4153
+ const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
4154
+ try {
4155
+ const names = await promises_.readdir(resolved.resolved);
4156
+ const sortedNames = names.toSorted();
4157
+ if (!withFileTypes) {
4158
+ return sortedNames;
4159
+ }
4160
+ const entries = [];
4161
+ for (const name of sortedNames) {
4162
+ entries.push({
4163
+ name,
4164
+ ...pathStatFromStats(await promises_.lstat(external_node_path_.join(resolved.resolved, name))),
4165
+ });
4166
+ }
4167
+ return entries;
4168
+ }
4169
+ catch (error) {
4170
+ if (path_isNotFoundPathError(error)) {
4171
+ throw new errors_FsSafeError("not-found", "directory not found", {
4172
+ cause: error instanceof Error ? error : undefined,
4173
+ });
4174
+ }
4175
+ throw error;
4176
+ }
4177
+ }
4178
+ async function movePathFallback(root, params) {
4179
+ const source = await resolvePathInRoot(root, params.fromRelative);
4180
+ await resolvePinnedRootPathInRoot(root, {
4181
+ relativePath: params.fromRelative,
4182
+ policy: PATH_ALIAS_POLICIES.strict,
4183
+ });
4184
+ const target = await resolvePathInRoot(root, params.toRelative);
4185
+ await resolvePinnedRootPathInRoot(root, {
4186
+ relativePath: params.toRelative,
4187
+ policy: PATH_ALIAS_POLICIES.unlinkTarget,
4188
+ });
4189
+ try {
4190
+ await assertNoPathAliasEscape({
4191
+ absolutePath: target.resolved,
4192
+ rootPath: target.rootReal,
4193
+ boundaryLabel: "root",
4194
+ });
4195
+ }
4196
+ catch (error) {
4197
+ throw new errors_FsSafeError("path-alias", "path alias escape blocked", {
4198
+ cause: error instanceof Error ? error : undefined,
4199
+ });
4200
+ }
4201
+ let sourceStat;
4202
+ try {
4203
+ sourceStat = await promises_.lstat(source.resolved);
4204
+ }
4205
+ catch (error) {
4206
+ if (path_isNotFoundPathError(error)) {
4207
+ throw new errors_FsSafeError("not-found", "file not found", {
4208
+ cause: error instanceof Error ? error : undefined,
4209
+ });
4210
+ }
4211
+ throw error;
4212
+ }
4213
+ if (sourceStat.isSymbolicLink()) {
4214
+ throw new errors_FsSafeError("symlink", "symlink not allowed");
4215
+ }
4216
+ if (sourceStat.isFile() && sourceStat.nlink > 1) {
4217
+ throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
4218
+ }
4219
+ if (!params.overwrite) {
4220
+ try {
4221
+ await promises_.lstat(target.resolved);
4222
+ throw new errors_FsSafeError("already-exists", "destination exists");
4223
+ }
4224
+ catch (error) {
4225
+ if (error instanceof errors_FsSafeError) {
4226
+ throw error;
4227
+ }
4228
+ if (!path_isNotFoundPathError(error)) {
4229
+ throw error;
4230
+ }
4231
+ }
4232
+ }
4233
+ const sourceParentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(source.resolved));
4234
+ const targetParentGuard = await directory_guard_createNearestExistingDirectoryGuard(target.rootReal, external_node_path_.dirname(target.resolved));
4235
+ await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("move", target.resolved);
4236
+ await assertAsyncDirectoryGuard(sourceParentGuard);
4237
+ await assertAsyncDirectoryGuard(targetParentGuard);
4238
+ try {
4239
+ await promises_.rename(source.resolved, target.resolved);
4240
+ }
4241
+ catch (error) {
4242
+ if (path_isNotFoundPathError(error)) {
4243
+ throw new errors_FsSafeError("not-found", "file not found", {
4244
+ cause: error instanceof Error ? error : undefined,
4245
+ });
4246
+ }
4247
+ if (hasNodeErrorCode(error, "EEXIST")) {
4248
+ throw new errors_FsSafeError("already-exists", "destination exists", {
4249
+ cause: error instanceof Error ? error : undefined,
4250
+ });
4251
+ }
4252
+ throw error;
4253
+ }
4254
+ await assertAsyncDirectoryGuard(targetParentGuard).catch(() => undefined);
4255
+ }
4256
+ async function writeFileFallback(root, params) {
4257
+ if (params.overwrite === false) {
4258
+ await writeMissingFileFallback(root, params);
4259
+ return;
4260
+ }
4261
+ const target = await openWritableFileInRoot(root, {
4262
+ relativePath: params.relativePath,
4263
+ mkdir: params.mkdir,
4264
+ mode: params.mode,
4265
+ truncateExisting: false,
4266
+ });
4267
+ const destinationPath = target.realPath;
4268
+ const mode = params.mode ?? (target.stat.mode & 0o777);
4269
+ await target.handle.close().catch(() => { });
4270
+ const destinationGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(destinationPath));
4271
+ let tempPath = null;
4272
+ let unregisterTempPath = null;
4273
+ try {
4274
+ tempPath = buildAtomicWriteTempPath(destinationPath);
4275
+ unregisterTempPath = registerTempPathForExit(tempPath);
4276
+ const writtenStat = await writeTempFileForAtomicReplace({
4277
+ tempPath,
4278
+ data: params.data,
4279
+ encoding: params.encoding,
4280
+ mode: mode || 0o600,
4281
+ });
4282
+ const commitTempPath = tempPath;
4283
+ await withAsyncDirectoryGuards([destinationGuard], async () => {
4284
+ await promises_.rename(commitTempPath, destinationPath);
4285
+ });
4286
+ tempPath = null;
4287
+ unregisterTempPath();
4288
+ unregisterTempPath = null;
4289
+ try {
4290
+ await verifyAtomicWriteResult({
4291
+ root,
4292
+ targetPath: destinationPath,
4293
+ expectedIdentity: writtenStat,
4294
+ });
4295
+ }
4296
+ catch (err) {
4297
+ emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
4298
+ throw err;
4299
+ }
4300
+ }
4301
+ finally {
4302
+ if (tempPath) {
4303
+ await promises_.rm(tempPath, { force: true }).catch(() => { });
4304
+ }
4305
+ unregisterTempPath?.();
4306
+ }
4307
+ }
4308
+ async function writeMissingFileFallback(root, params) {
4309
+ const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath);
4310
+ try {
4311
+ await assertNoPathAliasEscape({
4312
+ absolutePath: resolved,
4313
+ rootPath: rootReal,
4314
+ boundaryLabel: "root",
4315
+ });
4316
+ }
4317
+ catch (err) {
4318
+ throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
4319
+ }
4320
+ if (params.mkdir !== false) {
4321
+ await promises_.mkdir(external_node_path_.dirname(resolved), { recursive: true });
4322
+ }
4323
+ const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(resolved));
4324
+ let created = false;
4325
+ try {
4326
+ const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
4327
+ const handle = await promises_.open(resolved, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
4328
+ created = true;
4329
+ try {
4330
+ if (typeof params.data === "string") {
4331
+ await handle.writeFile(params.data, params.encoding ?? "utf8");
4332
+ }
4333
+ else {
4334
+ await handle.writeFile(params.data);
4335
+ }
4336
+ return { handle, writtenStat: await handle.stat() };
4337
+ }
4338
+ catch (error) {
4339
+ await handle.close().catch(() => undefined);
4340
+ throw error;
4341
+ }
4342
+ }, {
4343
+ onPostGuardFailure: async ({ handle }) => {
4344
+ created = false; // Parent is untrusted now; skip outer path cleanup by name.
4345
+ await handle.close().catch(() => undefined);
4346
+ },
4347
+ });
4348
+ await handle.close();
4349
+ await verifyAtomicWriteResult({
4350
+ root,
4351
+ targetPath: resolved,
4352
+ expectedIdentity: writtenStat,
4353
+ });
4354
+ created = false;
4355
+ }
4356
+ catch (err) {
4357
+ if (hasNodeErrorCode(err, "EEXIST")) {
4358
+ throw new errors_FsSafeError("already-exists", "file already exists", {
4359
+ cause: err instanceof Error ? err : undefined,
4360
+ });
4361
+ }
4362
+ throw err;
4363
+ }
4364
+ finally {
4365
+ if (created) {
4366
+ await promises_.rm(resolved, { force: true }).catch(() => undefined);
4367
+ }
4368
+ }
4369
+ }
4370
+ async function copyFileFallback(root, params, source) {
4371
+ let target = null;
4372
+ let sourceClosedByStream = false;
4373
+ let targetClosedByUs = false;
4374
+ let tempHandle = null;
4375
+ let tempPath = null;
4376
+ let unregisterTempPath = null;
4377
+ let tempClosedByStream = false;
4378
+ try {
4379
+ target = await openWritableFileInRoot(root, {
4380
+ relativePath: params.relativePath,
4381
+ mkdir: params.mkdir,
4382
+ mode: params.mode,
4383
+ truncateExisting: false,
4384
+ });
4385
+ const destinationPath = target.realPath;
4386
+ const mode = params.mode ?? (target.stat.mode & 0o777);
4387
+ await target.handle.close().catch(() => { });
4388
+ targetClosedByUs = true;
4389
+ const destinationGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(destinationPath));
4390
+ tempPath = buildAtomicWriteTempPath(destinationPath);
4391
+ unregisterTempPath = registerTempPathForExit(tempPath);
4392
+ tempHandle = await promises_.open(tempPath, OPEN_WRITE_CREATE_FLAGS, mode || 0o600);
4393
+ const sourceStream = createBoundedReadStream(source, params.maxBytes);
4394
+ const targetStream = tempHandle.createWriteStream();
4395
+ sourceStream.once("close", () => {
4396
+ sourceClosedByStream = true;
4397
+ });
4398
+ targetStream.once("close", () => {
4399
+ tempClosedByStream = true;
4400
+ });
4401
+ await (0,external_node_stream_promises_.pipeline)(sourceStream, targetStream);
4402
+ const writtenStat = await promises_.stat(tempPath);
4403
+ if (!tempClosedByStream) {
4404
+ await tempHandle.close().catch(() => { });
4405
+ tempClosedByStream = true;
4406
+ }
4407
+ tempHandle = null;
4408
+ const commitTempPath = tempPath;
4409
+ await withAsyncDirectoryGuards([destinationGuard], async () => {
4410
+ await promises_.rename(commitTempPath, destinationPath);
4411
+ });
4412
+ tempPath = null;
4413
+ unregisterTempPath();
4414
+ unregisterTempPath = null;
4415
+ try {
4416
+ await verifyAtomicWriteResult({
4417
+ root,
4418
+ targetPath: destinationPath,
4419
+ expectedIdentity: writtenStat,
4420
+ });
4421
+ }
4422
+ catch (err) {
4423
+ emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
4424
+ throw err;
4425
+ }
4426
+ }
4427
+ catch (err) {
4428
+ if (target?.createdForWrite) {
4429
+ await promises_.rm(target.realPath, { force: true }).catch(() => { });
4430
+ }
4431
+ throw err;
4432
+ }
4433
+ finally {
4434
+ if (!sourceClosedByStream) {
4435
+ await source.handle.close().catch(() => { });
4436
+ }
4437
+ if (tempHandle && !tempClosedByStream) {
4438
+ await tempHandle.close().catch(() => { });
4439
+ }
4440
+ if (tempPath) {
4441
+ await promises_.rm(tempPath, { force: true }).catch(() => { });
4442
+ }
4443
+ unregisterTempPath?.();
4444
+ if (target && !targetClosedByUs) {
4445
+ await target.handle.close().catch(() => { });
4446
+ }
4447
+ }
832
4448
  }
833
4449
 
834
- // EXTERNAL MODULE: external "node:fs"
835
- var external_node_fs_ = __webpack_require__(3024);
836
4450
  ;// CONCATENATED MODULE: ../core/src/gateways/file-system-utils.ts
837
4451
  /**
838
4452
  * Shared file system utilities for gateways.
839
4453
  */
840
4454
 
841
4455
 
4456
+
842
4457
  /**
843
4458
  * Checks if a file or directory exists.
844
4459
  */ async function fileExists(path) {
845
4460
  try {
846
- await (0,promises_.access)(path);
4461
+ await access(path);
847
4462
  return true;
848
4463
  } catch {
849
4464
  return false;
@@ -852,9 +4467,92 @@ var external_node_fs_ = __webpack_require__(3024);
852
4467
  function isPathWithinRoot(rootPath, candidatePath) {
853
4468
  return candidatePath === rootPath || candidatePath.startsWith(`${rootPath}${external_node_path_.sep}`);
854
4469
  }
4470
+ function mapFsSafeError(error, relativePath) {
4471
+ if (error instanceof errors_FsSafeError) {
4472
+ switch(error.code){
4473
+ case "outside-workspace":
4474
+ case "invalid-path":
4475
+ throw new Error(`Resolved path is outside target directory: ${relativePath}`, {
4476
+ cause: error
4477
+ });
4478
+ case "path-alias":
4479
+ case "symlink":
4480
+ throw new Error(`Symlinks are not allowed: path contains symbolic link: ${relativePath}`, {
4481
+ cause: error
4482
+ });
4483
+ case "too-large":
4484
+ throw new Error(`File ${relativePath} exceeds size limit: ${error.message}`, {
4485
+ cause: error
4486
+ });
4487
+ default:
4488
+ throw error;
4489
+ }
4490
+ }
4491
+ throw error;
4492
+ }
4493
+ async function createSafeRoot(targetDir, maxBytes) {
4494
+ return root_impl_root(targetDir, {
4495
+ hardlinks: "reject",
4496
+ maxBytes,
4497
+ symlinks: "reject"
4498
+ });
4499
+ }
4500
+ async function readTextWithinRoot(targetDir, relativePath, maxBytes) {
4501
+ try {
4502
+ const safeRoot = await createSafeRoot(targetDir, maxBytes);
4503
+ return await safeRoot.readText(relativePath, {
4504
+ maxBytes
4505
+ });
4506
+ } catch (error) {
4507
+ mapFsSafeError(error, relativePath);
4508
+ }
4509
+ }
4510
+ async function listDirectoryWithinRoot(targetDir, relativePath) {
4511
+ try {
4512
+ const safeRoot = await createSafeRoot(targetDir);
4513
+ const entries = await safeRoot.list(relativePath, {
4514
+ withFileTypes: true
4515
+ });
4516
+ return entries.map(toSafeDirEntry);
4517
+ } catch (error) {
4518
+ mapFsSafeError(error, relativePath);
4519
+ }
4520
+ }
4521
+ function toSafeDirEntry(entry) {
4522
+ return {
4523
+ name: entry.name,
4524
+ isDirectory: ()=>entry.isDirectory,
4525
+ isFile: ()=>entry.isFile,
4526
+ isSymbolicLink: ()=>entry.isSymbolicLink
4527
+ };
4528
+ }
4529
+ async function pathExistsWithinRoot(targetDir, relativePath) {
4530
+ try {
4531
+ const safeRoot = await createSafeRoot(targetDir);
4532
+ return await safeRoot.exists(relativePath);
4533
+ } catch (error) {
4534
+ mapFsSafeError(error, relativePath);
4535
+ }
4536
+ }
4537
+ /**
4538
+ * Returns true if the error originated from an fs-safe security check
4539
+ * (symlink, traversal, or size-limit violation). These errors carry a
4540
+ * FsSafeError as their `cause` and should be re-thrown rather than
4541
+ * silently swallowed by per-file error handlers.
4542
+ */ function isFsSafeViolation(error) {
4543
+ return error instanceof Error && error.cause instanceof FsSafeError;
4544
+ }
4545
+ async function statWithinRoot(targetDir, relativePath) {
4546
+ try {
4547
+ const safeRoot = await createSafeRoot(targetDir);
4548
+ return await safeRoot.stat(relativePath);
4549
+ } catch (error) {
4550
+ mapFsSafeError(error, relativePath);
4551
+ }
4552
+ }
855
4553
  /**
856
4554
  * Resolves a relative path under targetDir and rejects traversal outside the root.
857
- */ async function resolvePathWithinRoot(targetDir, relativePath) {
4555
+ */ async function file_system_utils_resolvePathWithinRoot(targetDir, relativePath) {
858
4556
  if (!relativePath) {
859
4557
  throw new Error("Path must not be empty");
860
4558
  }
@@ -896,7 +4594,7 @@ function isPathWithinRoot(rootPath, candidatePath) {
896
4594
  /**
897
4595
  * Resolves a relative path under targetDir and validates it does not pass through symlinks.
898
4596
  */ async function resolveSafePath(targetDir, relativePath) {
899
- const resolvedPath = await resolvePathWithinRoot(targetDir, relativePath);
4597
+ const resolvedPath = await file_system_utils_resolvePathWithinRoot(targetDir, relativePath);
900
4598
  await assertPathHasNoSymbolicLinks(targetDir, resolvedPath);
901
4599
  return resolvedPath;
902
4600
  }
@@ -954,38 +4652,31 @@ function isPathWithinRoot(rootPath, candidatePath) {
954
4652
  */
955
4653
 
956
4654
 
957
-
958
4655
  /** Maximum agent file size: 1 MB */ const MAX_AGENT_FILE_BYTES = 1_048_576;
959
4656
  /**
960
4657
  * File system implementation of the agent lint gateway.
961
4658
  */ class FileSystemAgentLintGateway {
962
4659
  async discoverAgents(targetDir) {
963
- const agentsDir = (0,external_node_path_.join)(targetDir, ".github", "agents");
964
- let agentsDirStats;
4660
+ const agentsDir = (0,external_node_path_.join)(".github", "agents");
965
4661
  try {
966
- agentsDirStats = await (0,promises_.lstat)(agentsDir);
967
- } catch (error) {
968
- if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
4662
+ if (!await pathExistsWithinRoot(targetDir, agentsDir)) {
969
4663
  return [];
970
4664
  }
971
- throw error;
972
- }
973
- if (agentsDirStats.isSymbolicLink() || !agentsDirStats.isDirectory()) {
4665
+ } catch {
974
4666
  return [];
975
4667
  }
976
- const resolvedAgentsDir = (0,external_node_path_.resolve)(agentsDir);
4668
+ const resolvedAgentsDir = (0,external_node_path_.resolve)(targetDir, agentsDir);
977
4669
  const agents = [];
978
4670
  const walk = async (dir)=>{
979
- const entries = await (0,promises_.readdir)(dir, {
980
- withFileTypes: true
981
- });
4671
+ const entries = await listDirectoryWithinRoot(targetDir, dir);
982
4672
  for (const entry of entries){
983
4673
  const name = entry.name;
984
4674
  if (name.includes("..") || name.includes("/") || name.includes("\\")) {
985
4675
  continue;
986
4676
  }
987
4677
  const entryPath = (0,external_node_path_.join)(dir, name);
988
- const resolvedPath = (0,external_node_path_.resolve)(entryPath);
4678
+ const absoluteEntryPath = (0,external_node_path_.join)(targetDir, entryPath);
4679
+ const resolvedPath = (0,external_node_path_.resolve)(absoluteEntryPath);
989
4680
  const rel = (0,external_node_path_.relative)(resolvedAgentsDir, resolvedPath);
990
4681
  if (rel.startsWith("..") || rel.startsWith(external_node_path_.sep)) {
991
4682
  continue;
@@ -1005,7 +4696,7 @@ function isPathWithinRoot(rootPath, candidatePath) {
1005
4696
  }
1006
4697
  const agentName = name.endsWith(".agent.md") ? (0,external_node_path_.basename)(name, ".agent.md") : (0,external_node_path_.basename)(name, ".md");
1007
4698
  agents.push({
1008
- filePath: entryPath,
4699
+ filePath: absoluteEntryPath,
1009
4700
  agentName
1010
4701
  });
1011
4702
  }
@@ -1119,53 +4810,57 @@ function isPathWithinRoot(rootPath, candidatePath) {
1119
4810
  * Supports GitHub Copilot, Claude Code, and community agent instruction formats.
1120
4811
  */
1121
4812
 
1122
-
1123
4813
  /**
1124
4814
  * File system implementation of the instruction file discovery gateway.
1125
4815
  */ class FileSystemInstructionFileDiscoveryGateway {
1126
4816
  async discoverInstructionFiles(targetDir) {
1127
4817
  const files = [];
1128
4818
  // .github/copilot-instructions.md
1129
- const copilotInstructions = (0,external_node_path_.join)(targetDir, ".github", "copilot-instructions.md");
1130
- if (await fileExists(copilotInstructions) && !await this.isSymlink(copilotInstructions)) {
4819
+ const copilotInstructions = (0,external_node_path_.join)(".github", "copilot-instructions.md");
4820
+ if (await this.safePathExists(targetDir, copilotInstructions)) {
1131
4821
  files.push({
1132
- filePath: copilotInstructions,
4822
+ filePath: (0,external_node_path_.join)(targetDir, copilotInstructions),
1133
4823
  format: "copilot-instructions"
1134
4824
  });
1135
4825
  }
1136
4826
  // .github/instructions/*.md
1137
- const instructionsDir = (0,external_node_path_.join)(targetDir, ".github", "instructions");
1138
- await this.discoverMdFilesInDir(instructionsDir, "copilot-scoped", files);
4827
+ const instructionsDir = (0,external_node_path_.join)(".github", "instructions");
4828
+ await this.discoverMdFilesInDir(targetDir, instructionsDir, "copilot-scoped", files);
1139
4829
  // .github/agents/*.md
1140
- const agentsDir = (0,external_node_path_.join)(targetDir, ".github", "agents");
1141
- await this.discoverMdFilesInDir(agentsDir, "copilot-agent", files);
4830
+ const agentsDir = (0,external_node_path_.join)(".github", "agents");
4831
+ await this.discoverMdFilesInDir(targetDir, agentsDir, "copilot-agent", files);
1142
4832
  // AGENTS.md at repo root
1143
- const agentsMd = (0,external_node_path_.join)(targetDir, "AGENTS.md");
1144
- if (await fileExists(agentsMd) && !await this.isSymlink(agentsMd)) {
4833
+ const agentsMd = "AGENTS.md";
4834
+ if (await this.safePathExists(targetDir, agentsMd)) {
1145
4835
  files.push({
1146
- filePath: agentsMd,
4836
+ filePath: (0,external_node_path_.join)(targetDir, agentsMd),
1147
4837
  format: "agents-md"
1148
4838
  });
1149
4839
  }
1150
4840
  // CLAUDE.md at repo root
1151
- const claudeMd = (0,external_node_path_.join)(targetDir, "CLAUDE.md");
1152
- if (await fileExists(claudeMd) && !await this.isSymlink(claudeMd)) {
4841
+ const claudeMd = "CLAUDE.md";
4842
+ if (await this.safePathExists(targetDir, claudeMd)) {
1153
4843
  files.push({
1154
- filePath: claudeMd,
4844
+ filePath: (0,external_node_path_.join)(targetDir, claudeMd),
1155
4845
  format: "claude-md"
1156
4846
  });
1157
4847
  }
1158
4848
  return files;
1159
4849
  }
1160
- async discoverMdFilesInDir(dirPath, format, files) {
1161
- if (!await fileExists(dirPath)) {
4850
+ async safePathExists(targetDir, relativePath) {
4851
+ try {
4852
+ return await pathExistsWithinRoot(targetDir, relativePath);
4853
+ } catch {
4854
+ return false;
4855
+ }
4856
+ }
4857
+ async discoverMdFilesInDir(targetDir, dirPath, format, files) {
4858
+ if (!await this.safePathExists(targetDir, dirPath)) {
1162
4859
  return;
1163
4860
  }
1164
4861
  try {
1165
- const entries = await (0,promises_.readdir)(dirPath, {
1166
- withFileTypes: true
1167
- });
1168
- const resolvedDirPath = (0,external_node_path_.resolve)(dirPath);
4862
+ const entries = await listDirectoryWithinRoot(targetDir, dirPath);
4863
+ const resolvedDirPath = (0,external_node_path_.resolve)(targetDir, dirPath);
1169
4864
  for (const entry of entries){
1170
4865
  if (entry.isSymbolicLink()) {
1171
4866
  continue;
@@ -1178,7 +4873,7 @@ function isPathWithinRoot(rootPath, candidatePath) {
1178
4873
  continue;
1179
4874
  }
1180
4875
  if (name.endsWith(".md")) {
1181
- const filePath = (0,external_node_path_.join)(dirPath, name);
4876
+ const filePath = (0,external_node_path_.join)(targetDir, dirPath, name);
1182
4877
  const resolvedFilePath = (0,external_node_path_.resolve)(filePath);
1183
4878
  const rel = (0,external_node_path_.relative)(resolvedDirPath, resolvedFilePath);
1184
4879
  if (rel.startsWith("..") || rel.startsWith(external_node_path_.sep)) {
@@ -1194,14 +4889,6 @@ function isPathWithinRoot(rootPath, candidatePath) {
1194
4889
  // Skip directory if we can't read it
1195
4890
  }
1196
4891
  }
1197
- async isSymlink(filePath) {
1198
- try {
1199
- const stats = await (0,promises_.lstat)(filePath);
1200
- return stats.isSymbolicLink();
1201
- } catch {
1202
- return false;
1203
- }
1204
- }
1205
4892
  }
1206
4893
  /**
1207
4894
  * Creates and returns the default instruction file discovery gateway.
@@ -29620,7 +33307,6 @@ const PackageJsonSchema = schemas_object({
29620
33307
  */
29621
33308
 
29622
33309
 
29623
-
29624
33310
  /** Maximum skill file size: 1 MB */ const MAX_SKILL_FILE_BYTES = 1_048_576;
29625
33311
  /**
29626
33312
  * Skill directory locations to search for SKILL.md files.
@@ -29635,30 +33321,22 @@ const PackageJsonSchema = schemas_object({
29635
33321
  async discoverSkills(targetDir) {
29636
33322
  const skills = [];
29637
33323
  for (const relativeDir of SKILL_DIRECTORIES){
29638
- const skillsDir = (0,external_node_path_.join)(targetDir, relativeDir);
29639
- const discovered = await this.discoverSkillsInDir(skillsDir);
33324
+ const discovered = await this.discoverSkillsInDir(targetDir, relativeDir);
29640
33325
  skills.push(...discovered);
29641
33326
  }
29642
33327
  return skills;
29643
33328
  }
29644
- async discoverSkillsInDir(skillsDir) {
29645
- let dirStats;
33329
+ async discoverSkillsInDir(targetDir, skillsDir) {
29646
33330
  try {
29647
- dirStats = await (0,promises_.lstat)(skillsDir);
29648
- } catch (error) {
29649
- if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
33331
+ if (!await pathExistsWithinRoot(targetDir, skillsDir)) {
29650
33332
  return [];
29651
33333
  }
29652
- throw error;
29653
- }
29654
- if (dirStats.isSymbolicLink() || !dirStats.isDirectory()) {
33334
+ } catch {
29655
33335
  return [];
29656
33336
  }
29657
33337
  let entries;
29658
33338
  try {
29659
- entries = await (0,promises_.readdir)(skillsDir, {
29660
- withFileTypes: true
29661
- });
33339
+ entries = await listDirectoryWithinRoot(targetDir, skillsDir);
29662
33340
  } catch (error) {
29663
33341
  if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
29664
33342
  return [];
@@ -29673,20 +33351,16 @@ const PackageJsonSchema = schemas_object({
29673
33351
  if (entry.name.includes("..") || entry.name.includes("/") || entry.name.includes("\\")) {
29674
33352
  continue;
29675
33353
  }
29676
- const skillFilePath = (0,external_node_path_.join)(skillsDir, entry.name, "SKILL.md");
29677
- let skillStat;
33354
+ const skillRelativePath = (0,external_node_path_.join)(skillsDir, entry.name, "SKILL.md");
33355
+ let hasSkillFile = false;
29678
33356
  try {
29679
- skillStat = await (0,promises_.lstat)(skillFilePath);
29680
- } catch (error) {
29681
- if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
29682
- skillStat = null;
29683
- } else {
29684
- throw error;
29685
- }
33357
+ hasSkillFile = await pathExistsWithinRoot(targetDir, skillRelativePath);
33358
+ } catch {
33359
+ hasSkillFile = false;
29686
33360
  }
29687
- if (skillStat && !skillStat.isSymbolicLink() && skillStat.isFile()) {
33361
+ if (hasSkillFile) {
29688
33362
  skills.push({
29689
- filePath: skillFilePath,
33363
+ filePath: (0,external_node_path_.join)(targetDir, skillRelativePath),
29690
33364
  skillName: entry.name
29691
33365
  });
29692
33366
  }
@@ -29733,7 +33407,7 @@ const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
29733
33407
  const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
29734
33408
  const _PATH_ROOT_RE = /^[/\\]|^[a-zA-Z]:[/\\]/;
29735
33409
  const sep = "/";
29736
- const normalize = function(path) {
33410
+ const pathe_M_eThtNZ_normalize = function(path) {
29737
33411
  if (path.length === 0) {
29738
33412
  return ".";
29739
33413
  }
@@ -29781,7 +33455,7 @@ const pathe_M_eThtNZ_join = function(...segments) {
29781
33455
  path += seg;
29782
33456
  }
29783
33457
  }
29784
- return normalize(path);
33458
+ return pathe_M_eThtNZ_normalize(path);
29785
33459
  };
29786
33460
  function pathe_M_eThtNZ_cwd() {
29787
33461
  if (typeof process !== "undefined" && typeof process.cwd === "function") {
@@ -29877,7 +33551,7 @@ const pathe_M_eThtNZ_extname = function(p) {
29877
33551
  const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
29878
33552
  return match && match[1] || "";
29879
33553
  };
29880
- const relative = function(from, to) {
33554
+ const pathe_M_eThtNZ_relative = function(from, to) {
29881
33555
  const _from = pathe_M_eThtNZ_resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
29882
33556
  const _to = pathe_M_eThtNZ_resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
29883
33557
  if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
@@ -29934,7 +33608,7 @@ const pathe_M_eThtNZ_parse = function(p) {
29934
33608
  };
29935
33609
  };
29936
33610
  const matchesGlob = (path, pattern) => {
29937
- return zeptomatch(pattern, normalize(path));
33611
+ return zeptomatch(pattern, pathe_M_eThtNZ_normalize(path));
29938
33612
  };
29939
33613
 
29940
33614
  const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
@@ -29946,10 +33620,10 @@ const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
29946
33620
  isAbsolute: isAbsolute,
29947
33621
  join: pathe_M_eThtNZ_join,
29948
33622
  matchesGlob: matchesGlob,
29949
- normalize: normalize,
33623
+ normalize: pathe_M_eThtNZ_normalize,
29950
33624
  normalizeString: normalizeString,
29951
33625
  parse: pathe_M_eThtNZ_parse,
29952
- relative: relative,
33626
+ relative: pathe_M_eThtNZ_relative,
29953
33627
  resolve: pathe_M_eThtNZ_resolve,
29954
33628
  sep: sep,
29955
33629
  toNamespacedPath: toNamespacedPath
@@ -29959,8 +33633,6 @@ const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
29959
33633
 
29960
33634
  // EXTERNAL MODULE: ../../node_modules/dotenv/lib/main.js
29961
33635
  var lib_main = __webpack_require__(5599);
29962
- // EXTERNAL MODULE: external "node:os"
29963
- var external_node_os_ = __webpack_require__(8161);
29964
33636
  // EXTERNAL MODULE: external "node:assert"
29965
33637
  var external_node_assert_ = __webpack_require__(4589);
29966
33638
  // EXTERNAL MODULE: external "node:v8"
@@ -31495,8 +35167,8 @@ function findFarthestFile(filename, options = {}) {
31495
35167
  });
31496
35168
  }
31497
35169
  function _resolvePath(id, opts = {}) {
31498
- if (id instanceof URL || id.startsWith("file://")) return normalize((0,external_node_url_.fileURLToPath)(id));
31499
- if (isAbsolute(id)) return normalize(id);
35170
+ if (id instanceof URL || id.startsWith("file://")) return pathe_M_eThtNZ_normalize((0,external_node_url_.fileURLToPath)(id));
35171
+ if (isAbsolute(id)) return pathe_M_eThtNZ_normalize(id);
31500
35172
  return resolveModulePath(id, {
31501
35173
  ...opts,
31502
35174
  from: opts.from || opts.parent || opts.url
@@ -32105,7 +35777,7 @@ function tryResolve(id, options) {
32105
35777
  extensions: SUPPORTED_EXTENSIONS,
32106
35778
  cache: false
32107
35779
  });
32108
- return res ? normalize(res) : void 0;
35780
+ return res ? pathe_M_eThtNZ_normalize(res) : void 0;
32109
35781
  }
32110
35782
  //#endregion
32111
35783
  //#region src/types.ts
@@ -42338,7 +46010,7 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
42338
46010
  // module factories are used so entry inlining is disabled
42339
46011
  // startup
42340
46012
  // Load entry module and return exports
42341
- var __webpack_exports__ = __webpack_require__(5144);
46013
+ var __webpack_exports__ = __webpack_require__(2202);
42342
46014
  var __webpack_exports__DEFAULT_LINT_RULES = __webpack_exports__.Kd;
42343
46015
  var __webpack_exports__LintValidationError = __webpack_exports__.F5;
42344
46016
  var __webpack_exports__createFormatter = __webpack_exports__.xi;