@lousy-agents/mcp 5.14.1 → 5.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-server.js +3863 -182
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -6559,7 +6559,7 @@ function escapeJsonPtr(str) {
|
|
|
6559
6559
|
|
|
6560
6560
|
|
|
6561
6561
|
},
|
|
6562
|
-
|
|
6562
|
+
2415(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
6563
6563
|
// NAMESPACE OBJECT: ../../node_modules/micromark/lib/constructs.js
|
|
6564
6564
|
var constructs_namespaceObject = {};
|
|
6565
6565
|
__webpack_require__.r(constructs_namespaceObject);
|
|
@@ -26479,8 +26479,6 @@ const EMPTY_COMPLETION_RESULT = {
|
|
|
26479
26479
|
}
|
|
26480
26480
|
};
|
|
26481
26481
|
//# sourceMappingURL=mcp.js.map
|
|
26482
|
-
// EXTERNAL MODULE: external "node:fs/promises"
|
|
26483
|
-
var promises_ = __webpack_require__(1455);
|
|
26484
26482
|
// EXTERNAL MODULE: external "node:path"
|
|
26485
26483
|
var external_node_path_ = __webpack_require__(6760);
|
|
26486
26484
|
;// CONCATENATED MODULE: ../core/src/gateways/action-version-gateway.ts
|
|
@@ -26506,34 +26504,3650 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
26506
26504
|
// Simulate async lookup (for future remote API compatibility)
|
|
26507
26505
|
return DEFAULT_ACTION_VERSIONS[actionName];
|
|
26508
26506
|
}
|
|
26509
|
-
async getVersions(actionNames) {
|
|
26510
|
-
const result = {};
|
|
26511
|
-
for (const actionName of actionNames){
|
|
26512
|
-
const version = await this.getVersion(actionName);
|
|
26513
|
-
if (version) {
|
|
26514
|
-
result[actionName] = version;
|
|
26507
|
+
async getVersions(actionNames) {
|
|
26508
|
+
const result = {};
|
|
26509
|
+
for (const actionName of actionNames){
|
|
26510
|
+
const version = await this.getVersion(actionName);
|
|
26511
|
+
if (version) {
|
|
26512
|
+
result[actionName] = version;
|
|
26513
|
+
}
|
|
26514
|
+
}
|
|
26515
|
+
return result;
|
|
26516
|
+
}
|
|
26517
|
+
}
|
|
26518
|
+
/**
|
|
26519
|
+
* Creates and returns the default action version gateway
|
|
26520
|
+
*/ function createActionVersionGateway() {
|
|
26521
|
+
return new LocalActionVersionGateway();
|
|
26522
|
+
}
|
|
26523
|
+
/**
|
|
26524
|
+
* List of known action names for convenience
|
|
26525
|
+
*/ const KNOWN_ACTIONS = Object.keys(DEFAULT_ACTION_VERSIONS);
|
|
26526
|
+
|
|
26527
|
+
// EXTERNAL MODULE: external "node:fs/promises"
|
|
26528
|
+
var promises_ = __webpack_require__(1455);
|
|
26529
|
+
// EXTERNAL MODULE: external "node:fs"
|
|
26530
|
+
var external_node_fs_ = __webpack_require__(3024);
|
|
26531
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/errors.js
|
|
26532
|
+
const OPERATIONAL_CODES = new Set([
|
|
26533
|
+
"helper-failed",
|
|
26534
|
+
"helper-unavailable",
|
|
26535
|
+
"permission-unverified",
|
|
26536
|
+
"timeout",
|
|
26537
|
+
"unsupported-platform",
|
|
26538
|
+
]);
|
|
26539
|
+
function categorizeFsSafeError(code) {
|
|
26540
|
+
return OPERATIONAL_CODES.has(code) ? "operational" : "policy";
|
|
26541
|
+
}
|
|
26542
|
+
class errors_FsSafeError extends Error {
|
|
26543
|
+
code;
|
|
26544
|
+
category;
|
|
26545
|
+
constructor(code, message, options = {}) {
|
|
26546
|
+
super(message, options);
|
|
26547
|
+
this.name = "FsSafeError";
|
|
26548
|
+
this.code = code;
|
|
26549
|
+
this.category = categorizeFsSafeError(code);
|
|
26550
|
+
}
|
|
26551
|
+
}
|
|
26552
|
+
|
|
26553
|
+
// EXTERNAL MODULE: external "node:crypto"
|
|
26554
|
+
var external_node_crypto_ = __webpack_require__(7598);
|
|
26555
|
+
// EXTERNAL MODULE: external "node:stream/promises"
|
|
26556
|
+
var external_node_stream_promises_ = __webpack_require__(6466);
|
|
26557
|
+
// EXTERNAL MODULE: external "node:stream"
|
|
26558
|
+
var external_node_stream_ = __webpack_require__(7075);
|
|
26559
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read-stream.js
|
|
26560
|
+
|
|
26561
|
+
|
|
26562
|
+
function createMaxBytesTransform(maxBytes) {
|
|
26563
|
+
let bytes = 0;
|
|
26564
|
+
return new external_node_stream_.Transform({
|
|
26565
|
+
transform(chunk, _encoding, callback) {
|
|
26566
|
+
const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
|
|
26567
|
+
bytes += buffer.byteLength;
|
|
26568
|
+
if (bytes > maxBytes) {
|
|
26569
|
+
callback(new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
|
|
26570
|
+
return;
|
|
26571
|
+
}
|
|
26572
|
+
callback(null, buffer);
|
|
26573
|
+
},
|
|
26574
|
+
});
|
|
26575
|
+
}
|
|
26576
|
+
function createBoundedReadStream(opened, maxBytes) {
|
|
26577
|
+
const stream = opened.handle.createReadStream();
|
|
26578
|
+
return maxBytes === undefined ? stream : stream.pipe(createMaxBytesTransform(maxBytes));
|
|
26579
|
+
}
|
|
26580
|
+
|
|
26581
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/file-identity.js
|
|
26582
|
+
function isZero(value) {
|
|
26583
|
+
return value === 0 || value === 0n;
|
|
26584
|
+
}
|
|
26585
|
+
function file_identity_sameFileIdentity(left, right, platform = process.platform) {
|
|
26586
|
+
if (left.ino !== right.ino) {
|
|
26587
|
+
return false;
|
|
26588
|
+
}
|
|
26589
|
+
// On Windows, path-based stat calls can report dev=0 while fd-based stat
|
|
26590
|
+
// reports a real volume serial; treat either-side dev=0 as "unknown device".
|
|
26591
|
+
if (left.dev === right.dev) {
|
|
26592
|
+
return true;
|
|
26593
|
+
}
|
|
26594
|
+
return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
|
|
26595
|
+
}
|
|
26596
|
+
|
|
26597
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/string-coerce.js
|
|
26598
|
+
function readStringValue(value) {
|
|
26599
|
+
return typeof value === "string" ? value : undefined;
|
|
26600
|
+
}
|
|
26601
|
+
function normalizeNullableString(value) {
|
|
26602
|
+
if (typeof value !== "string") {
|
|
26603
|
+
return null;
|
|
26604
|
+
}
|
|
26605
|
+
const trimmed = value.trim();
|
|
26606
|
+
return trimmed ? trimmed : null;
|
|
26607
|
+
}
|
|
26608
|
+
function normalizeOptionalString(value) {
|
|
26609
|
+
return normalizeNullableString(value) ?? undefined;
|
|
26610
|
+
}
|
|
26611
|
+
function normalizeStringifiedOptionalString(value) {
|
|
26612
|
+
if (typeof value === "string") {
|
|
26613
|
+
return normalizeOptionalString(value);
|
|
26614
|
+
}
|
|
26615
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
26616
|
+
return normalizeOptionalString(String(value));
|
|
26617
|
+
}
|
|
26618
|
+
return undefined;
|
|
26619
|
+
}
|
|
26620
|
+
function normalizeOptionalLowercaseString(value) {
|
|
26621
|
+
return normalizeOptionalString(value)?.toLowerCase();
|
|
26622
|
+
}
|
|
26623
|
+
function normalizeLowercaseStringOrEmpty(value) {
|
|
26624
|
+
return normalizeOptionalLowercaseString(value) ?? "";
|
|
26625
|
+
}
|
|
26626
|
+
function normalizeFastMode(raw) {
|
|
26627
|
+
if (typeof raw === "boolean") {
|
|
26628
|
+
return raw;
|
|
26629
|
+
}
|
|
26630
|
+
if (!raw) {
|
|
26631
|
+
return undefined;
|
|
26632
|
+
}
|
|
26633
|
+
const key = normalizeLowercaseStringOrEmpty(raw);
|
|
26634
|
+
if (["off", "false", "no", "0", "disable", "disabled", "normal"].includes(key)) {
|
|
26635
|
+
return false;
|
|
26636
|
+
}
|
|
26637
|
+
if (["on", "true", "yes", "1", "enable", "enabled", "fast"].includes(key)) {
|
|
26638
|
+
return true;
|
|
26639
|
+
}
|
|
26640
|
+
return undefined;
|
|
26641
|
+
}
|
|
26642
|
+
function lowercasePreservingWhitespace(value) {
|
|
26643
|
+
return value.toLowerCase();
|
|
26644
|
+
}
|
|
26645
|
+
function localeLowercasePreservingWhitespace(value) {
|
|
26646
|
+
return value.toLocaleLowerCase();
|
|
26647
|
+
}
|
|
26648
|
+
function resolvePrimaryStringValue(value) {
|
|
26649
|
+
if (typeof value === "string") {
|
|
26650
|
+
return normalizeOptionalString(value);
|
|
26651
|
+
}
|
|
26652
|
+
if (!value || typeof value !== "object") {
|
|
26653
|
+
return undefined;
|
|
26654
|
+
}
|
|
26655
|
+
return normalizeOptionalString(value.primary);
|
|
26656
|
+
}
|
|
26657
|
+
function normalizeOptionalThreadValue(value) {
|
|
26658
|
+
if (typeof value === "number") {
|
|
26659
|
+
return Number.isFinite(value) ? Math.trunc(value) : undefined;
|
|
26660
|
+
}
|
|
26661
|
+
return normalizeOptionalString(value);
|
|
26662
|
+
}
|
|
26663
|
+
function normalizeOptionalStringifiedId(value) {
|
|
26664
|
+
const normalized = normalizeOptionalThreadValue(value);
|
|
26665
|
+
return normalized == null ? undefined : String(normalized);
|
|
26666
|
+
}
|
|
26667
|
+
function hasNonEmptyString(value) {
|
|
26668
|
+
return normalizeOptionalString(value) !== undefined;
|
|
26669
|
+
}
|
|
26670
|
+
|
|
26671
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path.js
|
|
26672
|
+
|
|
26673
|
+
|
|
26674
|
+
|
|
26675
|
+
|
|
26676
|
+
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
26677
|
+
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
|
|
26678
|
+
const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
|
|
26679
|
+
function normalizeWindowsPathForComparison(input) {
|
|
26680
|
+
let normalized = external_node_path_.win32.normalize(input);
|
|
26681
|
+
if (normalized.startsWith("\\\\?\\")) {
|
|
26682
|
+
normalized = normalized.slice(4);
|
|
26683
|
+
if (normalized.toUpperCase().startsWith("UNC\\")) {
|
|
26684
|
+
normalized = `\\\\${normalized.slice(4)}`;
|
|
26685
|
+
}
|
|
26686
|
+
}
|
|
26687
|
+
return normalizeLowercaseStringOrEmpty(normalized.replaceAll("/", "\\"));
|
|
26688
|
+
}
|
|
26689
|
+
function isNodeError(value) {
|
|
26690
|
+
return Boolean(value && typeof value === "object" && "code" in value);
|
|
26691
|
+
}
|
|
26692
|
+
function hasNodeErrorCode(value, code) {
|
|
26693
|
+
return isNodeError(value) && value.code === code;
|
|
26694
|
+
}
|
|
26695
|
+
function path_assertNoNulPathInput(filePath, message = "path contains a NUL byte") {
|
|
26696
|
+
if (filePath.includes("\0")) {
|
|
26697
|
+
throw new errors_FsSafeError("invalid-path", message);
|
|
26698
|
+
}
|
|
26699
|
+
}
|
|
26700
|
+
function path_isNotFoundPathError(value) {
|
|
26701
|
+
return isNodeError(value) && typeof value.code === "string" && NOT_FOUND_CODES.has(value.code);
|
|
26702
|
+
}
|
|
26703
|
+
function isSymlinkOpenError(value) {
|
|
26704
|
+
return isNodeError(value) && typeof value.code === "string" && SYMLINK_OPEN_CODES.has(value.code);
|
|
26705
|
+
}
|
|
26706
|
+
function path_isPathInside(root, target) {
|
|
26707
|
+
if (process.platform === "win32") {
|
|
26708
|
+
const rootForCompare = normalizeWindowsPathForComparison(external_node_path_.win32.resolve(root));
|
|
26709
|
+
const targetForCompare = normalizeWindowsPathForComparison(external_node_path_.win32.resolve(target));
|
|
26710
|
+
const relative = external_node_path_.win32.relative(rootForCompare, targetForCompare);
|
|
26711
|
+
const firstSegment = relative.split(external_node_path_.win32.sep)[0];
|
|
26712
|
+
return (relative === "" || (firstSegment !== ".." && !external_node_path_.win32.isAbsolute(relative)));
|
|
26713
|
+
}
|
|
26714
|
+
if (root.length > 0 &&
|
|
26715
|
+
root.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
|
|
26716
|
+
target.length >= root.length &&
|
|
26717
|
+
target.charCodeAt(0) === POSIX_SEPARATOR_CHAR_CODE &&
|
|
26718
|
+
!target.includes("/..") &&
|
|
26719
|
+
(target === root ||
|
|
26720
|
+
(target.startsWith(root) && target.charCodeAt(root.length) === POSIX_SEPARATOR_CHAR_CODE))) {
|
|
26721
|
+
return true;
|
|
26722
|
+
}
|
|
26723
|
+
const resolvedRoot = external_node_path_.resolve(root);
|
|
26724
|
+
const resolvedTarget = external_node_path_.resolve(target);
|
|
26725
|
+
const relative = external_node_path_.relative(resolvedRoot, resolvedTarget);
|
|
26726
|
+
const firstSegment = relative.split(external_node_path_.posix.sep)[0];
|
|
26727
|
+
return relative === "" || (firstSegment !== ".." && !external_node_path_.isAbsolute(relative));
|
|
26728
|
+
}
|
|
26729
|
+
function resolveSafeBaseDir(rootDir) {
|
|
26730
|
+
const resolved = path.resolve(rootDir);
|
|
26731
|
+
return resolved.endsWith(path.sep) ? resolved : `${resolved}${path.sep}`;
|
|
26732
|
+
}
|
|
26733
|
+
function isWithinDir(rootDir, targetPath) {
|
|
26734
|
+
return path_isPathInside(rootDir, targetPath);
|
|
26735
|
+
}
|
|
26736
|
+
function safeRealpathSync(targetPath, cache) {
|
|
26737
|
+
const cached = cache?.get(targetPath);
|
|
26738
|
+
if (cached) {
|
|
26739
|
+
return cached;
|
|
26740
|
+
}
|
|
26741
|
+
try {
|
|
26742
|
+
const resolved = fs.realpathSync(targetPath);
|
|
26743
|
+
cache?.set(targetPath, resolved);
|
|
26744
|
+
cache?.set(resolved, resolved);
|
|
26745
|
+
return resolved;
|
|
26746
|
+
}
|
|
26747
|
+
catch {
|
|
26748
|
+
return null;
|
|
26749
|
+
}
|
|
26750
|
+
}
|
|
26751
|
+
function isPathInsideWithRealpath(basePath, candidatePath, opts) {
|
|
26752
|
+
if (!path_isPathInside(basePath, candidatePath)) {
|
|
26753
|
+
return false;
|
|
26754
|
+
}
|
|
26755
|
+
const baseReal = safeRealpathSync(basePath, opts?.cache);
|
|
26756
|
+
const candidateReal = safeRealpathSync(candidatePath, opts?.cache);
|
|
26757
|
+
if (!baseReal || !candidateReal) {
|
|
26758
|
+
return opts?.requireRealpath === false;
|
|
26759
|
+
}
|
|
26760
|
+
return path_isPathInside(baseReal, candidateReal);
|
|
26761
|
+
}
|
|
26762
|
+
function safeStatSync(targetPath) {
|
|
26763
|
+
try {
|
|
26764
|
+
return fs.statSync(targetPath);
|
|
26765
|
+
}
|
|
26766
|
+
catch {
|
|
26767
|
+
return null;
|
|
26768
|
+
}
|
|
26769
|
+
}
|
|
26770
|
+
function splitSafeRelativePath(relativePath) {
|
|
26771
|
+
if (relativePath.length === 0 || relativePath === ".") {
|
|
26772
|
+
return [];
|
|
26773
|
+
}
|
|
26774
|
+
path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
26775
|
+
if (relativePath.includes("\\")) {
|
|
26776
|
+
throw new FsSafeError("invalid-path", "relative path must use forward slashes");
|
|
26777
|
+
}
|
|
26778
|
+
if (path.posix.isAbsolute(relativePath) ||
|
|
26779
|
+
path.win32.isAbsolute(relativePath) ||
|
|
26780
|
+
relativePath.startsWith("//")) {
|
|
26781
|
+
throw new FsSafeError("invalid-path", "relative path must not be absolute");
|
|
26782
|
+
}
|
|
26783
|
+
const segments = relativePath.split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
26784
|
+
for (const segment of segments) {
|
|
26785
|
+
if (segment === "..") {
|
|
26786
|
+
throw new FsSafeError("invalid-path", "relative path must not contain '..'");
|
|
26787
|
+
}
|
|
26788
|
+
}
|
|
26789
|
+
return segments;
|
|
26790
|
+
}
|
|
26791
|
+
function resolveSafeRelativePath(rootDir, relativePath) {
|
|
26792
|
+
const root = path.resolve(rootDir);
|
|
26793
|
+
const target = path.resolve(root, ...splitSafeRelativePath(relativePath));
|
|
26794
|
+
if (target !== root && !target.startsWith(root + path.sep)) {
|
|
26795
|
+
throw new FsSafeError("outside-workspace", "relative path escapes root");
|
|
26796
|
+
}
|
|
26797
|
+
return target;
|
|
26798
|
+
}
|
|
26799
|
+
|
|
26800
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/directory-guard.js
|
|
26801
|
+
|
|
26802
|
+
|
|
26803
|
+
|
|
26804
|
+
|
|
26805
|
+
|
|
26806
|
+
|
|
26807
|
+
async function directory_guard_createAsyncDirectoryGuard(dir) {
|
|
26808
|
+
const stat = await promises_.lstat(dir);
|
|
26809
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26810
|
+
throw new errors_FsSafeError("not-file", "directory component must be a directory");
|
|
26811
|
+
}
|
|
26812
|
+
return { dir, realPath: await promises_.realpath(dir), stat };
|
|
26813
|
+
}
|
|
26814
|
+
async function assertAsyncDirectoryGuard(guard) {
|
|
26815
|
+
const stat = await promises_.lstat(guard.dir);
|
|
26816
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26817
|
+
throw new errors_FsSafeError("not-file", "directory component must be a directory");
|
|
26818
|
+
}
|
|
26819
|
+
if (!file_identity_sameFileIdentity(stat, guard.stat) || (await promises_.realpath(guard.dir)) !== guard.realPath) {
|
|
26820
|
+
throw new errors_FsSafeError("path-mismatch", "directory changed during operation");
|
|
26821
|
+
}
|
|
26822
|
+
}
|
|
26823
|
+
function directory_guard_createSyncDirectoryGuard(dir) {
|
|
26824
|
+
const stat = fsSync.lstatSync(dir);
|
|
26825
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26826
|
+
throw new FsSafeError("not-file", "directory component must be a directory");
|
|
26827
|
+
}
|
|
26828
|
+
return { dir, realPath: fsSync.realpathSync(dir), stat };
|
|
26829
|
+
}
|
|
26830
|
+
function directory_guard_assertSyncDirectoryGuard(guard) {
|
|
26831
|
+
const stat = fsSync.lstatSync(guard.dir);
|
|
26832
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26833
|
+
throw new FsSafeError("not-file", "directory component must be a directory");
|
|
26834
|
+
}
|
|
26835
|
+
if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync(guard.dir) !== guard.realPath) {
|
|
26836
|
+
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
26837
|
+
}
|
|
26838
|
+
}
|
|
26839
|
+
async function directory_guard_createNearestExistingDirectoryGuard(rootReal, targetPath) {
|
|
26840
|
+
let current = external_node_path_.resolve(targetPath);
|
|
26841
|
+
const root = external_node_path_.resolve(rootReal);
|
|
26842
|
+
while (current !== root) {
|
|
26843
|
+
try {
|
|
26844
|
+
return await directory_guard_createAsyncDirectoryGuard(current);
|
|
26845
|
+
}
|
|
26846
|
+
catch (error) {
|
|
26847
|
+
if (!path_isNotFoundPathError(error)) {
|
|
26848
|
+
throw error;
|
|
26849
|
+
}
|
|
26850
|
+
current = external_node_path_.dirname(current);
|
|
26851
|
+
}
|
|
26852
|
+
}
|
|
26853
|
+
return await directory_guard_createAsyncDirectoryGuard(root);
|
|
26854
|
+
}
|
|
26855
|
+
function directory_guard_createNearestExistingSyncDirectoryGuard(rootReal, targetPath) {
|
|
26856
|
+
let current = path.resolve(targetPath);
|
|
26857
|
+
const root = path.resolve(rootReal);
|
|
26858
|
+
while (current !== root) {
|
|
26859
|
+
try {
|
|
26860
|
+
return directory_guard_createSyncDirectoryGuard(current);
|
|
26861
|
+
}
|
|
26862
|
+
catch (error) {
|
|
26863
|
+
if (!isNotFoundPathError(error)) {
|
|
26864
|
+
throw error;
|
|
26865
|
+
}
|
|
26866
|
+
current = path.dirname(current);
|
|
26867
|
+
}
|
|
26868
|
+
}
|
|
26869
|
+
return directory_guard_createSyncDirectoryGuard(root);
|
|
26870
|
+
}
|
|
26871
|
+
|
|
26872
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js
|
|
26873
|
+
|
|
26874
|
+
|
|
26875
|
+
|
|
26876
|
+
|
|
26877
|
+
function isSameOrChildPath(candidate, parent) {
|
|
26878
|
+
return candidate === parent || candidate.startsWith(`${parent}${external_node_path_.sep}`);
|
|
26879
|
+
}
|
|
26880
|
+
function isPathEscape(relativePath) {
|
|
26881
|
+
return relativePath === ".." || relativePath.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relativePath);
|
|
26882
|
+
}
|
|
26883
|
+
async function mkdirPathComponentsWithGuards(params) {
|
|
26884
|
+
const root = external_node_path_.resolve(params.rootReal);
|
|
26885
|
+
const target = external_node_path_.resolve(params.targetPath);
|
|
26886
|
+
const relative = external_node_path_.relative(root, target);
|
|
26887
|
+
if (isPathEscape(relative)) {
|
|
26888
|
+
throw new errors_FsSafeError("outside-workspace", "directory is outside workspace root");
|
|
26889
|
+
}
|
|
26890
|
+
let current = root;
|
|
26891
|
+
for (const part of relative.split(external_node_path_.sep).filter(Boolean)) {
|
|
26892
|
+
const next = external_node_path_.join(current, part);
|
|
26893
|
+
const parentGuard = await directory_guard_createAsyncDirectoryGuard(current);
|
|
26894
|
+
await params.beforeComponent?.(next);
|
|
26895
|
+
await assertAsyncDirectoryGuard(parentGuard);
|
|
26896
|
+
try {
|
|
26897
|
+
await promises_.mkdir(next);
|
|
26898
|
+
}
|
|
26899
|
+
catch (error) {
|
|
26900
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
|
|
26901
|
+
throw error;
|
|
26902
|
+
}
|
|
26903
|
+
}
|
|
26904
|
+
const stat = await promises_.lstat(next);
|
|
26905
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26906
|
+
throw new errors_FsSafeError("not-file", "directory component must be a directory");
|
|
26907
|
+
}
|
|
26908
|
+
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
26909
|
+
// segment at a time and realpath-check each segment before descending.
|
|
26910
|
+
if (!isSameOrChildPath(external_node_path_.resolve(await promises_.realpath(next)), root)) {
|
|
26911
|
+
throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
|
|
26912
|
+
}
|
|
26913
|
+
await directory_guard_createAsyncDirectoryGuard(next);
|
|
26914
|
+
await assertAsyncDirectoryGuard(parentGuard);
|
|
26915
|
+
current = next;
|
|
26916
|
+
}
|
|
26917
|
+
}
|
|
26918
|
+
|
|
26919
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/guarded-mutation.js
|
|
26920
|
+
|
|
26921
|
+
|
|
26922
|
+
|
|
26923
|
+
|
|
26924
|
+
async function withAsyncDirectoryGuards(guards, mutate, options = {}) {
|
|
26925
|
+
for (const guard of guards) {
|
|
26926
|
+
await assertAsyncDirectoryGuard(guard);
|
|
26927
|
+
}
|
|
26928
|
+
const result = await mutate();
|
|
26929
|
+
if (options.verifyAfter !== false) {
|
|
26930
|
+
try {
|
|
26931
|
+
for (const guard of guards) {
|
|
26932
|
+
await assertAsyncDirectoryGuard(guard);
|
|
26933
|
+
}
|
|
26934
|
+
}
|
|
26935
|
+
catch (error) {
|
|
26936
|
+
if (options.onPostGuardFailure) {
|
|
26937
|
+
try {
|
|
26938
|
+
// The mutation may have returned an owned resource before the post-guard
|
|
26939
|
+
// check detected a swapped directory. Give callers one chance to close
|
|
26940
|
+
// handles without letting cleanup hide the boundary failure.
|
|
26941
|
+
await options.onPostGuardFailure(result, error);
|
|
26942
|
+
}
|
|
26943
|
+
catch {
|
|
26944
|
+
// Preserve the boundary failure. Cleanup is best-effort.
|
|
26945
|
+
}
|
|
26946
|
+
}
|
|
26947
|
+
throw error;
|
|
26948
|
+
}
|
|
26949
|
+
}
|
|
26950
|
+
return result;
|
|
26951
|
+
}
|
|
26952
|
+
function withSyncDirectoryGuards(guards, mutate, options = {}) {
|
|
26953
|
+
for (const guard of guards) {
|
|
26954
|
+
assertSyncDirectoryGuard(guard);
|
|
26955
|
+
}
|
|
26956
|
+
const result = mutate();
|
|
26957
|
+
if (options.verifyAfter !== false) {
|
|
26958
|
+
for (const guard of guards) {
|
|
26959
|
+
assertSyncDirectoryGuard(guard);
|
|
26960
|
+
}
|
|
26961
|
+
}
|
|
26962
|
+
return result;
|
|
26963
|
+
}
|
|
26964
|
+
async function guardedRename(params) {
|
|
26965
|
+
const sourceGuard = await createAsyncDirectoryGuard(path.dirname(params.from));
|
|
26966
|
+
const targetGuard = params.targetRoot
|
|
26967
|
+
? await createNearestExistingDirectoryGuard(params.targetRoot, path.dirname(params.to))
|
|
26968
|
+
: await createAsyncDirectoryGuard(path.dirname(params.to));
|
|
26969
|
+
await withAsyncDirectoryGuards([sourceGuard, targetGuard], async () => {
|
|
26970
|
+
await fs.rename(params.from, params.to);
|
|
26971
|
+
}, { verifyAfter: params.verifyAfter });
|
|
26972
|
+
}
|
|
26973
|
+
function guardedRenameSync(params) {
|
|
26974
|
+
const sourceGuard = createSyncDirectoryGuard(path.dirname(params.from));
|
|
26975
|
+
const targetGuard = params.targetRoot
|
|
26976
|
+
? createNearestExistingSyncDirectoryGuard(params.targetRoot, path.dirname(params.to))
|
|
26977
|
+
: createSyncDirectoryGuard(path.dirname(params.to));
|
|
26978
|
+
withSyncDirectoryGuards([sourceGuard, targetGuard], () => fsSync.renameSync(params.from, params.to), { verifyAfter: params.verifyAfter });
|
|
26979
|
+
}
|
|
26980
|
+
async function guardedRm(params) {
|
|
26981
|
+
const guard = await createAsyncDirectoryGuard(path.dirname(params.target));
|
|
26982
|
+
await withAsyncDirectoryGuards([guard], async () => {
|
|
26983
|
+
await fs.rm(params.target, {
|
|
26984
|
+
...(params.recursive !== undefined ? { recursive: params.recursive } : {}),
|
|
26985
|
+
...(params.force !== undefined ? { force: params.force } : {}),
|
|
26986
|
+
});
|
|
26987
|
+
}, { verifyAfter: params.verifyAfter });
|
|
26988
|
+
}
|
|
26989
|
+
function guardedRmSync(params) {
|
|
26990
|
+
const guard = createSyncDirectoryGuard(path.dirname(params.target));
|
|
26991
|
+
withSyncDirectoryGuards([guard], () => fsSync.rmSync(params.target, {
|
|
26992
|
+
...(params.recursive !== undefined ? { recursive: params.recursive } : {}),
|
|
26993
|
+
...(params.force !== undefined ? { force: params.force } : {}),
|
|
26994
|
+
}), { verifyAfter: params.verifyAfter });
|
|
26995
|
+
}
|
|
26996
|
+
|
|
26997
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-python-config.js
|
|
26998
|
+
let overrideConfig = {};
|
|
26999
|
+
function parseMode(value) {
|
|
27000
|
+
if (!value) {
|
|
27001
|
+
return undefined;
|
|
27002
|
+
}
|
|
27003
|
+
const normalized = value.trim().toLowerCase();
|
|
27004
|
+
if (normalized === "0" || normalized === "false" || normalized === "off" || normalized === "never") {
|
|
27005
|
+
return "off";
|
|
27006
|
+
}
|
|
27007
|
+
if (normalized === "1" || normalized === "true" || normalized === "on" || normalized === "auto") {
|
|
27008
|
+
return "auto";
|
|
27009
|
+
}
|
|
27010
|
+
if (normalized === "required" || normalized === "require") {
|
|
27011
|
+
return "require";
|
|
27012
|
+
}
|
|
27013
|
+
return undefined;
|
|
27014
|
+
}
|
|
27015
|
+
function configureFsSafePython(config) {
|
|
27016
|
+
overrideConfig = { ...overrideConfig, ...config };
|
|
27017
|
+
}
|
|
27018
|
+
function getFsSafePythonConfig() {
|
|
27019
|
+
return {
|
|
27020
|
+
mode: overrideConfig.mode ??
|
|
27021
|
+
parseMode(process.env.FS_SAFE_PYTHON_MODE) ??
|
|
27022
|
+
parseMode(process.env.OPENCLAW_FS_SAFE_PYTHON_MODE) ??
|
|
27023
|
+
"auto",
|
|
27024
|
+
pythonPath: overrideConfig.pythonPath ??
|
|
27025
|
+
process.env.FS_SAFE_PYTHON ??
|
|
27026
|
+
process.env.OPENCLAW_FS_SAFE_PYTHON ??
|
|
27027
|
+
process.env.OPENCLAW_PINNED_PYTHON ??
|
|
27028
|
+
process.env.OPENCLAW_PINNED_WRITE_PYTHON,
|
|
27029
|
+
};
|
|
27030
|
+
}
|
|
27031
|
+
function canFallbackFromPythonError(error) {
|
|
27032
|
+
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
27033
|
+
return (getFsSafePythonConfig().mode !== "require" &&
|
|
27034
|
+
(code === "helper-unavailable" || code === "unsupported-platform"));
|
|
27035
|
+
}
|
|
27036
|
+
|
|
27037
|
+
// EXTERNAL MODULE: external "node:child_process"
|
|
27038
|
+
var external_node_child_process_ = __webpack_require__(1421);
|
|
27039
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-python.js
|
|
27040
|
+
|
|
27041
|
+
|
|
27042
|
+
|
|
27043
|
+
|
|
27044
|
+
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
27045
|
+
import base64
|
|
27046
|
+
import errno
|
|
27047
|
+
import json
|
|
27048
|
+
import os
|
|
27049
|
+
import secrets
|
|
27050
|
+
import stat
|
|
27051
|
+
import sys
|
|
27052
|
+
|
|
27053
|
+
DIR_FLAGS = os.O_RDONLY
|
|
27054
|
+
if hasattr(os, "O_DIRECTORY"):
|
|
27055
|
+
DIR_FLAGS |= os.O_DIRECTORY
|
|
27056
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
27057
|
+
DIR_FLAGS |= os.O_NOFOLLOW
|
|
27058
|
+
READ_FLAGS = os.O_RDONLY
|
|
27059
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
27060
|
+
READ_FLAGS |= os.O_NOFOLLOW
|
|
27061
|
+
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
27062
|
+
if hasattr(os, "O_NOFOLLOW"):
|
|
27063
|
+
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
27064
|
+
|
|
27065
|
+
def split_relative(value):
|
|
27066
|
+
if value in ("", "."):
|
|
27067
|
+
return []
|
|
27068
|
+
if "\x00" in value or value.startswith("/") or value.startswith("//"):
|
|
27069
|
+
raise OSError(errno.EPERM, "invalid relative path")
|
|
27070
|
+
if value.startswith("..\\"):
|
|
27071
|
+
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
27072
|
+
parts = [part for part in value.split("/") if part and part != "."]
|
|
27073
|
+
for part in parts:
|
|
27074
|
+
if part == "..":
|
|
27075
|
+
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
27076
|
+
return parts
|
|
27077
|
+
|
|
27078
|
+
def open_dir(path_value, dir_fd=None):
|
|
27079
|
+
return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
|
|
27080
|
+
|
|
27081
|
+
def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
27082
|
+
current_fd = os.dup(root_fd)
|
|
27083
|
+
try:
|
|
27084
|
+
for segment in segments:
|
|
27085
|
+
try:
|
|
27086
|
+
next_fd = open_dir(segment, dir_fd=current_fd)
|
|
27087
|
+
except FileNotFoundError:
|
|
27088
|
+
if not mkdir_enabled:
|
|
27089
|
+
raise
|
|
27090
|
+
os.mkdir(segment, 0o777, dir_fd=current_fd)
|
|
27091
|
+
next_fd = open_dir(segment, dir_fd=current_fd)
|
|
27092
|
+
os.close(current_fd)
|
|
27093
|
+
current_fd = next_fd
|
|
27094
|
+
return current_fd
|
|
27095
|
+
except Exception:
|
|
27096
|
+
os.close(current_fd)
|
|
27097
|
+
raise
|
|
27098
|
+
|
|
27099
|
+
def parent_and_basename(root_fd, relative):
|
|
27100
|
+
segments = split_relative(relative)
|
|
27101
|
+
if not segments:
|
|
27102
|
+
raise OSError(errno.EPERM, "operation requires a non-root path")
|
|
27103
|
+
parent_fd = walk_dir(root_fd, segments[:-1])
|
|
27104
|
+
return parent_fd, segments[-1]
|
|
27105
|
+
|
|
27106
|
+
def encode_stat(st):
|
|
27107
|
+
mode = st.st_mode
|
|
27108
|
+
return {
|
|
27109
|
+
"dev": st.st_dev,
|
|
27110
|
+
"gid": st.st_gid,
|
|
27111
|
+
"ino": st.st_ino,
|
|
27112
|
+
"isDirectory": stat.S_ISDIR(mode),
|
|
27113
|
+
"isFile": stat.S_ISREG(mode),
|
|
27114
|
+
"isSymbolicLink": stat.S_ISLNK(mode),
|
|
27115
|
+
"mode": mode,
|
|
27116
|
+
"mtimeMs": st.st_mtime * 1000,
|
|
27117
|
+
"nlink": st.st_nlink,
|
|
27118
|
+
"size": st.st_size,
|
|
27119
|
+
"uid": st.st_uid,
|
|
27120
|
+
}
|
|
27121
|
+
|
|
27122
|
+
def reject_unsafe_endpoint(st):
|
|
27123
|
+
mode = st.st_mode
|
|
27124
|
+
if stat.S_ISLNK(mode):
|
|
27125
|
+
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
27126
|
+
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
27127
|
+
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
27128
|
+
|
|
27129
|
+
def stat_path(root_fd, payload):
|
|
27130
|
+
relative = payload.get("relativePath", "")
|
|
27131
|
+
segments = split_relative(relative)
|
|
27132
|
+
if not segments:
|
|
27133
|
+
return encode_stat(os.fstat(root_fd))
|
|
27134
|
+
parent_fd, basename = parent_and_basename(root_fd, relative)
|
|
27135
|
+
try:
|
|
27136
|
+
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27137
|
+
if payload.get("rejectSymlink", True) and stat.S_ISLNK(st.st_mode):
|
|
27138
|
+
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
27139
|
+
return encode_stat(st)
|
|
27140
|
+
finally:
|
|
27141
|
+
os.close(parent_fd)
|
|
27142
|
+
|
|
27143
|
+
def readdir_path(root_fd, payload):
|
|
27144
|
+
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
|
|
27145
|
+
try:
|
|
27146
|
+
names = sorted(os.listdir(dir_fd))
|
|
27147
|
+
if not payload.get("withFileTypes", False):
|
|
27148
|
+
return names
|
|
27149
|
+
entries = []
|
|
27150
|
+
for name in names:
|
|
27151
|
+
st = os.lstat(name, dir_fd=dir_fd)
|
|
27152
|
+
entry = encode_stat(st)
|
|
27153
|
+
entry["name"] = name
|
|
27154
|
+
entries.append(entry)
|
|
27155
|
+
return entries
|
|
27156
|
+
finally:
|
|
27157
|
+
os.close(dir_fd)
|
|
27158
|
+
|
|
27159
|
+
def mkdirp_path(root_fd, payload):
|
|
27160
|
+
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
|
|
27161
|
+
os.close(dir_fd)
|
|
27162
|
+
return None
|
|
27163
|
+
|
|
27164
|
+
def remove_tree(parent_fd, basename):
|
|
27165
|
+
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27166
|
+
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
27167
|
+
dir_fd = open_dir(basename, dir_fd=parent_fd)
|
|
27168
|
+
try:
|
|
27169
|
+
for child in os.listdir(dir_fd):
|
|
27170
|
+
remove_tree(dir_fd, child)
|
|
27171
|
+
finally:
|
|
27172
|
+
os.close(dir_fd)
|
|
27173
|
+
os.rmdir(basename, dir_fd=parent_fd)
|
|
27174
|
+
else:
|
|
27175
|
+
os.unlink(basename, dir_fd=parent_fd)
|
|
27176
|
+
|
|
27177
|
+
def remove_path(root_fd, payload):
|
|
27178
|
+
parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
|
|
27179
|
+
try:
|
|
27180
|
+
try:
|
|
27181
|
+
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27182
|
+
except FileNotFoundError:
|
|
27183
|
+
if payload.get("force", True):
|
|
27184
|
+
return None
|
|
27185
|
+
raise
|
|
27186
|
+
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
27187
|
+
if payload.get("recursive", False):
|
|
27188
|
+
remove_tree(parent_fd, basename)
|
|
27189
|
+
else:
|
|
27190
|
+
os.rmdir(basename, dir_fd=parent_fd)
|
|
27191
|
+
else:
|
|
27192
|
+
os.unlink(basename, dir_fd=parent_fd)
|
|
27193
|
+
return None
|
|
27194
|
+
finally:
|
|
27195
|
+
os.close(parent_fd)
|
|
27196
|
+
|
|
27197
|
+
def rename_path(root_fd, payload):
|
|
27198
|
+
from_parent_fd, from_base = parent_and_basename(root_fd, payload["from"])
|
|
27199
|
+
to_parent_fd, to_base = parent_and_basename(root_fd, payload["to"])
|
|
27200
|
+
try:
|
|
27201
|
+
from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
|
|
27202
|
+
reject_unsafe_endpoint(from_stat)
|
|
27203
|
+
if not payload.get("overwrite", True):
|
|
27204
|
+
try:
|
|
27205
|
+
os.lstat(to_base, dir_fd=to_parent_fd)
|
|
27206
|
+
raise FileExistsError(errno.EEXIST, "destination exists", to_base)
|
|
27207
|
+
except FileNotFoundError:
|
|
27208
|
+
pass
|
|
27209
|
+
os.rename(from_base, to_base, src_dir_fd=from_parent_fd, dst_dir_fd=to_parent_fd)
|
|
27210
|
+
os.fsync(from_parent_fd)
|
|
27211
|
+
if from_parent_fd != to_parent_fd:
|
|
27212
|
+
os.fsync(to_parent_fd)
|
|
27213
|
+
return None
|
|
27214
|
+
finally:
|
|
27215
|
+
os.close(from_parent_fd)
|
|
27216
|
+
os.close(to_parent_fd)
|
|
27217
|
+
|
|
27218
|
+
def create_temp_file(parent_fd, basename, mode):
|
|
27219
|
+
prefix = "." + basename + "."
|
|
27220
|
+
for _ in range(128):
|
|
27221
|
+
candidate = prefix + secrets.token_hex(6) + ".tmp"
|
|
27222
|
+
try:
|
|
27223
|
+
fd = os.open(candidate, WRITE_FLAGS, mode, dir_fd=parent_fd)
|
|
27224
|
+
return candidate, fd
|
|
27225
|
+
except FileExistsError:
|
|
27226
|
+
continue
|
|
27227
|
+
raise RuntimeError("failed to allocate pinned temp file")
|
|
27228
|
+
|
|
27229
|
+
def write_path(root_fd, payload):
|
|
27230
|
+
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
27231
|
+
temp_fd = None
|
|
27232
|
+
temp_name = None
|
|
27233
|
+
basename = payload["basename"]
|
|
27234
|
+
mode = int(payload.get("mode", 0o600))
|
|
27235
|
+
overwrite = bool(payload.get("overwrite", True))
|
|
27236
|
+
max_bytes = int(payload.get("maxBytes", -1))
|
|
27237
|
+
data = base64.b64decode(payload.get("base64", ""))
|
|
27238
|
+
try:
|
|
27239
|
+
if max_bytes >= 0 and len(data) > max_bytes:
|
|
27240
|
+
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, len(data)))
|
|
27241
|
+
if not overwrite:
|
|
27242
|
+
try:
|
|
27243
|
+
os.lstat(basename, dir_fd=parent_fd)
|
|
27244
|
+
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
27245
|
+
except FileNotFoundError:
|
|
27246
|
+
pass
|
|
27247
|
+
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
27248
|
+
view = memoryview(data)
|
|
27249
|
+
while view:
|
|
27250
|
+
written = os.write(temp_fd, view)
|
|
27251
|
+
if written <= 0:
|
|
27252
|
+
raise OSError(errno.EIO, "short write")
|
|
27253
|
+
view = view[written:]
|
|
27254
|
+
os.fsync(temp_fd)
|
|
27255
|
+
os.close(temp_fd)
|
|
27256
|
+
temp_fd = None
|
|
27257
|
+
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
27258
|
+
temp_name = None
|
|
27259
|
+
os.fsync(parent_fd)
|
|
27260
|
+
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
27261
|
+
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
27262
|
+
finally:
|
|
27263
|
+
if temp_fd is not None:
|
|
27264
|
+
os.close(temp_fd)
|
|
27265
|
+
if temp_name is not None:
|
|
27266
|
+
try:
|
|
27267
|
+
os.unlink(temp_name, dir_fd=parent_fd)
|
|
27268
|
+
except FileNotFoundError:
|
|
27269
|
+
pass
|
|
27270
|
+
os.close(parent_fd)
|
|
27271
|
+
|
|
27272
|
+
def copy_path(root_fd, payload):
|
|
27273
|
+
source_fd = os.open(payload["sourcePath"], READ_FLAGS)
|
|
27274
|
+
parent_fd = None
|
|
27275
|
+
temp_fd = None
|
|
27276
|
+
temp_name = None
|
|
27277
|
+
try:
|
|
27278
|
+
source_stat = os.fstat(source_fd)
|
|
27279
|
+
if not stat.S_ISREG(source_stat.st_mode):
|
|
27280
|
+
raise RuntimeError("fs-safe-not-file")
|
|
27281
|
+
if source_stat.st_dev != int(payload["sourceDev"]) or source_stat.st_ino != int(payload["sourceIno"]):
|
|
27282
|
+
raise RuntimeError("fs-safe-source-mismatch")
|
|
27283
|
+
basename = payload["basename"]
|
|
27284
|
+
mode = int(payload.get("mode", 0o600))
|
|
27285
|
+
overwrite = bool(payload.get("overwrite", True))
|
|
27286
|
+
max_bytes = int(payload.get("maxBytes", -1))
|
|
27287
|
+
if max_bytes >= 0 and source_stat.st_size > max_bytes:
|
|
27288
|
+
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
27289
|
+
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
27290
|
+
if not overwrite:
|
|
27291
|
+
try:
|
|
27292
|
+
os.lstat(basename, dir_fd=parent_fd)
|
|
27293
|
+
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
27294
|
+
except FileNotFoundError:
|
|
27295
|
+
pass
|
|
27296
|
+
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
27297
|
+
written_bytes = 0
|
|
27298
|
+
while True:
|
|
27299
|
+
chunk = os.read(source_fd, 65536)
|
|
27300
|
+
if not chunk:
|
|
27301
|
+
break
|
|
27302
|
+
written_bytes += len(chunk)
|
|
27303
|
+
if max_bytes >= 0 and written_bytes > max_bytes:
|
|
27304
|
+
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, written_bytes))
|
|
27305
|
+
view = memoryview(chunk)
|
|
27306
|
+
while view:
|
|
27307
|
+
written = os.write(temp_fd, view)
|
|
27308
|
+
if written <= 0:
|
|
27309
|
+
raise OSError(errno.EIO, "short write")
|
|
27310
|
+
view = view[written:]
|
|
27311
|
+
os.fsync(temp_fd)
|
|
27312
|
+
os.close(temp_fd)
|
|
27313
|
+
temp_fd = None
|
|
27314
|
+
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
27315
|
+
temp_name = None
|
|
27316
|
+
os.fsync(parent_fd)
|
|
27317
|
+
result_stat = os.stat(basename, dir_fd=parent_fd, follow_symlinks=False)
|
|
27318
|
+
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
27319
|
+
finally:
|
|
27320
|
+
os.close(source_fd)
|
|
27321
|
+
if temp_fd is not None:
|
|
27322
|
+
os.close(temp_fd)
|
|
27323
|
+
if temp_name is not None and parent_fd is not None:
|
|
27324
|
+
try:
|
|
27325
|
+
os.unlink(temp_name, dir_fd=parent_fd)
|
|
27326
|
+
except FileNotFoundError:
|
|
27327
|
+
pass
|
|
27328
|
+
if parent_fd is not None:
|
|
27329
|
+
os.close(parent_fd)
|
|
27330
|
+
|
|
27331
|
+
def run_operation(operation, root_path, payload):
|
|
27332
|
+
root_fd = open_dir(root_path)
|
|
27333
|
+
try:
|
|
27334
|
+
if operation == "stat":
|
|
27335
|
+
return stat_path(root_fd, payload)
|
|
27336
|
+
if operation == "readdir":
|
|
27337
|
+
return readdir_path(root_fd, payload)
|
|
27338
|
+
if operation == "mkdirp":
|
|
27339
|
+
return mkdirp_path(root_fd, payload)
|
|
27340
|
+
if operation == "remove":
|
|
27341
|
+
return remove_path(root_fd, payload)
|
|
27342
|
+
if operation == "rename":
|
|
27343
|
+
return rename_path(root_fd, payload)
|
|
27344
|
+
if operation == "write":
|
|
27345
|
+
return write_path(root_fd, payload)
|
|
27346
|
+
if operation == "copy":
|
|
27347
|
+
return copy_path(root_fd, payload)
|
|
27348
|
+
raise RuntimeError("unknown operation: " + operation)
|
|
27349
|
+
finally:
|
|
27350
|
+
os.close(root_fd)
|
|
27351
|
+
|
|
27352
|
+
for line in sys.stdin:
|
|
27353
|
+
try:
|
|
27354
|
+
request = json.loads(line)
|
|
27355
|
+
result = run_operation(request["operation"], request["rootPath"], request.get("payload") or {})
|
|
27356
|
+
response = {"id": request["id"], "ok": True, "result": result}
|
|
27357
|
+
except Exception as exc:
|
|
27358
|
+
response = {
|
|
27359
|
+
"id": request.get("id") if isinstance(locals().get("request"), dict) else None,
|
|
27360
|
+
"ok": False,
|
|
27361
|
+
"code": exc.__class__.__name__,
|
|
27362
|
+
"errno": getattr(exc, "errno", None),
|
|
27363
|
+
"message": str(exc),
|
|
27364
|
+
}
|
|
27365
|
+
print(json.dumps(response, separators=(",", ":")), flush=True)
|
|
27366
|
+
`;
|
|
27367
|
+
let nextRequestId = 1;
|
|
27368
|
+
let worker = null;
|
|
27369
|
+
function __resetPinnedPythonWorkerForTest() {
|
|
27370
|
+
const currentWorker = worker;
|
|
27371
|
+
worker = null;
|
|
27372
|
+
if (!currentWorker) {
|
|
27373
|
+
return;
|
|
27374
|
+
}
|
|
27375
|
+
currentWorker.pending.clear();
|
|
27376
|
+
currentWorker.child.kill("SIGTERM");
|
|
27377
|
+
}
|
|
27378
|
+
const PYTHON_CANDIDATE_DEFAULTS = [
|
|
27379
|
+
"/usr/bin/python3",
|
|
27380
|
+
"/opt/homebrew/bin/python3",
|
|
27381
|
+
"/usr/local/bin/python3",
|
|
27382
|
+
];
|
|
27383
|
+
function canExecute(binPath) {
|
|
27384
|
+
try {
|
|
27385
|
+
external_node_fs_.accessSync(binPath, external_node_fs_.constants.X_OK);
|
|
27386
|
+
return true;
|
|
27387
|
+
}
|
|
27388
|
+
catch {
|
|
27389
|
+
return false;
|
|
27390
|
+
}
|
|
27391
|
+
}
|
|
27392
|
+
function resolvePython() {
|
|
27393
|
+
const configured = getFsSafePythonConfig().pythonPath;
|
|
27394
|
+
if (configured) {
|
|
27395
|
+
return configured;
|
|
27396
|
+
}
|
|
27397
|
+
for (const candidate of PYTHON_CANDIDATE_DEFAULTS) {
|
|
27398
|
+
if (canExecute(candidate)) {
|
|
27399
|
+
return candidate;
|
|
27400
|
+
}
|
|
27401
|
+
}
|
|
27402
|
+
return "python3";
|
|
27403
|
+
}
|
|
27404
|
+
function assertPinnedHelperSupported() {
|
|
27405
|
+
if (process.platform === "win32") {
|
|
27406
|
+
throw new errors_FsSafeError("unsupported-platform", "fd-relative pinned filesystem operations are not available on Windows");
|
|
27407
|
+
}
|
|
27408
|
+
if (getFsSafePythonConfig().mode === "off") {
|
|
27409
|
+
throw new errors_FsSafeError("helper-unavailable", "Python helper is disabled");
|
|
27410
|
+
}
|
|
27411
|
+
}
|
|
27412
|
+
function isSpawnUnavailable(error) {
|
|
27413
|
+
if (!(error instanceof Error)) {
|
|
27414
|
+
return false;
|
|
27415
|
+
}
|
|
27416
|
+
const maybeErrno = error;
|
|
27417
|
+
return (typeof maybeErrno.syscall === "string" &&
|
|
27418
|
+
maybeErrno.syscall.startsWith("spawn") &&
|
|
27419
|
+
["EACCES", "ENOENT", "ENOEXEC"].includes(maybeErrno.code ?? ""));
|
|
27420
|
+
}
|
|
27421
|
+
function mapWorkerError(response) {
|
|
27422
|
+
const code = typeof response.code === "string" ? response.code : "";
|
|
27423
|
+
const errno = typeof response.errno === "number" ? response.errno : undefined;
|
|
27424
|
+
const message = typeof response.message === "string" && response.message
|
|
27425
|
+
? response.message
|
|
27426
|
+
: "pinned helper failed";
|
|
27427
|
+
const tooLarge = message.match(/fs-safe-too-large:(\d+):(\d+)/);
|
|
27428
|
+
if (tooLarge) {
|
|
27429
|
+
const [, limit, got] = tooLarge;
|
|
27430
|
+
return new errors_FsSafeError("too-large", `file exceeds limit of ${limit} bytes (got at least ${got})`);
|
|
27431
|
+
}
|
|
27432
|
+
if (message.includes("fs-safe-not-file")) {
|
|
27433
|
+
return new errors_FsSafeError("not-file", "not a file");
|
|
27434
|
+
}
|
|
27435
|
+
if (message.includes("fs-safe-source-mismatch")) {
|
|
27436
|
+
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
27437
|
+
}
|
|
27438
|
+
if (code === "FileNotFoundError" || errno === 2) {
|
|
27439
|
+
return new errors_FsSafeError("not-found", "file not found");
|
|
27440
|
+
}
|
|
27441
|
+
if (code === "FileExistsError" || errno === 17) {
|
|
27442
|
+
return new errors_FsSafeError("already-exists", message);
|
|
27443
|
+
}
|
|
27444
|
+
if (errno === 39) {
|
|
27445
|
+
return new errors_FsSafeError("not-empty", "directory is not empty");
|
|
27446
|
+
}
|
|
27447
|
+
if (errno === 1 || errno === 13 || errno === 21) {
|
|
27448
|
+
return new errors_FsSafeError("not-removable", "path is not removable under root");
|
|
27449
|
+
}
|
|
27450
|
+
if (code === "NotADirectoryError" || code === "OSError" || errno === 20 || errno === 40) {
|
|
27451
|
+
return new errors_FsSafeError("path-alias", message);
|
|
27452
|
+
}
|
|
27453
|
+
return new errors_FsSafeError("helper-failed", message);
|
|
27454
|
+
}
|
|
27455
|
+
function rejectPending(error) {
|
|
27456
|
+
if (!worker) {
|
|
27457
|
+
return;
|
|
27458
|
+
}
|
|
27459
|
+
setWorkerRef(worker, false);
|
|
27460
|
+
for (const pending of worker.pending.values()) {
|
|
27461
|
+
pending.reject(error);
|
|
27462
|
+
}
|
|
27463
|
+
worker.pending.clear();
|
|
27464
|
+
worker = null;
|
|
27465
|
+
}
|
|
27466
|
+
function handleWorkerLine(line) {
|
|
27467
|
+
if (!worker || !line.trim()) {
|
|
27468
|
+
return;
|
|
27469
|
+
}
|
|
27470
|
+
let decoded;
|
|
27471
|
+
try {
|
|
27472
|
+
decoded = JSON.parse(line);
|
|
27473
|
+
}
|
|
27474
|
+
catch {
|
|
27475
|
+
rejectPending(new errors_FsSafeError("helper-failed", `pinned helper returned invalid JSON: ${line}`));
|
|
27476
|
+
return;
|
|
27477
|
+
}
|
|
27478
|
+
if (typeof decoded !== "object" || decoded === null || !("id" in decoded)) {
|
|
27479
|
+
rejectPending(new errors_FsSafeError("helper-failed", "pinned helper returned invalid response"));
|
|
27480
|
+
return;
|
|
27481
|
+
}
|
|
27482
|
+
const response = decoded;
|
|
27483
|
+
const id = typeof response.id === "number" ? response.id : undefined;
|
|
27484
|
+
if (id === undefined) {
|
|
27485
|
+
return;
|
|
27486
|
+
}
|
|
27487
|
+
const pending = worker.pending.get(id);
|
|
27488
|
+
if (!pending) {
|
|
27489
|
+
return;
|
|
27490
|
+
}
|
|
27491
|
+
worker.pending.delete(id);
|
|
27492
|
+
if (worker.pending.size === 0) {
|
|
27493
|
+
setWorkerRef(worker, false);
|
|
27494
|
+
}
|
|
27495
|
+
if (response.ok === true) {
|
|
27496
|
+
pending.resolve(response.result);
|
|
27497
|
+
return;
|
|
27498
|
+
}
|
|
27499
|
+
pending.reject(mapWorkerError(decoded));
|
|
27500
|
+
}
|
|
27501
|
+
function getWorker() {
|
|
27502
|
+
assertPinnedHelperSupported();
|
|
27503
|
+
if (worker) {
|
|
27504
|
+
return worker;
|
|
27505
|
+
}
|
|
27506
|
+
const child = (0,external_node_child_process_.spawn)(resolvePython(), ["-u", "-c", PINNED_PYTHON_WORKER_SOURCE], {
|
|
27507
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
27508
|
+
});
|
|
27509
|
+
worker = { child, pending: new Map(), stderr: "", stdoutBuffer: "" };
|
|
27510
|
+
child.stdout.setEncoding("utf8");
|
|
27511
|
+
child.stderr.setEncoding("utf8");
|
|
27512
|
+
child.stdout.on("data", (chunk) => {
|
|
27513
|
+
const current = worker;
|
|
27514
|
+
if (!current) {
|
|
27515
|
+
return;
|
|
27516
|
+
}
|
|
27517
|
+
current.stdoutBuffer += chunk;
|
|
27518
|
+
for (;;) {
|
|
27519
|
+
const newline = current.stdoutBuffer.indexOf("\n");
|
|
27520
|
+
if (newline < 0) {
|
|
27521
|
+
break;
|
|
27522
|
+
}
|
|
27523
|
+
const line = current.stdoutBuffer.slice(0, newline);
|
|
27524
|
+
current.stdoutBuffer = current.stdoutBuffer.slice(newline + 1);
|
|
27525
|
+
handleWorkerLine(line);
|
|
27526
|
+
}
|
|
27527
|
+
});
|
|
27528
|
+
child.stderr.on("data", (chunk) => {
|
|
27529
|
+
if (worker) {
|
|
27530
|
+
worker.stderr = `${worker.stderr}${chunk}`.slice(-4096);
|
|
27531
|
+
}
|
|
27532
|
+
});
|
|
27533
|
+
child.once("error", (error) => {
|
|
27534
|
+
const mapped = isSpawnUnavailable(error)
|
|
27535
|
+
? new errors_FsSafeError("helper-unavailable", "Python helper is unavailable", { cause: error })
|
|
27536
|
+
: error instanceof Error
|
|
27537
|
+
? error
|
|
27538
|
+
: new Error(String(error));
|
|
27539
|
+
rejectPending(mapped);
|
|
27540
|
+
});
|
|
27541
|
+
child.once("close", (code, signal) => {
|
|
27542
|
+
const stderr = worker?.stderr.trim();
|
|
27543
|
+
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`));
|
|
27544
|
+
});
|
|
27545
|
+
process.once("exit", () => {
|
|
27546
|
+
child.kill("SIGTERM");
|
|
27547
|
+
});
|
|
27548
|
+
setWorkerRef(worker, false);
|
|
27549
|
+
return worker;
|
|
27550
|
+
}
|
|
27551
|
+
function setRefable(value, ref) {
|
|
27552
|
+
if (!value) {
|
|
27553
|
+
return;
|
|
27554
|
+
}
|
|
27555
|
+
const method = ref ? "ref" : "unref";
|
|
27556
|
+
const refable = value;
|
|
27557
|
+
refable[method]?.();
|
|
27558
|
+
}
|
|
27559
|
+
function setWorkerRef(currentWorker, ref) {
|
|
27560
|
+
setRefable(currentWorker.child, ref);
|
|
27561
|
+
setRefable(currentWorker.child.stdin, ref);
|
|
27562
|
+
setRefable(currentWorker.child.stdout, ref);
|
|
27563
|
+
setRefable(currentWorker.child.stderr, ref);
|
|
27564
|
+
}
|
|
27565
|
+
async function runPinnedPythonOperation(params) {
|
|
27566
|
+
const requestId = nextRequestId++;
|
|
27567
|
+
const currentWorker = getWorker();
|
|
27568
|
+
if (typeof currentWorker.child.stdin?.write !== "function") {
|
|
27569
|
+
throw new errors_FsSafeError("helper-unavailable", "Python helper stdin is unavailable");
|
|
27570
|
+
}
|
|
27571
|
+
setWorkerRef(currentWorker, true);
|
|
27572
|
+
return await new Promise((resolve, reject) => {
|
|
27573
|
+
currentWorker.pending.set(requestId, {
|
|
27574
|
+
reject,
|
|
27575
|
+
resolve: (value) => resolve(value),
|
|
27576
|
+
});
|
|
27577
|
+
const request = JSON.stringify({
|
|
27578
|
+
id: requestId,
|
|
27579
|
+
operation: params.operation,
|
|
27580
|
+
rootPath: params.rootPath,
|
|
27581
|
+
payload: params.payload,
|
|
27582
|
+
});
|
|
27583
|
+
currentWorker.child.stdin.write(`${request}\n`, (error) => {
|
|
27584
|
+
if (error) {
|
|
27585
|
+
currentWorker.pending.delete(requestId);
|
|
27586
|
+
if (currentWorker.pending.size === 0) {
|
|
27587
|
+
setWorkerRef(currentWorker, false);
|
|
27588
|
+
}
|
|
27589
|
+
reject(error);
|
|
27590
|
+
}
|
|
27591
|
+
});
|
|
27592
|
+
});
|
|
27593
|
+
}
|
|
27594
|
+
function assertPinnedPythonOperationAvailable() {
|
|
27595
|
+
const currentWorker = getWorker();
|
|
27596
|
+
if (typeof currentWorker.child.stdin?.write !== "function") {
|
|
27597
|
+
throw new errors_FsSafeError("helper-unavailable", "Python helper stdin is unavailable");
|
|
27598
|
+
}
|
|
27599
|
+
}
|
|
27600
|
+
function validatePinnedOperationPayload(payload) {
|
|
27601
|
+
if (typeof payload.relativePath === "string") {
|
|
27602
|
+
validatePinnedRelativePath(payload.relativePath);
|
|
27603
|
+
}
|
|
27604
|
+
if (typeof payload.relativeParentPath === "string") {
|
|
27605
|
+
validatePinnedRelativePath(payload.relativeParentPath);
|
|
27606
|
+
}
|
|
27607
|
+
if (typeof payload.from === "string") {
|
|
27608
|
+
validatePinnedRelativePath(payload.from);
|
|
27609
|
+
}
|
|
27610
|
+
if (typeof payload.to === "string") {
|
|
27611
|
+
validatePinnedRelativePath(payload.to);
|
|
27612
|
+
}
|
|
27613
|
+
}
|
|
27614
|
+
function isPinnedHelperUnavailable(error) {
|
|
27615
|
+
return error instanceof Error &&
|
|
27616
|
+
"code" in error &&
|
|
27617
|
+
error.code === "helper-unavailable";
|
|
27618
|
+
}
|
|
27619
|
+
function validatePinnedRelativePath(relativePath) {
|
|
27620
|
+
if (relativePath.length === 0 || relativePath === ".") {
|
|
27621
|
+
return;
|
|
27622
|
+
}
|
|
27623
|
+
if (relativePath.includes("\0")) {
|
|
27624
|
+
throw new errors_FsSafeError("invalid-path", "relative path contains a NUL byte");
|
|
27625
|
+
}
|
|
27626
|
+
if (relativePath.startsWith("/") ||
|
|
27627
|
+
relativePath.startsWith("//") ||
|
|
27628
|
+
relativePath === ".." ||
|
|
27629
|
+
relativePath.startsWith("../") ||
|
|
27630
|
+
relativePath.startsWith("..\\")) {
|
|
27631
|
+
throw new errors_FsSafeError("invalid-path", "relative path must not escape root");
|
|
27632
|
+
}
|
|
27633
|
+
for (const segment of relativePath.split("/")) {
|
|
27634
|
+
if (segment === "..") {
|
|
27635
|
+
throw new errors_FsSafeError("invalid-path", "relative path must not contain '..'");
|
|
27636
|
+
}
|
|
27637
|
+
}
|
|
27638
|
+
}
|
|
27639
|
+
|
|
27640
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-helper.js
|
|
27641
|
+
|
|
27642
|
+
|
|
27643
|
+
async function runPinnedHelper(operation, rootDir, payload) {
|
|
27644
|
+
validatePinnedOperationPayload(payload);
|
|
27645
|
+
return await runPinnedPythonOperation({
|
|
27646
|
+
operation,
|
|
27647
|
+
rootPath: rootDir,
|
|
27648
|
+
payload,
|
|
27649
|
+
});
|
|
27650
|
+
}
|
|
27651
|
+
async function helperStat(rootDir, relativePath) {
|
|
27652
|
+
return await runPinnedHelper("stat", rootDir, { relativePath });
|
|
27653
|
+
}
|
|
27654
|
+
async function helperReaddir(rootDir, relativePath, withFileTypes) {
|
|
27655
|
+
return await runPinnedHelper("readdir", rootDir, {
|
|
27656
|
+
relativePath,
|
|
27657
|
+
withFileTypes,
|
|
27658
|
+
});
|
|
27659
|
+
}
|
|
27660
|
+
|
|
27661
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-path.js
|
|
27662
|
+
|
|
27663
|
+
|
|
27664
|
+
|
|
27665
|
+
function isPinnedPathHelperSpawnError(error) {
|
|
27666
|
+
return canFallbackFromPythonError(error);
|
|
27667
|
+
}
|
|
27668
|
+
async function runPinnedPathHelper(params) {
|
|
27669
|
+
try {
|
|
27670
|
+
await runPinnedHelper(params.operation, params.rootPath, {
|
|
27671
|
+
relativePath: params.relativePath,
|
|
27672
|
+
});
|
|
27673
|
+
}
|
|
27674
|
+
catch (error) {
|
|
27675
|
+
if (error instanceof errors_FsSafeError) {
|
|
27676
|
+
throw error;
|
|
27677
|
+
}
|
|
27678
|
+
throw new errors_FsSafeError("helper-failed", "pinned path helper failed", {
|
|
27679
|
+
cause: error instanceof Error ? error : undefined,
|
|
27680
|
+
});
|
|
27681
|
+
}
|
|
27682
|
+
}
|
|
27683
|
+
|
|
27684
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-write.js
|
|
27685
|
+
|
|
27686
|
+
|
|
27687
|
+
|
|
27688
|
+
|
|
27689
|
+
|
|
27690
|
+
|
|
27691
|
+
|
|
27692
|
+
|
|
27693
|
+
|
|
27694
|
+
|
|
27695
|
+
|
|
27696
|
+
function pinned_write_byteLength(input, encoding) {
|
|
27697
|
+
return typeof input === "string"
|
|
27698
|
+
? Buffer.byteLength(input, encoding ?? "utf8")
|
|
27699
|
+
: input.byteLength;
|
|
27700
|
+
}
|
|
27701
|
+
function assertSafeBasename(basename) {
|
|
27702
|
+
if (!basename ||
|
|
27703
|
+
basename === "." ||
|
|
27704
|
+
basename === ".." ||
|
|
27705
|
+
basename.includes("/") ||
|
|
27706
|
+
basename.includes("\0")) {
|
|
27707
|
+
throw new errors_FsSafeError("invalid-path", "invalid target path");
|
|
27708
|
+
}
|
|
27709
|
+
}
|
|
27710
|
+
function assertWithinMaxBytes(bytes, maxBytes) {
|
|
27711
|
+
if (maxBytes !== undefined && bytes > maxBytes) {
|
|
27712
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
27713
|
+
}
|
|
27714
|
+
}
|
|
27715
|
+
function pinned_write_createMaxBytesTransform(maxBytes) {
|
|
27716
|
+
if (maxBytes === undefined) {
|
|
27717
|
+
return undefined;
|
|
27718
|
+
}
|
|
27719
|
+
let bytes = 0;
|
|
27720
|
+
return new external_node_stream_.Transform({
|
|
27721
|
+
transform(chunk, _encoding, callback) {
|
|
27722
|
+
bytes += chunk.byteLength;
|
|
27723
|
+
if (bytes > maxBytes) {
|
|
27724
|
+
callback(new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
|
|
27725
|
+
return;
|
|
27726
|
+
}
|
|
27727
|
+
callback(null, chunk);
|
|
27728
|
+
},
|
|
27729
|
+
});
|
|
27730
|
+
}
|
|
27731
|
+
async function pipelineWithMaxBytes(stream, destination, maxBytes) {
|
|
27732
|
+
const limiter = pinned_write_createMaxBytesTransform(maxBytes);
|
|
27733
|
+
if (limiter) {
|
|
27734
|
+
await (0,external_node_stream_promises_.pipeline)(stream, limiter, destination);
|
|
27735
|
+
return;
|
|
27736
|
+
}
|
|
27737
|
+
await (0,external_node_stream_promises_.pipeline)(stream, destination);
|
|
27738
|
+
}
|
|
27739
|
+
async function inputToBase64(input, maxBytes) {
|
|
27740
|
+
if (input.kind === "buffer") {
|
|
27741
|
+
assertWithinMaxBytes(pinned_write_byteLength(input.data, input.encoding), maxBytes);
|
|
27742
|
+
return (typeof input.data === "string"
|
|
27743
|
+
? Buffer.from(input.data, input.encoding ?? "utf8")
|
|
27744
|
+
: input.data).toString("base64");
|
|
27745
|
+
}
|
|
27746
|
+
const chunks = [];
|
|
27747
|
+
let bytes = 0;
|
|
27748
|
+
for await (const chunk of input.stream) {
|
|
27749
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
27750
|
+
bytes += buffer.byteLength;
|
|
27751
|
+
assertWithinMaxBytes(bytes, maxBytes);
|
|
27752
|
+
chunks.push(buffer);
|
|
27753
|
+
}
|
|
27754
|
+
return Buffer.concat(chunks, bytes).toString("base64");
|
|
27755
|
+
}
|
|
27756
|
+
async function runPinnedWriteHelper(params) {
|
|
27757
|
+
assertSafeBasename(params.basename);
|
|
27758
|
+
validatePinnedOperationPayload({
|
|
27759
|
+
relativeParentPath: params.relativeParentPath,
|
|
27760
|
+
});
|
|
27761
|
+
if (getFsSafePythonConfig().mode === "off") {
|
|
27762
|
+
return await runPinnedWriteFallback(params);
|
|
27763
|
+
}
|
|
27764
|
+
if (params.input.kind === "stream") {
|
|
27765
|
+
try {
|
|
27766
|
+
assertPinnedPythonOperationAvailable();
|
|
27767
|
+
}
|
|
27768
|
+
catch (error) {
|
|
27769
|
+
if (canFallbackFromPythonError(error)) {
|
|
27770
|
+
return await runPinnedWriteFallback(params);
|
|
27771
|
+
}
|
|
27772
|
+
throw error;
|
|
27773
|
+
}
|
|
27774
|
+
}
|
|
27775
|
+
const payload = {
|
|
27776
|
+
base64: await inputToBase64(params.input, params.maxBytes),
|
|
27777
|
+
basename: params.basename,
|
|
27778
|
+
maxBytes: params.maxBytes ?? -1,
|
|
27779
|
+
mkdir: params.mkdir,
|
|
27780
|
+
mode: params.mode || 0o600,
|
|
27781
|
+
overwrite: params.overwrite !== false,
|
|
27782
|
+
relativeParentPath: params.relativeParentPath,
|
|
27783
|
+
};
|
|
27784
|
+
try {
|
|
27785
|
+
return await runPinnedPythonOperation({
|
|
27786
|
+
operation: "write",
|
|
27787
|
+
rootPath: params.rootPath,
|
|
27788
|
+
payload,
|
|
27789
|
+
});
|
|
27790
|
+
}
|
|
27791
|
+
catch (error) {
|
|
27792
|
+
if (canFallbackFromPythonError(error)) {
|
|
27793
|
+
return await runPinnedWriteFallback(params);
|
|
27794
|
+
}
|
|
27795
|
+
throw error;
|
|
27796
|
+
}
|
|
27797
|
+
}
|
|
27798
|
+
async function runPinnedCopyHelper(params) {
|
|
27799
|
+
assertSafeBasename(params.basename);
|
|
27800
|
+
validatePinnedOperationPayload({
|
|
27801
|
+
relativeParentPath: params.relativeParentPath,
|
|
27802
|
+
});
|
|
27803
|
+
return await runPinnedPythonOperation({
|
|
27804
|
+
operation: "copy",
|
|
27805
|
+
rootPath: params.rootPath,
|
|
27806
|
+
payload: {
|
|
27807
|
+
basename: params.basename,
|
|
27808
|
+
maxBytes: params.maxBytes ?? -1,
|
|
27809
|
+
mkdir: params.mkdir,
|
|
27810
|
+
mode: params.mode || 0o600,
|
|
27811
|
+
overwrite: params.overwrite !== false,
|
|
27812
|
+
relativeParentPath: params.relativeParentPath,
|
|
27813
|
+
sourceDev: params.sourceIdentity.dev,
|
|
27814
|
+
sourceIno: params.sourceIdentity.ino,
|
|
27815
|
+
sourcePath: params.sourcePath,
|
|
27816
|
+
},
|
|
27817
|
+
});
|
|
27818
|
+
}
|
|
27819
|
+
async function runPinnedWriteFallback(params) {
|
|
27820
|
+
const parentPath = params.relativeParentPath
|
|
27821
|
+
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
27822
|
+
: params.rootPath;
|
|
27823
|
+
const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(params.rootPath, parentPath);
|
|
27824
|
+
if (params.mkdir) {
|
|
27825
|
+
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
27826
|
+
await promises_.mkdir(parentPath, { recursive: true });
|
|
27827
|
+
});
|
|
27828
|
+
}
|
|
27829
|
+
const targetPath = external_node_path_.join(parentPath, params.basename);
|
|
27830
|
+
if (params.overwrite === false) {
|
|
27831
|
+
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), {
|
|
27832
|
+
onPostGuardFailure: async (openedHandle) => {
|
|
27833
|
+
// The parent failed verification, so targetPath may now resolve
|
|
27834
|
+
// somewhere else. Close the fd, but do not clean up by path.
|
|
27835
|
+
await openedHandle.close().catch(() => undefined);
|
|
27836
|
+
},
|
|
27837
|
+
});
|
|
27838
|
+
let created = true;
|
|
27839
|
+
try {
|
|
27840
|
+
if (params.input.kind === "buffer") {
|
|
27841
|
+
assertWithinMaxBytes(pinned_write_byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
27842
|
+
if (typeof params.input.data === "string") {
|
|
27843
|
+
await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
|
|
27844
|
+
}
|
|
27845
|
+
else {
|
|
27846
|
+
await handle.writeFile(params.input.data);
|
|
27847
|
+
}
|
|
27848
|
+
}
|
|
27849
|
+
else {
|
|
27850
|
+
await pipelineWithMaxBytes(params.input.stream, handle.createWriteStream(), params.maxBytes);
|
|
27851
|
+
}
|
|
27852
|
+
const stat = await handle.stat();
|
|
27853
|
+
created = false;
|
|
27854
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
27855
|
+
}
|
|
27856
|
+
finally {
|
|
27857
|
+
await handle.close().catch(() => undefined);
|
|
27858
|
+
if (created) {
|
|
27859
|
+
await promises_.rm(targetPath, { force: true }).catch(() => undefined);
|
|
27860
|
+
}
|
|
27861
|
+
}
|
|
27862
|
+
}
|
|
27863
|
+
const tempPath = external_node_path_.join(parentPath, `.${params.basename}.${(0,external_node_crypto_.randomUUID)()}.fallback.tmp`);
|
|
27864
|
+
const tempFlags = external_node_fs_.constants.O_WRONLY |
|
|
27865
|
+
external_node_fs_.constants.O_CREAT |
|
|
27866
|
+
external_node_fs_.constants.O_EXCL |
|
|
27867
|
+
(process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants
|
|
27868
|
+
? external_node_fs_.constants.O_NOFOLLOW
|
|
27869
|
+
: 0);
|
|
27870
|
+
let handle;
|
|
27871
|
+
let handleClosedByStream = false;
|
|
27872
|
+
try {
|
|
27873
|
+
handle = await promises_.open(tempPath, tempFlags, params.mode);
|
|
27874
|
+
if (params.input.kind === "buffer") {
|
|
27875
|
+
assertWithinMaxBytes(pinned_write_byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
27876
|
+
if (typeof params.input.data === "string") {
|
|
27877
|
+
await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
|
|
27878
|
+
}
|
|
27879
|
+
else {
|
|
27880
|
+
await handle.writeFile(params.input.data);
|
|
27881
|
+
}
|
|
27882
|
+
}
|
|
27883
|
+
else {
|
|
27884
|
+
const writable = handle.createWriteStream();
|
|
27885
|
+
writable.once("close", () => {
|
|
27886
|
+
handleClosedByStream = true;
|
|
27887
|
+
});
|
|
27888
|
+
await pipelineWithMaxBytes(params.input.stream, writable, params.maxBytes);
|
|
27889
|
+
}
|
|
27890
|
+
if (!handleClosedByStream) {
|
|
27891
|
+
await handle.close().catch(() => undefined);
|
|
27892
|
+
handle = undefined;
|
|
27893
|
+
}
|
|
27894
|
+
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
27895
|
+
await promises_.rename(tempPath, targetPath);
|
|
27896
|
+
});
|
|
27897
|
+
}
|
|
27898
|
+
catch (error) {
|
|
27899
|
+
if (handle && !handleClosedByStream) {
|
|
27900
|
+
await handle.close().catch(() => undefined);
|
|
27901
|
+
}
|
|
27902
|
+
await promises_.rm(tempPath, { force: true }).catch(() => undefined);
|
|
27903
|
+
throw error;
|
|
27904
|
+
}
|
|
27905
|
+
const stat = await promises_.stat(targetPath);
|
|
27906
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
27907
|
+
}
|
|
27908
|
+
|
|
27909
|
+
// EXTERNAL MODULE: external "node:os"
|
|
27910
|
+
var external_node_os_ = __webpack_require__(8161);
|
|
27911
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path.js
|
|
27912
|
+
|
|
27913
|
+
|
|
27914
|
+
|
|
27915
|
+
|
|
27916
|
+
|
|
27917
|
+
const ROOT_PATH_ALIAS_POLICIES = {
|
|
27918
|
+
strict: Object.freeze({
|
|
27919
|
+
allowFinalSymlinkForUnlink: false,
|
|
27920
|
+
allowFinalHardlinkForUnlink: false,
|
|
27921
|
+
}),
|
|
27922
|
+
unlinkTarget: Object.freeze({
|
|
27923
|
+
allowFinalSymlinkForUnlink: true,
|
|
27924
|
+
allowFinalHardlinkForUnlink: true,
|
|
27925
|
+
}),
|
|
27926
|
+
};
|
|
27927
|
+
async function resolveRootPath(params) {
|
|
27928
|
+
const rootPath = external_node_path_.resolve(params.rootPath);
|
|
27929
|
+
const absolutePath = external_node_path_.resolve(params.absolutePath);
|
|
27930
|
+
const rootCanonicalPath = params.rootCanonicalPath
|
|
27931
|
+
? external_node_path_.resolve(params.rootCanonicalPath)
|
|
27932
|
+
: await resolvePathViaExistingAncestor(rootPath);
|
|
27933
|
+
const context = createBoundaryResolutionContext({
|
|
27934
|
+
resolveParams: params,
|
|
27935
|
+
rootPath,
|
|
27936
|
+
absolutePath,
|
|
27937
|
+
rootCanonicalPath,
|
|
27938
|
+
outsideLexicalCanonicalPath: await resolveOutsideLexicalCanonicalPathAsync({
|
|
27939
|
+
rootPath,
|
|
27940
|
+
absolutePath,
|
|
27941
|
+
}),
|
|
27942
|
+
});
|
|
27943
|
+
const outsideResult = await resolveOutsideRootPathAsync({
|
|
27944
|
+
boundaryLabel: params.boundaryLabel,
|
|
27945
|
+
context,
|
|
27946
|
+
});
|
|
27947
|
+
if (outsideResult) {
|
|
27948
|
+
return outsideResult;
|
|
27949
|
+
}
|
|
27950
|
+
return resolveRootPathLexicalAsync({
|
|
27951
|
+
params,
|
|
27952
|
+
absolutePath: context.absolutePath,
|
|
27953
|
+
rootPath: context.rootPath,
|
|
27954
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
27955
|
+
});
|
|
27956
|
+
}
|
|
27957
|
+
function resolveRootPathSync(params) {
|
|
27958
|
+
const rootPath = path.resolve(params.rootPath);
|
|
27959
|
+
const absolutePath = path.resolve(params.absolutePath);
|
|
27960
|
+
const rootCanonicalPath = params.rootCanonicalPath
|
|
27961
|
+
? path.resolve(params.rootCanonicalPath)
|
|
27962
|
+
: resolvePathViaExistingAncestorSync(rootPath);
|
|
27963
|
+
const context = createBoundaryResolutionContext({
|
|
27964
|
+
resolveParams: params,
|
|
27965
|
+
rootPath,
|
|
27966
|
+
absolutePath,
|
|
27967
|
+
rootCanonicalPath,
|
|
27968
|
+
outsideLexicalCanonicalPath: resolveOutsideLexicalCanonicalPathSync({
|
|
27969
|
+
rootPath,
|
|
27970
|
+
absolutePath,
|
|
27971
|
+
}),
|
|
27972
|
+
});
|
|
27973
|
+
const outsideResult = resolveOutsideRootPathSync({
|
|
27974
|
+
boundaryLabel: params.boundaryLabel,
|
|
27975
|
+
context,
|
|
27976
|
+
});
|
|
27977
|
+
if (outsideResult) {
|
|
27978
|
+
return outsideResult;
|
|
27979
|
+
}
|
|
27980
|
+
return resolveRootPathLexicalSync({
|
|
27981
|
+
params,
|
|
27982
|
+
absolutePath: context.absolutePath,
|
|
27983
|
+
rootPath: context.rootPath,
|
|
27984
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
27985
|
+
});
|
|
27986
|
+
}
|
|
27987
|
+
function isPromiseLike(value) {
|
|
27988
|
+
return Boolean(value &&
|
|
27989
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
27990
|
+
"then" in value &&
|
|
27991
|
+
typeof value.then === "function");
|
|
27992
|
+
}
|
|
27993
|
+
function createLexicalTraversalState(params) {
|
|
27994
|
+
const relative = external_node_path_.relative(params.rootPath, params.absolutePath);
|
|
27995
|
+
return {
|
|
27996
|
+
segments: relative.split(external_node_path_.sep).filter(Boolean),
|
|
27997
|
+
allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
|
|
27998
|
+
canonicalCursor: params.rootCanonicalPath,
|
|
27999
|
+
lexicalCursor: params.rootPath,
|
|
28000
|
+
preserveFinalSymlink: false,
|
|
28001
|
+
};
|
|
28002
|
+
}
|
|
28003
|
+
function assertLexicalCursorInsideBoundary(params) {
|
|
28004
|
+
assertInsideBoundary({
|
|
28005
|
+
boundaryLabel: params.params.boundaryLabel,
|
|
28006
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28007
|
+
candidatePath: params.candidatePath,
|
|
28008
|
+
absolutePath: params.absolutePath,
|
|
28009
|
+
});
|
|
28010
|
+
}
|
|
28011
|
+
function applyMissingSuffixToCanonicalCursor(params) {
|
|
28012
|
+
const missingSuffix = params.state.segments.slice(params.missingFromIndex);
|
|
28013
|
+
params.state.canonicalCursor = external_node_path_.resolve(params.state.canonicalCursor, ...missingSuffix);
|
|
28014
|
+
assertLexicalCursorInsideBoundary({
|
|
28015
|
+
params: params.params,
|
|
28016
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28017
|
+
candidatePath: params.state.canonicalCursor,
|
|
28018
|
+
absolutePath: params.absolutePath,
|
|
28019
|
+
});
|
|
28020
|
+
}
|
|
28021
|
+
function advanceCanonicalCursorForSegment(params) {
|
|
28022
|
+
params.state.canonicalCursor = external_node_path_.resolve(params.state.canonicalCursor, params.segment);
|
|
28023
|
+
assertLexicalCursorInsideBoundary({
|
|
28024
|
+
params: params.params,
|
|
28025
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28026
|
+
candidatePath: params.state.canonicalCursor,
|
|
28027
|
+
absolutePath: params.absolutePath,
|
|
28028
|
+
});
|
|
28029
|
+
}
|
|
28030
|
+
function finalizeLexicalResolution(params) {
|
|
28031
|
+
assertLexicalCursorInsideBoundary({
|
|
28032
|
+
params: params.params,
|
|
28033
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28034
|
+
candidatePath: params.state.canonicalCursor,
|
|
28035
|
+
absolutePath: params.absolutePath,
|
|
28036
|
+
});
|
|
28037
|
+
return buildResolvedRootPath({
|
|
28038
|
+
absolutePath: params.absolutePath,
|
|
28039
|
+
canonicalPath: params.state.canonicalCursor,
|
|
28040
|
+
rootPath: params.rootPath,
|
|
28041
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28042
|
+
kind: params.kind,
|
|
28043
|
+
});
|
|
28044
|
+
}
|
|
28045
|
+
function handleLexicalLstatFailure(params) {
|
|
28046
|
+
if (!path_isNotFoundPathError(params.error)) {
|
|
28047
|
+
return false;
|
|
28048
|
+
}
|
|
28049
|
+
applyMissingSuffixToCanonicalCursor({
|
|
28050
|
+
state: params.state,
|
|
28051
|
+
missingFromIndex: params.missingFromIndex,
|
|
28052
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28053
|
+
params: params.resolveParams,
|
|
28054
|
+
absolutePath: params.absolutePath,
|
|
28055
|
+
});
|
|
28056
|
+
return true;
|
|
28057
|
+
}
|
|
28058
|
+
function handleLexicalStatReadFailure(params) {
|
|
28059
|
+
if (handleLexicalLstatFailure({
|
|
28060
|
+
error: params.error,
|
|
28061
|
+
state: params.state,
|
|
28062
|
+
missingFromIndex: params.missingFromIndex,
|
|
28063
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28064
|
+
resolveParams: params.resolveParams,
|
|
28065
|
+
absolutePath: params.absolutePath,
|
|
28066
|
+
})) {
|
|
28067
|
+
return null;
|
|
28068
|
+
}
|
|
28069
|
+
throw params.error;
|
|
28070
|
+
}
|
|
28071
|
+
function handleLexicalStatDisposition(params) {
|
|
28072
|
+
if (!params.isSymbolicLink) {
|
|
28073
|
+
advanceCanonicalCursorForSegment({
|
|
28074
|
+
state: params.state,
|
|
28075
|
+
segment: params.segment,
|
|
28076
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28077
|
+
params: params.resolveParams,
|
|
28078
|
+
absolutePath: params.absolutePath,
|
|
28079
|
+
});
|
|
28080
|
+
return "continue";
|
|
28081
|
+
}
|
|
28082
|
+
if (params.state.allowFinalSymlink && params.isLast) {
|
|
28083
|
+
params.state.preserveFinalSymlink = true;
|
|
28084
|
+
advanceCanonicalCursorForSegment({
|
|
28085
|
+
state: params.state,
|
|
28086
|
+
segment: params.segment,
|
|
28087
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28088
|
+
params: params.resolveParams,
|
|
28089
|
+
absolutePath: params.absolutePath,
|
|
28090
|
+
});
|
|
28091
|
+
return "break";
|
|
28092
|
+
}
|
|
28093
|
+
return "resolve-link";
|
|
28094
|
+
}
|
|
28095
|
+
function applyResolvedSymlinkHop(params) {
|
|
28096
|
+
if (!path_isPathInside(params.rootCanonicalPath, params.linkCanonical)) {
|
|
28097
|
+
throw symlinkEscapeError({
|
|
28098
|
+
boundaryLabel: params.boundaryLabel,
|
|
28099
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28100
|
+
symlinkPath: params.state.lexicalCursor,
|
|
28101
|
+
});
|
|
28102
|
+
}
|
|
28103
|
+
params.state.canonicalCursor = params.linkCanonical;
|
|
28104
|
+
params.state.lexicalCursor = params.linkCanonical;
|
|
28105
|
+
}
|
|
28106
|
+
function readLexicalStat(params) {
|
|
28107
|
+
try {
|
|
28108
|
+
const stat = params.read(params.state.lexicalCursor);
|
|
28109
|
+
if (isPromiseLike(stat)) {
|
|
28110
|
+
return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure({ ...params, error }));
|
|
28111
|
+
}
|
|
28112
|
+
return stat;
|
|
28113
|
+
}
|
|
28114
|
+
catch (error) {
|
|
28115
|
+
return handleLexicalStatReadFailure({ ...params, error });
|
|
28116
|
+
}
|
|
28117
|
+
}
|
|
28118
|
+
function resolveAndApplySymlinkHop(params) {
|
|
28119
|
+
const linkCanonical = params.resolveLinkCanonical(params.state.lexicalCursor);
|
|
28120
|
+
if (isPromiseLike(linkCanonical)) {
|
|
28121
|
+
return Promise.resolve(linkCanonical).then((value) => applyResolvedSymlinkHop({
|
|
28122
|
+
state: params.state,
|
|
28123
|
+
linkCanonical: value,
|
|
28124
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28125
|
+
boundaryLabel: params.boundaryLabel,
|
|
28126
|
+
}));
|
|
28127
|
+
}
|
|
28128
|
+
applyResolvedSymlinkHop({
|
|
28129
|
+
state: params.state,
|
|
28130
|
+
linkCanonical,
|
|
28131
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28132
|
+
boundaryLabel: params.boundaryLabel,
|
|
28133
|
+
});
|
|
28134
|
+
}
|
|
28135
|
+
function* iterateLexicalTraversal(state) {
|
|
28136
|
+
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
28137
|
+
const segment = state.segments[idx] ?? "";
|
|
28138
|
+
const isLast = idx === state.segments.length - 1;
|
|
28139
|
+
state.lexicalCursor = external_node_path_.join(state.lexicalCursor, segment);
|
|
28140
|
+
yield { idx, segment, isLast };
|
|
28141
|
+
}
|
|
28142
|
+
}
|
|
28143
|
+
async function resolveRootPathLexicalAsync(params) {
|
|
28144
|
+
const state = createLexicalTraversalState(params);
|
|
28145
|
+
const sharedStepParams = {
|
|
28146
|
+
state,
|
|
28147
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28148
|
+
resolveParams: params.params,
|
|
28149
|
+
absolutePath: params.absolutePath,
|
|
28150
|
+
};
|
|
28151
|
+
for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
|
|
28152
|
+
const stat = await readLexicalStat({
|
|
28153
|
+
...sharedStepParams,
|
|
28154
|
+
missingFromIndex: idx,
|
|
28155
|
+
read: (cursor) => promises_.lstat(cursor),
|
|
28156
|
+
});
|
|
28157
|
+
if (!stat) {
|
|
28158
|
+
break;
|
|
28159
|
+
}
|
|
28160
|
+
const disposition = handleLexicalStatDisposition({
|
|
28161
|
+
...sharedStepParams,
|
|
28162
|
+
isSymbolicLink: stat.isSymbolicLink(),
|
|
28163
|
+
segment,
|
|
28164
|
+
isLast,
|
|
28165
|
+
});
|
|
28166
|
+
if (disposition === "continue") {
|
|
28167
|
+
continue;
|
|
28168
|
+
}
|
|
28169
|
+
if (disposition === "break") {
|
|
28170
|
+
break;
|
|
28171
|
+
}
|
|
28172
|
+
await resolveAndApplySymlinkHop({
|
|
28173
|
+
state,
|
|
28174
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28175
|
+
boundaryLabel: params.params.boundaryLabel,
|
|
28176
|
+
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor),
|
|
28177
|
+
});
|
|
28178
|
+
}
|
|
28179
|
+
const kind = await getPathKind(params.absolutePath, state.preserveFinalSymlink);
|
|
28180
|
+
return finalizeLexicalResolution({
|
|
28181
|
+
...params,
|
|
28182
|
+
state,
|
|
28183
|
+
kind,
|
|
28184
|
+
});
|
|
28185
|
+
}
|
|
28186
|
+
function resolveRootPathLexicalSync(params) {
|
|
28187
|
+
const state = createLexicalTraversalState(params);
|
|
28188
|
+
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
28189
|
+
const segment = state.segments[idx] ?? "";
|
|
28190
|
+
const isLast = idx === state.segments.length - 1;
|
|
28191
|
+
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
28192
|
+
const maybeStat = readLexicalStat({
|
|
28193
|
+
state,
|
|
28194
|
+
missingFromIndex: idx,
|
|
28195
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28196
|
+
resolveParams: params.params,
|
|
28197
|
+
absolutePath: params.absolutePath,
|
|
28198
|
+
read: (cursor) => fs.lstatSync(cursor),
|
|
28199
|
+
});
|
|
28200
|
+
if (isPromiseLike(maybeStat)) {
|
|
28201
|
+
throw new Error("Unexpected async lexical stat");
|
|
28202
|
+
}
|
|
28203
|
+
const stat = maybeStat;
|
|
28204
|
+
if (!stat) {
|
|
28205
|
+
break;
|
|
28206
|
+
}
|
|
28207
|
+
const disposition = handleLexicalStatDisposition({
|
|
28208
|
+
state,
|
|
28209
|
+
isSymbolicLink: stat.isSymbolicLink(),
|
|
28210
|
+
segment,
|
|
28211
|
+
isLast,
|
|
28212
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28213
|
+
resolveParams: params.params,
|
|
28214
|
+
absolutePath: params.absolutePath,
|
|
28215
|
+
});
|
|
28216
|
+
if (disposition === "continue") {
|
|
28217
|
+
continue;
|
|
28218
|
+
}
|
|
28219
|
+
if (disposition === "break") {
|
|
28220
|
+
break;
|
|
28221
|
+
}
|
|
28222
|
+
const maybeApplied = resolveAndApplySymlinkHop({
|
|
28223
|
+
state,
|
|
28224
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28225
|
+
boundaryLabel: params.params.boundaryLabel,
|
|
28226
|
+
resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor),
|
|
28227
|
+
});
|
|
28228
|
+
if (isPromiseLike(maybeApplied)) {
|
|
28229
|
+
throw new Error("Unexpected async symlink resolution");
|
|
28230
|
+
}
|
|
28231
|
+
}
|
|
28232
|
+
const kind = getPathKindSync(params.absolutePath, state.preserveFinalSymlink);
|
|
28233
|
+
return finalizeLexicalResolution({
|
|
28234
|
+
...params,
|
|
28235
|
+
state,
|
|
28236
|
+
kind,
|
|
28237
|
+
});
|
|
28238
|
+
}
|
|
28239
|
+
function resolveCanonicalOutsideLexicalPath(params) {
|
|
28240
|
+
return params.outsideLexicalCanonicalPath ?? params.absolutePath;
|
|
28241
|
+
}
|
|
28242
|
+
function createBoundaryResolutionContext(params) {
|
|
28243
|
+
const lexicalInside = path_isPathInside(params.rootPath, params.absolutePath);
|
|
28244
|
+
const canonicalOutsideLexicalPath = resolveCanonicalOutsideLexicalPath({
|
|
28245
|
+
absolutePath: params.absolutePath,
|
|
28246
|
+
outsideLexicalCanonicalPath: params.outsideLexicalCanonicalPath,
|
|
28247
|
+
});
|
|
28248
|
+
assertLexicalBoundaryOrCanonicalAlias({
|
|
28249
|
+
skipLexicalRootCheck: params.resolveParams.skipLexicalRootCheck,
|
|
28250
|
+
lexicalInside,
|
|
28251
|
+
canonicalOutsideLexicalPath,
|
|
28252
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28253
|
+
boundaryLabel: params.resolveParams.boundaryLabel,
|
|
28254
|
+
rootPath: params.rootPath,
|
|
28255
|
+
absolutePath: params.absolutePath,
|
|
28256
|
+
});
|
|
28257
|
+
return {
|
|
28258
|
+
rootPath: params.rootPath,
|
|
28259
|
+
absolutePath: params.absolutePath,
|
|
28260
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28261
|
+
lexicalInside,
|
|
28262
|
+
canonicalOutsideLexicalPath,
|
|
28263
|
+
};
|
|
28264
|
+
}
|
|
28265
|
+
async function resolveOutsideRootPathAsync(params) {
|
|
28266
|
+
if (params.context.lexicalInside) {
|
|
28267
|
+
return null;
|
|
28268
|
+
}
|
|
28269
|
+
const kind = await getPathKind(params.context.absolutePath, false);
|
|
28270
|
+
return buildOutsideRootPathFromContext({
|
|
28271
|
+
boundaryLabel: params.boundaryLabel,
|
|
28272
|
+
context: params.context,
|
|
28273
|
+
kind,
|
|
28274
|
+
});
|
|
28275
|
+
}
|
|
28276
|
+
function resolveOutsideRootPathSync(params) {
|
|
28277
|
+
if (params.context.lexicalInside) {
|
|
28278
|
+
return null;
|
|
28279
|
+
}
|
|
28280
|
+
const kind = getPathKindSync(params.context.absolutePath, false);
|
|
28281
|
+
return buildOutsideRootPathFromContext({
|
|
28282
|
+
boundaryLabel: params.boundaryLabel,
|
|
28283
|
+
context: params.context,
|
|
28284
|
+
kind,
|
|
28285
|
+
});
|
|
28286
|
+
}
|
|
28287
|
+
function buildOutsideRootPathFromContext(params) {
|
|
28288
|
+
return buildOutsideLexicalRootPath({
|
|
28289
|
+
boundaryLabel: params.boundaryLabel,
|
|
28290
|
+
rootCanonicalPath: params.context.rootCanonicalPath,
|
|
28291
|
+
absolutePath: params.context.absolutePath,
|
|
28292
|
+
canonicalOutsideLexicalPath: params.context.canonicalOutsideLexicalPath,
|
|
28293
|
+
rootPath: params.context.rootPath,
|
|
28294
|
+
kind: params.kind,
|
|
28295
|
+
});
|
|
28296
|
+
}
|
|
28297
|
+
async function resolveOutsideLexicalCanonicalPathAsync(params) {
|
|
28298
|
+
if (path_isPathInside(params.rootPath, params.absolutePath)) {
|
|
28299
|
+
return undefined;
|
|
28300
|
+
}
|
|
28301
|
+
return await resolvePathViaExistingAncestor(params.absolutePath);
|
|
28302
|
+
}
|
|
28303
|
+
function resolveOutsideLexicalCanonicalPathSync(params) {
|
|
28304
|
+
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
28305
|
+
return undefined;
|
|
28306
|
+
}
|
|
28307
|
+
return resolvePathViaExistingAncestorSync(params.absolutePath);
|
|
28308
|
+
}
|
|
28309
|
+
function buildOutsideLexicalRootPath(params) {
|
|
28310
|
+
assertInsideBoundary({
|
|
28311
|
+
boundaryLabel: params.boundaryLabel,
|
|
28312
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28313
|
+
candidatePath: params.canonicalOutsideLexicalPath,
|
|
28314
|
+
absolutePath: params.absolutePath,
|
|
28315
|
+
});
|
|
28316
|
+
return buildResolvedRootPath({
|
|
28317
|
+
absolutePath: params.absolutePath,
|
|
28318
|
+
canonicalPath: params.canonicalOutsideLexicalPath,
|
|
28319
|
+
rootPath: params.rootPath,
|
|
28320
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28321
|
+
kind: params.kind,
|
|
28322
|
+
});
|
|
28323
|
+
}
|
|
28324
|
+
function assertLexicalBoundaryOrCanonicalAlias(params) {
|
|
28325
|
+
if (params.skipLexicalRootCheck || params.lexicalInside) {
|
|
28326
|
+
return;
|
|
28327
|
+
}
|
|
28328
|
+
if (path_isPathInside(params.rootCanonicalPath, params.canonicalOutsideLexicalPath)) {
|
|
28329
|
+
return;
|
|
28330
|
+
}
|
|
28331
|
+
throw pathEscapeError({
|
|
28332
|
+
boundaryLabel: params.boundaryLabel,
|
|
28333
|
+
rootPath: params.rootPath,
|
|
28334
|
+
absolutePath: params.absolutePath,
|
|
28335
|
+
});
|
|
28336
|
+
}
|
|
28337
|
+
function buildResolvedRootPath(params) {
|
|
28338
|
+
return {
|
|
28339
|
+
absolutePath: params.absolutePath,
|
|
28340
|
+
canonicalPath: params.canonicalPath,
|
|
28341
|
+
rootPath: params.rootPath,
|
|
28342
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
28343
|
+
relativePath: relativeInsideRoot(params.rootCanonicalPath, params.canonicalPath),
|
|
28344
|
+
exists: params.kind.exists,
|
|
28345
|
+
kind: params.kind.kind,
|
|
28346
|
+
};
|
|
28347
|
+
}
|
|
28348
|
+
async function resolvePathViaExistingAncestor(targetPath) {
|
|
28349
|
+
const normalized = external_node_path_.resolve(targetPath);
|
|
28350
|
+
let cursor = normalized;
|
|
28351
|
+
const missingSuffix = [];
|
|
28352
|
+
while (!isFilesystemRoot(cursor) && !(await pathExists(cursor))) {
|
|
28353
|
+
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
28354
|
+
const parent = external_node_path_.dirname(cursor);
|
|
28355
|
+
if (parent === cursor) {
|
|
28356
|
+
break;
|
|
28357
|
+
}
|
|
28358
|
+
cursor = parent;
|
|
28359
|
+
}
|
|
28360
|
+
if (!(await pathExists(cursor))) {
|
|
28361
|
+
return normalized;
|
|
28362
|
+
}
|
|
28363
|
+
try {
|
|
28364
|
+
const resolvedAncestor = external_node_path_.resolve(await promises_.realpath(cursor));
|
|
28365
|
+
if (missingSuffix.length === 0) {
|
|
28366
|
+
return resolvedAncestor;
|
|
28367
|
+
}
|
|
28368
|
+
return external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
|
|
28369
|
+
}
|
|
28370
|
+
catch {
|
|
28371
|
+
return normalized;
|
|
28372
|
+
}
|
|
28373
|
+
}
|
|
28374
|
+
function resolvePathViaExistingAncestorSync(targetPath) {
|
|
28375
|
+
const normalized = path.resolve(targetPath);
|
|
28376
|
+
let cursor = normalized;
|
|
28377
|
+
const missingSuffix = [];
|
|
28378
|
+
while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
|
|
28379
|
+
missingSuffix.unshift(path.basename(cursor));
|
|
28380
|
+
const parent = path.dirname(cursor);
|
|
28381
|
+
if (parent === cursor) {
|
|
28382
|
+
break;
|
|
28383
|
+
}
|
|
28384
|
+
cursor = parent;
|
|
28385
|
+
}
|
|
28386
|
+
if (!fs.existsSync(cursor)) {
|
|
28387
|
+
return normalized;
|
|
28388
|
+
}
|
|
28389
|
+
try {
|
|
28390
|
+
// Keep sync behavior aligned with async (`fsp.realpath`) to avoid
|
|
28391
|
+
// platform-specific canonical alias drift (notably on Windows).
|
|
28392
|
+
const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
|
|
28393
|
+
if (missingSuffix.length === 0) {
|
|
28394
|
+
return resolvedAncestor;
|
|
28395
|
+
}
|
|
28396
|
+
return path.resolve(resolvedAncestor, ...missingSuffix);
|
|
28397
|
+
}
|
|
28398
|
+
catch {
|
|
28399
|
+
return normalized;
|
|
28400
|
+
}
|
|
28401
|
+
}
|
|
28402
|
+
async function getPathKind(absolutePath, preserveFinalSymlink) {
|
|
28403
|
+
try {
|
|
28404
|
+
const stat = preserveFinalSymlink
|
|
28405
|
+
? await promises_.lstat(absolutePath)
|
|
28406
|
+
: await promises_.stat(absolutePath);
|
|
28407
|
+
return { exists: true, kind: toResolvedKind(stat) };
|
|
28408
|
+
}
|
|
28409
|
+
catch (error) {
|
|
28410
|
+
if (path_isNotFoundPathError(error)) {
|
|
28411
|
+
return { exists: false, kind: "missing" };
|
|
28412
|
+
}
|
|
28413
|
+
throw error;
|
|
28414
|
+
}
|
|
28415
|
+
}
|
|
28416
|
+
function getPathKindSync(absolutePath, preserveFinalSymlink) {
|
|
28417
|
+
try {
|
|
28418
|
+
const stat = preserveFinalSymlink ? fs.lstatSync(absolutePath) : fs.statSync(absolutePath);
|
|
28419
|
+
return { exists: true, kind: toResolvedKind(stat) };
|
|
28420
|
+
}
|
|
28421
|
+
catch (error) {
|
|
28422
|
+
if (isNotFoundPathError(error)) {
|
|
28423
|
+
return { exists: false, kind: "missing" };
|
|
28424
|
+
}
|
|
28425
|
+
throw error;
|
|
28426
|
+
}
|
|
28427
|
+
}
|
|
28428
|
+
function toResolvedKind(stat) {
|
|
28429
|
+
if (stat.isFile()) {
|
|
28430
|
+
return "file";
|
|
28431
|
+
}
|
|
28432
|
+
if (stat.isDirectory()) {
|
|
28433
|
+
return "directory";
|
|
28434
|
+
}
|
|
28435
|
+
if (stat.isSymbolicLink()) {
|
|
28436
|
+
return "symlink";
|
|
28437
|
+
}
|
|
28438
|
+
return "other";
|
|
28439
|
+
}
|
|
28440
|
+
function relativeInsideRoot(rootPath, targetPath) {
|
|
28441
|
+
const relative = external_node_path_.relative(external_node_path_.resolve(rootPath), external_node_path_.resolve(targetPath));
|
|
28442
|
+
if (!relative || relative === ".") {
|
|
28443
|
+
return "";
|
|
28444
|
+
}
|
|
28445
|
+
if (relative.startsWith("..") || external_node_path_.isAbsolute(relative)) {
|
|
28446
|
+
return "";
|
|
28447
|
+
}
|
|
28448
|
+
return relative;
|
|
28449
|
+
}
|
|
28450
|
+
function assertInsideBoundary(params) {
|
|
28451
|
+
if (path_isPathInside(params.rootCanonicalPath, params.candidatePath)) {
|
|
28452
|
+
return;
|
|
28453
|
+
}
|
|
28454
|
+
throw new Error(`Path resolves outside ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.absolutePath)}`);
|
|
28455
|
+
}
|
|
28456
|
+
function pathEscapeError(params) {
|
|
28457
|
+
return new Error(`Path escapes ${params.boundaryLabel} (${shortPath(params.rootPath)}): ${shortPath(params.absolutePath)}`);
|
|
28458
|
+
}
|
|
28459
|
+
function symlinkEscapeError(params) {
|
|
28460
|
+
return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
|
|
28461
|
+
}
|
|
28462
|
+
function shortPath(value) {
|
|
28463
|
+
const home = external_node_os_.homedir();
|
|
28464
|
+
if (value.startsWith(home)) {
|
|
28465
|
+
return `~${value.slice(home.length)}`;
|
|
28466
|
+
}
|
|
28467
|
+
return value;
|
|
28468
|
+
}
|
|
28469
|
+
function isFilesystemRoot(candidate) {
|
|
28470
|
+
return external_node_path_.parse(candidate).root === candidate;
|
|
28471
|
+
}
|
|
28472
|
+
async function pathExists(targetPath) {
|
|
28473
|
+
try {
|
|
28474
|
+
await promises_.lstat(targetPath);
|
|
28475
|
+
return true;
|
|
28476
|
+
}
|
|
28477
|
+
catch (error) {
|
|
28478
|
+
if (path_isNotFoundPathError(error)) {
|
|
28479
|
+
return false;
|
|
28480
|
+
}
|
|
28481
|
+
throw error;
|
|
28482
|
+
}
|
|
28483
|
+
}
|
|
28484
|
+
async function resolveSymlinkHopPath(symlinkPath) {
|
|
28485
|
+
try {
|
|
28486
|
+
return external_node_path_.resolve(await promises_.realpath(symlinkPath));
|
|
28487
|
+
}
|
|
28488
|
+
catch (error) {
|
|
28489
|
+
if (!path_isNotFoundPathError(error)) {
|
|
28490
|
+
throw error;
|
|
28491
|
+
}
|
|
28492
|
+
const linkTarget = await promises_.readlink(symlinkPath);
|
|
28493
|
+
const linkAbsolute = external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget);
|
|
28494
|
+
return resolvePathViaExistingAncestor(linkAbsolute);
|
|
28495
|
+
}
|
|
28496
|
+
}
|
|
28497
|
+
function resolveSymlinkHopPathSync(symlinkPath) {
|
|
28498
|
+
try {
|
|
28499
|
+
return path.resolve(fs.realpathSync(symlinkPath));
|
|
28500
|
+
}
|
|
28501
|
+
catch (error) {
|
|
28502
|
+
if (!isNotFoundPathError(error)) {
|
|
28503
|
+
throw error;
|
|
28504
|
+
}
|
|
28505
|
+
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
28506
|
+
const linkAbsolute = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
28507
|
+
return resolvePathViaExistingAncestorSync(linkAbsolute);
|
|
28508
|
+
}
|
|
28509
|
+
}
|
|
28510
|
+
|
|
28511
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-policy.js
|
|
28512
|
+
|
|
28513
|
+
|
|
28514
|
+
|
|
28515
|
+
|
|
28516
|
+
const PATH_ALIAS_POLICIES = ROOT_PATH_ALIAS_POLICIES;
|
|
28517
|
+
async function assertNoPathAliasEscape(params) {
|
|
28518
|
+
const resolved = await resolveRootPath({
|
|
28519
|
+
absolutePath: params.absolutePath,
|
|
28520
|
+
rootPath: params.rootPath,
|
|
28521
|
+
boundaryLabel: params.boundaryLabel,
|
|
28522
|
+
policy: params.policy,
|
|
28523
|
+
});
|
|
28524
|
+
const allowFinalSymlink = params.policy?.allowFinalSymlinkForUnlink === true;
|
|
28525
|
+
if (allowFinalSymlink && resolved.kind === "symlink") {
|
|
28526
|
+
return;
|
|
28527
|
+
}
|
|
28528
|
+
await assertNoHardlinkedFinalPath({
|
|
28529
|
+
filePath: resolved.absolutePath,
|
|
28530
|
+
root: resolved.rootPath,
|
|
28531
|
+
boundaryLabel: params.boundaryLabel,
|
|
28532
|
+
allowFinalHardlinkForUnlink: params.policy?.allowFinalHardlinkForUnlink,
|
|
28533
|
+
});
|
|
28534
|
+
}
|
|
28535
|
+
async function assertNoHardlinkedFinalPath(params) {
|
|
28536
|
+
if (params.allowFinalHardlinkForUnlink) {
|
|
28537
|
+
return;
|
|
28538
|
+
}
|
|
28539
|
+
let stat;
|
|
28540
|
+
try {
|
|
28541
|
+
stat = await promises_.stat(params.filePath);
|
|
28542
|
+
}
|
|
28543
|
+
catch (err) {
|
|
28544
|
+
if (path_isNotFoundPathError(err)) {
|
|
28545
|
+
return;
|
|
28546
|
+
}
|
|
28547
|
+
throw err;
|
|
28548
|
+
}
|
|
28549
|
+
if (!stat.isFile()) {
|
|
28550
|
+
return;
|
|
28551
|
+
}
|
|
28552
|
+
if (stat.nlink > 1) {
|
|
28553
|
+
throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${path_policy_shortPath(params.root)}): ${path_policy_shortPath(params.filePath)}`);
|
|
28554
|
+
}
|
|
28555
|
+
}
|
|
28556
|
+
function path_policy_shortPath(value) {
|
|
28557
|
+
if (value.startsWith(external_node_os_.homedir())) {
|
|
28558
|
+
return `~${value.slice(external_node_os_.homedir().length)}`;
|
|
28559
|
+
}
|
|
28560
|
+
return value;
|
|
28561
|
+
}
|
|
28562
|
+
|
|
28563
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-stat.js
|
|
28564
|
+
function pathStatFromStats(stat) {
|
|
28565
|
+
return {
|
|
28566
|
+
dev: Number(stat.dev),
|
|
28567
|
+
gid: Number(stat.gid),
|
|
28568
|
+
ino: Number(stat.ino),
|
|
28569
|
+
isDirectory: stat.isDirectory(),
|
|
28570
|
+
isFile: stat.isFile(),
|
|
28571
|
+
isSymbolicLink: stat.isSymbolicLink(),
|
|
28572
|
+
mode: stat.mode,
|
|
28573
|
+
mtimeMs: stat.mtimeMs,
|
|
28574
|
+
nlink: stat.nlink,
|
|
28575
|
+
size: stat.size,
|
|
28576
|
+
uid: stat.uid,
|
|
28577
|
+
};
|
|
28578
|
+
}
|
|
28579
|
+
|
|
28580
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/home-dir.js
|
|
28581
|
+
|
|
28582
|
+
|
|
28583
|
+
|
|
28584
|
+
function normalize(value) {
|
|
28585
|
+
const trimmed = normalizeOptionalString(value);
|
|
28586
|
+
if (!trimmed) {
|
|
28587
|
+
return undefined;
|
|
28588
|
+
}
|
|
28589
|
+
if (trimmed === "undefined" || trimmed === "null") {
|
|
28590
|
+
return undefined;
|
|
28591
|
+
}
|
|
28592
|
+
return trimmed;
|
|
28593
|
+
}
|
|
28594
|
+
function resolveEffectiveHomeDir(env = process.env, homedir = external_node_os_.homedir) {
|
|
28595
|
+
const raw = resolveRawHomeDir(env, homedir);
|
|
28596
|
+
return raw ? external_node_path_.resolve(raw) : undefined;
|
|
28597
|
+
}
|
|
28598
|
+
function resolveOsHomeDir(env = process.env, homedir = os.homedir) {
|
|
28599
|
+
const raw = resolveRawOsHomeDir(env, homedir);
|
|
28600
|
+
return raw ? path.resolve(raw) : undefined;
|
|
28601
|
+
}
|
|
28602
|
+
function resolveRawHomeDir(env, homedir) {
|
|
28603
|
+
const explicitHome = normalize(env.OPENCLAW_HOME);
|
|
28604
|
+
if (!explicitHome) {
|
|
28605
|
+
return resolveRawOsHomeDir(env, homedir);
|
|
28606
|
+
}
|
|
28607
|
+
const segments = external_node_path_.normalize(explicitHome).split(external_node_path_.sep);
|
|
28608
|
+
if (segments[0] !== "~") {
|
|
28609
|
+
return explicitHome;
|
|
28610
|
+
}
|
|
28611
|
+
// OPENCLAW_HOME starts with "~"; expand against the os home dir. Fall
|
|
28612
|
+
// back to undefined when there is no os home to expand against rather
|
|
28613
|
+
// than returning a raw "~"-prefixed path the caller cannot use.
|
|
28614
|
+
const fallbackHome = resolveRawOsHomeDir(env, homedir);
|
|
28615
|
+
if (!fallbackHome) {
|
|
28616
|
+
return undefined;
|
|
28617
|
+
}
|
|
28618
|
+
return expandHomePrefix(explicitHome, { home: fallbackHome });
|
|
28619
|
+
}
|
|
28620
|
+
function resolveRawOsHomeDir(env, homedir) {
|
|
28621
|
+
const envHome = normalize(env.HOME);
|
|
28622
|
+
if (envHome) {
|
|
28623
|
+
return envHome;
|
|
28624
|
+
}
|
|
28625
|
+
const userProfile = normalize(env.USERPROFILE);
|
|
28626
|
+
if (userProfile) {
|
|
28627
|
+
return userProfile;
|
|
28628
|
+
}
|
|
28629
|
+
return normalizeSafe(homedir);
|
|
28630
|
+
}
|
|
28631
|
+
function normalizeSafe(homedir) {
|
|
28632
|
+
try {
|
|
28633
|
+
return normalize(homedir());
|
|
28634
|
+
}
|
|
28635
|
+
catch {
|
|
28636
|
+
return undefined;
|
|
28637
|
+
}
|
|
28638
|
+
}
|
|
28639
|
+
function resolveRequiredHomeDir(env = process.env, homedir = os.homedir) {
|
|
28640
|
+
return resolveEffectiveHomeDir(env, homedir) ?? path.resolve(process.cwd());
|
|
28641
|
+
}
|
|
28642
|
+
function resolveRequiredOsHomeDir(env = process.env, homedir = os.homedir) {
|
|
28643
|
+
return resolveOsHomeDir(env, homedir) ?? path.resolve(process.cwd());
|
|
28644
|
+
}
|
|
28645
|
+
function expandHomePrefix(input, opts) {
|
|
28646
|
+
const segments = external_node_path_.normalize(input).split(external_node_path_.sep);
|
|
28647
|
+
if (segments[0] !== "~") {
|
|
28648
|
+
return input;
|
|
28649
|
+
}
|
|
28650
|
+
const home = normalize(opts?.home) ??
|
|
28651
|
+
resolveEffectiveHomeDir(opts?.env ?? process.env, opts?.homedir ?? external_node_os_.homedir);
|
|
28652
|
+
if (!home) {
|
|
28653
|
+
return input;
|
|
28654
|
+
}
|
|
28655
|
+
return external_node_path_.join(home, ...segments.slice(1));
|
|
28656
|
+
}
|
|
28657
|
+
function resolveHomeRelativePath(input, opts) {
|
|
28658
|
+
if (!input) {
|
|
28659
|
+
return input;
|
|
28660
|
+
}
|
|
28661
|
+
const segments = path.normalize(input).split(path.sep);
|
|
28662
|
+
if (segments[0] !== "~") {
|
|
28663
|
+
return path.resolve(input);
|
|
28664
|
+
}
|
|
28665
|
+
const expanded = expandHomePrefix(input, {
|
|
28666
|
+
home: resolveRequiredHomeDir(opts?.env ?? process.env, opts?.homedir ?? os.homedir),
|
|
28667
|
+
env: opts?.env,
|
|
28668
|
+
homedir: opts?.homedir,
|
|
28669
|
+
});
|
|
28670
|
+
return path.resolve(expanded);
|
|
28671
|
+
}
|
|
28672
|
+
function resolveUserPath(input, optsOrEnv, homedir) {
|
|
28673
|
+
const opts = optsOrEnv && ("env" in optsOrEnv || "homedir" in optsOrEnv)
|
|
28674
|
+
? optsOrEnv
|
|
28675
|
+
: { env: optsOrEnv, homedir };
|
|
28676
|
+
return resolveHomeRelativePath(input, opts);
|
|
28677
|
+
}
|
|
28678
|
+
function resolveOsHomeRelativePath(input, opts) {
|
|
28679
|
+
if (!input) {
|
|
28680
|
+
return input;
|
|
28681
|
+
}
|
|
28682
|
+
const segments = path.normalize(input).split(path.sep);
|
|
28683
|
+
if (segments[0] !== "~") {
|
|
28684
|
+
return path.resolve(input);
|
|
28685
|
+
}
|
|
28686
|
+
const expanded = expandHomePrefix(input, {
|
|
28687
|
+
home: resolveRequiredOsHomeDir(opts?.env ?? process.env, opts?.homedir ?? os.homedir),
|
|
28688
|
+
env: opts?.env,
|
|
28689
|
+
homedir: opts?.homedir,
|
|
28690
|
+
});
|
|
28691
|
+
return path.resolve(expanded);
|
|
28692
|
+
}
|
|
28693
|
+
|
|
28694
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-context.js
|
|
28695
|
+
|
|
28696
|
+
|
|
28697
|
+
|
|
28698
|
+
|
|
28699
|
+
|
|
28700
|
+
|
|
28701
|
+
const ensureTrailingSep = (value) => value.endsWith(external_node_path_.sep) ? value : value + external_node_path_.sep;
|
|
28702
|
+
function assertValidRootRelativePath(relativePath) {
|
|
28703
|
+
path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
28704
|
+
}
|
|
28705
|
+
let cachedHomePath;
|
|
28706
|
+
async function expandRelativePathWithHome(relativePath) {
|
|
28707
|
+
const rawHome = process.env.HOME || process.env.USERPROFILE || external_node_os_.homedir();
|
|
28708
|
+
if (cachedHomePath?.raw !== rawHome) {
|
|
28709
|
+
let realHome = rawHome;
|
|
28710
|
+
try {
|
|
28711
|
+
realHome = await promises_.realpath(rawHome);
|
|
28712
|
+
}
|
|
28713
|
+
catch {
|
|
28714
|
+
// If the home dir cannot be canonicalized, keep lexical expansion behavior.
|
|
28715
|
+
}
|
|
28716
|
+
cachedHomePath = { raw: rawHome, real: realHome };
|
|
28717
|
+
}
|
|
28718
|
+
return expandHomePrefix(relativePath, { home: cachedHomePath.real });
|
|
28719
|
+
}
|
|
28720
|
+
async function resolveRootContext(rootDir) {
|
|
28721
|
+
path_assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
|
|
28722
|
+
let rootReal;
|
|
28723
|
+
try {
|
|
28724
|
+
rootReal = await promises_.realpath(rootDir);
|
|
28725
|
+
const rootStat = await promises_.stat(rootReal);
|
|
28726
|
+
if (!rootStat.isDirectory()) {
|
|
28727
|
+
throw new errors_FsSafeError("invalid-path", "root dir is not a directory");
|
|
28728
|
+
}
|
|
28729
|
+
}
|
|
28730
|
+
catch (err) {
|
|
28731
|
+
if (err instanceof errors_FsSafeError) {
|
|
28732
|
+
throw err;
|
|
28733
|
+
}
|
|
28734
|
+
if (path_isNotFoundPathError(err)) {
|
|
28735
|
+
throw new errors_FsSafeError("not-found", "root dir not found");
|
|
28736
|
+
}
|
|
28737
|
+
throw err;
|
|
28738
|
+
}
|
|
28739
|
+
return {
|
|
28740
|
+
rootDir: external_node_path_.resolve(rootDir),
|
|
28741
|
+
rootReal,
|
|
28742
|
+
rootWithSep: ensureTrailingSep(rootReal),
|
|
28743
|
+
};
|
|
28744
|
+
}
|
|
28745
|
+
async function resolvePathInRoot(root, relativePath) {
|
|
28746
|
+
assertValidRootRelativePath(relativePath);
|
|
28747
|
+
const expanded = await expandRelativePathWithHome(relativePath);
|
|
28748
|
+
const resolved = external_node_path_.resolve(root.rootWithSep, expanded);
|
|
28749
|
+
if (!path_isPathInside(root.rootWithSep, resolved)) {
|
|
28750
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
28751
|
+
}
|
|
28752
|
+
return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved };
|
|
28753
|
+
}
|
|
28754
|
+
async function resolvePathWithinRoot(params) {
|
|
28755
|
+
return await resolvePathInRoot(await resolveRootContext(params.rootDir), params.relativePath);
|
|
28756
|
+
}
|
|
28757
|
+
|
|
28758
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-errors.js
|
|
28759
|
+
|
|
28760
|
+
|
|
28761
|
+
function isAlreadyExistsError(error) {
|
|
28762
|
+
return hasNodeErrorCode(error, "EEXIST") || /File exists|EEXIST/i.test(String(error));
|
|
28763
|
+
}
|
|
28764
|
+
function normalizePinnedWriteError(error) {
|
|
28765
|
+
if (error instanceof errors_FsSafeError) {
|
|
28766
|
+
return error;
|
|
28767
|
+
}
|
|
28768
|
+
return new errors_FsSafeError("invalid-path", "path is not a regular file under root", {
|
|
28769
|
+
cause: error instanceof Error ? error : undefined,
|
|
28770
|
+
});
|
|
28771
|
+
}
|
|
28772
|
+
function normalizePinnedPathError(error) {
|
|
28773
|
+
if (error instanceof errors_FsSafeError) {
|
|
28774
|
+
return error;
|
|
28775
|
+
}
|
|
28776
|
+
return new errors_FsSafeError("path-alias", "path is not under root", {
|
|
28777
|
+
cause: error instanceof Error ? error : undefined,
|
|
28778
|
+
});
|
|
28779
|
+
}
|
|
28780
|
+
|
|
28781
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
|
|
28782
|
+
let test_hooks_fsSafeTestHooks;
|
|
28783
|
+
function allowFsSafeTestHooks() {
|
|
28784
|
+
return false || process.env.VITEST === "true";
|
|
28785
|
+
}
|
|
28786
|
+
function getFsSafeTestHooks() {
|
|
28787
|
+
return test_hooks_fsSafeTestHooks;
|
|
28788
|
+
}
|
|
28789
|
+
function __setFsSafeTestHooksForTest(hooks) {
|
|
28790
|
+
if (hooks && !allowFsSafeTestHooks()) {
|
|
28791
|
+
throw new Error("__setFsSafeTestHooksForTest is only available in tests");
|
|
28792
|
+
}
|
|
28793
|
+
test_hooks_fsSafeTestHooks = hooks;
|
|
28794
|
+
}
|
|
28795
|
+
|
|
28796
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
|
|
28797
|
+
|
|
28798
|
+
const tempCleanupEntries = new Map();
|
|
28799
|
+
let cleanupRegistered = false;
|
|
28800
|
+
function cleanupRegisteredTempPathsSync() {
|
|
28801
|
+
for (const entry of tempCleanupEntries.values()) {
|
|
28802
|
+
try {
|
|
28803
|
+
external_node_fs_.rmSync(entry.path, { force: true, recursive: entry.recursive });
|
|
28804
|
+
}
|
|
28805
|
+
catch {
|
|
28806
|
+
// Process-exit cleanup is best-effort.
|
|
28807
|
+
}
|
|
28808
|
+
}
|
|
28809
|
+
tempCleanupEntries.clear();
|
|
28810
|
+
}
|
|
28811
|
+
function registerTempPathForExit(tempPath, options) {
|
|
28812
|
+
if (!cleanupRegistered) {
|
|
28813
|
+
cleanupRegistered = true;
|
|
28814
|
+
process.once("exit", cleanupRegisteredTempPathsSync);
|
|
28815
|
+
}
|
|
28816
|
+
tempCleanupEntries.set(tempPath, {
|
|
28817
|
+
path: tempPath,
|
|
28818
|
+
recursive: options?.recursive === true,
|
|
28819
|
+
});
|
|
28820
|
+
return () => {
|
|
28821
|
+
tempCleanupEntries.delete(tempPath);
|
|
28822
|
+
};
|
|
28823
|
+
}
|
|
28824
|
+
function __cleanupRegisteredTempPathsForTest() {
|
|
28825
|
+
cleanupRegisteredTempPathsSync();
|
|
28826
|
+
}
|
|
28827
|
+
function __cleanupRegisteredTempPathForTest(tempPath) {
|
|
28828
|
+
const entry = tempCleanupEntries.get(tempPath);
|
|
28829
|
+
if (!entry) {
|
|
28830
|
+
return;
|
|
28831
|
+
}
|
|
28832
|
+
try {
|
|
28833
|
+
fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
|
|
28834
|
+
}
|
|
28835
|
+
finally {
|
|
28836
|
+
tempCleanupEntries.delete(tempPath);
|
|
28837
|
+
}
|
|
28838
|
+
}
|
|
28839
|
+
|
|
28840
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/write-queue.js
|
|
28841
|
+
const writeQueues = new Map();
|
|
28842
|
+
async function serializePathWrite(key, run) {
|
|
28843
|
+
const previous = writeQueues.get(key) ?? Promise.resolve();
|
|
28844
|
+
const task = (async () => {
|
|
28845
|
+
await previous.catch(() => undefined);
|
|
28846
|
+
return await run();
|
|
28847
|
+
})();
|
|
28848
|
+
const done = task.then(() => undefined, () => undefined);
|
|
28849
|
+
writeQueues.set(key, done);
|
|
28850
|
+
try {
|
|
28851
|
+
return await task;
|
|
28852
|
+
}
|
|
28853
|
+
finally {
|
|
28854
|
+
if (writeQueues.get(key) === done) {
|
|
28855
|
+
writeQueues.delete(key);
|
|
28856
|
+
}
|
|
28857
|
+
}
|
|
28858
|
+
}
|
|
28859
|
+
|
|
28860
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-impl.js
|
|
28861
|
+
|
|
28862
|
+
|
|
28863
|
+
|
|
28864
|
+
|
|
28865
|
+
|
|
28866
|
+
|
|
28867
|
+
|
|
28868
|
+
|
|
28869
|
+
|
|
28870
|
+
|
|
28871
|
+
|
|
28872
|
+
|
|
28873
|
+
|
|
28874
|
+
|
|
28875
|
+
|
|
28876
|
+
|
|
28877
|
+
|
|
28878
|
+
|
|
28879
|
+
|
|
28880
|
+
|
|
28881
|
+
|
|
28882
|
+
|
|
28883
|
+
|
|
28884
|
+
|
|
28885
|
+
function logWarn(message) {
|
|
28886
|
+
if (process.env.FS_SAFE_DEBUG_WARNINGS === "1") {
|
|
28887
|
+
console.warn(message);
|
|
28888
|
+
}
|
|
28889
|
+
}
|
|
28890
|
+
const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants;
|
|
28891
|
+
const NONBLOCK_OPEN_FLAG = "O_NONBLOCK" in external_node_fs_.constants ? external_node_fs_.constants.O_NONBLOCK : 0;
|
|
28892
|
+
const OPEN_READ_FLAGS = external_node_fs_.constants.O_RDONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
28893
|
+
const OPEN_READ_NONBLOCK_FLAGS = OPEN_READ_FLAGS | NONBLOCK_OPEN_FLAG;
|
|
28894
|
+
const OPEN_READ_FOLLOW_FLAGS = external_node_fs_.constants.O_RDONLY;
|
|
28895
|
+
const OPEN_READ_FOLLOW_NONBLOCK_FLAGS = OPEN_READ_FOLLOW_FLAGS | NONBLOCK_OPEN_FLAG;
|
|
28896
|
+
const OPEN_WRITE_EXISTING_FLAGS = external_node_fs_.constants.O_WRONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
28897
|
+
const OPEN_WRITE_CREATE_FLAGS = external_node_fs_.constants.O_WRONLY |
|
|
28898
|
+
external_node_fs_.constants.O_CREAT |
|
|
28899
|
+
external_node_fs_.constants.O_EXCL |
|
|
28900
|
+
(SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
28901
|
+
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);
|
|
28902
|
+
const OPEN_APPEND_CREATE_FLAGS = external_node_fs_.constants.O_RDWR |
|
|
28903
|
+
external_node_fs_.constants.O_APPEND |
|
|
28904
|
+
external_node_fs_.constants.O_CREAT |
|
|
28905
|
+
external_node_fs_.constants.O_EXCL |
|
|
28906
|
+
(SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
28907
|
+
const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
28908
|
+
function closeHandleForDispose(handle) {
|
|
28909
|
+
return handle.close().catch(() => undefined);
|
|
28910
|
+
}
|
|
28911
|
+
function openResult(params) {
|
|
28912
|
+
return {
|
|
28913
|
+
handle: params.handle,
|
|
28914
|
+
realPath: params.realPath,
|
|
28915
|
+
stat: params.stat,
|
|
28916
|
+
[Symbol.asyncDispose]: async () => {
|
|
28917
|
+
await closeHandleForDispose(params.handle);
|
|
28918
|
+
},
|
|
28919
|
+
};
|
|
28920
|
+
}
|
|
28921
|
+
async function openVerifiedLocalFile(filePath, options) {
|
|
28922
|
+
const fsSafeTestHooks = getFsSafeTestHooks();
|
|
28923
|
+
// Reject directories before opening so we never surface EISDIR to callers (e.g. tool
|
|
28924
|
+
// results that get sent to messaging channels). See openclaw/openclaw#31186.
|
|
28925
|
+
try {
|
|
28926
|
+
const preStat = await promises_.lstat(filePath);
|
|
28927
|
+
if (preStat.isDirectory()) {
|
|
28928
|
+
throw new errors_FsSafeError("not-file", "not a file");
|
|
28929
|
+
}
|
|
28930
|
+
await fsSafeTestHooks?.afterPreOpenLstat?.(filePath);
|
|
28931
|
+
}
|
|
28932
|
+
catch (err) {
|
|
28933
|
+
if (err instanceof errors_FsSafeError) {
|
|
28934
|
+
throw err;
|
|
28935
|
+
}
|
|
28936
|
+
// ENOENT and other lstat errors: fall through and let fs.open handle.
|
|
28937
|
+
}
|
|
28938
|
+
let handle;
|
|
28939
|
+
try {
|
|
28940
|
+
const openFlags = options?.symlinks === "follow-within-root"
|
|
28941
|
+
? options?.nonBlockingRead
|
|
28942
|
+
? OPEN_READ_FOLLOW_NONBLOCK_FLAGS
|
|
28943
|
+
: OPEN_READ_FOLLOW_FLAGS
|
|
28944
|
+
: options?.nonBlockingRead
|
|
28945
|
+
? OPEN_READ_NONBLOCK_FLAGS
|
|
28946
|
+
: OPEN_READ_FLAGS;
|
|
28947
|
+
await fsSafeTestHooks?.beforeOpen?.(filePath, openFlags);
|
|
28948
|
+
handle = await promises_.open(filePath, openFlags);
|
|
28949
|
+
try {
|
|
28950
|
+
await fsSafeTestHooks?.afterOpen?.(filePath, handle);
|
|
28951
|
+
}
|
|
28952
|
+
catch (err) {
|
|
28953
|
+
await handle.close().catch(() => { });
|
|
28954
|
+
throw err;
|
|
28955
|
+
}
|
|
28956
|
+
}
|
|
28957
|
+
catch (err) {
|
|
28958
|
+
if (path_isNotFoundPathError(err)) {
|
|
28959
|
+
throw new errors_FsSafeError("not-found", "file not found");
|
|
28960
|
+
}
|
|
28961
|
+
if (isSymlinkOpenError(err)) {
|
|
28962
|
+
throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
|
|
28963
|
+
}
|
|
28964
|
+
// Defensive: if open still throws EISDIR (e.g. race), sanitize so it never leaks.
|
|
28965
|
+
if (hasNodeErrorCode(err, "EISDIR")) {
|
|
28966
|
+
throw new errors_FsSafeError("not-file", "not a file");
|
|
28967
|
+
}
|
|
28968
|
+
throw err;
|
|
28969
|
+
}
|
|
28970
|
+
try {
|
|
28971
|
+
const stat = await handle.stat();
|
|
28972
|
+
if (!stat.isFile()) {
|
|
28973
|
+
throw new errors_FsSafeError("not-file", "not a file");
|
|
28974
|
+
}
|
|
28975
|
+
if (options?.hardlinks === "reject" && stat.nlink > 1) {
|
|
28976
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
28977
|
+
}
|
|
28978
|
+
if (options?.symlinks === "follow-within-root") {
|
|
28979
|
+
const pathStat = await promises_.stat(filePath);
|
|
28980
|
+
if (!file_identity_sameFileIdentity(stat, pathStat)) {
|
|
28981
|
+
throw new errors_FsSafeError("path-mismatch", "path changed during read");
|
|
28982
|
+
}
|
|
28983
|
+
}
|
|
28984
|
+
else {
|
|
28985
|
+
const pathStat = await promises_.lstat(filePath);
|
|
28986
|
+
if (pathStat.isSymbolicLink()) {
|
|
28987
|
+
throw new errors_FsSafeError("symlink", "symlink not allowed");
|
|
28988
|
+
}
|
|
28989
|
+
if (!file_identity_sameFileIdentity(stat, pathStat)) {
|
|
28990
|
+
throw new errors_FsSafeError("path-mismatch", "path changed during read");
|
|
28991
|
+
}
|
|
28992
|
+
}
|
|
28993
|
+
const realPath = await resolveOpenedFileRealPathForHandle(handle, filePath);
|
|
28994
|
+
const realStat = await promises_.stat(realPath);
|
|
28995
|
+
if (options?.hardlinks === "reject" && realStat.nlink > 1) {
|
|
28996
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
28997
|
+
}
|
|
28998
|
+
if (!file_identity_sameFileIdentity(stat, realStat)) {
|
|
28999
|
+
throw new errors_FsSafeError("path-mismatch", "path mismatch");
|
|
29000
|
+
}
|
|
29001
|
+
return openResult({ handle, realPath, stat });
|
|
29002
|
+
}
|
|
29003
|
+
catch (err) {
|
|
29004
|
+
await handle.close().catch(() => { });
|
|
29005
|
+
if (err instanceof errors_FsSafeError) {
|
|
29006
|
+
throw err;
|
|
29007
|
+
}
|
|
29008
|
+
if (path_isNotFoundPathError(err)) {
|
|
29009
|
+
throw new errors_FsSafeError("not-found", "file not found");
|
|
29010
|
+
}
|
|
29011
|
+
throw err;
|
|
29012
|
+
}
|
|
29013
|
+
}
|
|
29014
|
+
class RootHandle {
|
|
29015
|
+
rootDir;
|
|
29016
|
+
rootReal;
|
|
29017
|
+
rootWithSep;
|
|
29018
|
+
defaults;
|
|
29019
|
+
constructor(context, defaults = {}) {
|
|
29020
|
+
this.rootDir = context.rootDir;
|
|
29021
|
+
this.rootReal = context.rootReal;
|
|
29022
|
+
this.rootWithSep = context.rootWithSep;
|
|
29023
|
+
this.defaults = defaults;
|
|
29024
|
+
}
|
|
29025
|
+
get context() {
|
|
29026
|
+
return {
|
|
29027
|
+
rootDir: this.rootDir,
|
|
29028
|
+
rootReal: this.rootReal,
|
|
29029
|
+
rootWithSep: this.rootWithSep,
|
|
29030
|
+
};
|
|
29031
|
+
}
|
|
29032
|
+
async resolve(relativePath) {
|
|
29033
|
+
return (await resolvePathInRoot(this.context, relativePath)).resolved;
|
|
29034
|
+
}
|
|
29035
|
+
async open(relativePath, options = {}) {
|
|
29036
|
+
return await openFileInRoot(this.context, {
|
|
29037
|
+
relativePath,
|
|
29038
|
+
...readDefaults(this.defaults),
|
|
29039
|
+
...options,
|
|
29040
|
+
});
|
|
29041
|
+
}
|
|
29042
|
+
async read(relativePath, options = {}) {
|
|
29043
|
+
return await readFileInRoot(this.context, {
|
|
29044
|
+
relativePath,
|
|
29045
|
+
...readDefaults(this.defaults),
|
|
29046
|
+
...options,
|
|
29047
|
+
});
|
|
29048
|
+
}
|
|
29049
|
+
async readBytes(relativePath, options = {}) {
|
|
29050
|
+
return (await this.read(relativePath, options)).buffer;
|
|
29051
|
+
}
|
|
29052
|
+
async readText(relativePath, options = {}) {
|
|
29053
|
+
const { encoding = "utf8", ...readOptions } = options;
|
|
29054
|
+
return (await this.read(relativePath, readOptions)).buffer.toString(encoding);
|
|
29055
|
+
}
|
|
29056
|
+
async readJson(relativePath, options = {}) {
|
|
29057
|
+
return JSON.parse(await this.readText(relativePath, options));
|
|
29058
|
+
}
|
|
29059
|
+
async readAbsolute(filePath, options = {}) {
|
|
29060
|
+
return await readPathInRoot(this.context, {
|
|
29061
|
+
filePath,
|
|
29062
|
+
...readDefaults(this.defaults),
|
|
29063
|
+
...options,
|
|
29064
|
+
});
|
|
29065
|
+
}
|
|
29066
|
+
reader(options = {}) {
|
|
29067
|
+
return async (filePath) => {
|
|
29068
|
+
return (await this.readAbsolute(filePath, options)).buffer;
|
|
29069
|
+
};
|
|
29070
|
+
}
|
|
29071
|
+
async openWritable(relativePath, options = {}) {
|
|
29072
|
+
const writeMode = options.writeMode ?? "replace";
|
|
29073
|
+
return await openWritableFileInRoot(this.context, {
|
|
29074
|
+
relativePath,
|
|
29075
|
+
mkdir: this.defaults.mkdir,
|
|
29076
|
+
mode: this.defaults.mode,
|
|
29077
|
+
...options,
|
|
29078
|
+
append: writeMode === "append",
|
|
29079
|
+
truncateExisting: writeMode === "replace",
|
|
29080
|
+
});
|
|
29081
|
+
}
|
|
29082
|
+
async append(relativePath, data, options = {}) {
|
|
29083
|
+
await appendFileInRoot(this.context, {
|
|
29084
|
+
relativePath,
|
|
29085
|
+
data,
|
|
29086
|
+
mkdir: this.defaults.mkdir,
|
|
29087
|
+
mode: this.defaults.mode,
|
|
29088
|
+
...options,
|
|
29089
|
+
});
|
|
29090
|
+
}
|
|
29091
|
+
async remove(relativePath) {
|
|
29092
|
+
assertValidRootRelativePath(relativePath);
|
|
29093
|
+
await removePathInRoot(this.context, relativePath);
|
|
29094
|
+
}
|
|
29095
|
+
async mkdir(relativePath) {
|
|
29096
|
+
assertValidRootRelativePath(relativePath);
|
|
29097
|
+
await mkdirPathInRoot(this.context, { relativePath });
|
|
29098
|
+
}
|
|
29099
|
+
async ensureRoot() {
|
|
29100
|
+
await mkdirPathInRoot(this.context, { relativePath: "", allowRoot: true });
|
|
29101
|
+
}
|
|
29102
|
+
async write(relativePath, data, options = {}) {
|
|
29103
|
+
await writeFileInRoot(this.context, {
|
|
29104
|
+
relativePath,
|
|
29105
|
+
data,
|
|
29106
|
+
mkdir: this.defaults.mkdir,
|
|
29107
|
+
mode: this.defaults.mode,
|
|
29108
|
+
...options,
|
|
29109
|
+
});
|
|
29110
|
+
}
|
|
29111
|
+
async create(relativePath, data, options = {}) {
|
|
29112
|
+
await writeFileInRoot(this.context, {
|
|
29113
|
+
relativePath,
|
|
29114
|
+
data,
|
|
29115
|
+
mkdir: this.defaults.mkdir,
|
|
29116
|
+
mode: this.defaults.mode,
|
|
29117
|
+
...options,
|
|
29118
|
+
overwrite: false,
|
|
29119
|
+
});
|
|
29120
|
+
}
|
|
29121
|
+
async writeJson(relativePath, data, options = {}) {
|
|
29122
|
+
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
29123
|
+
const json = JSON.stringify(data, replacer, space);
|
|
29124
|
+
await this.write(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
29125
|
+
}
|
|
29126
|
+
async createJson(relativePath, data, options = {}) {
|
|
29127
|
+
const { replacer, space, trailingNewline = true, ...writeOptions } = options;
|
|
29128
|
+
const json = JSON.stringify(data, replacer, space);
|
|
29129
|
+
await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
29130
|
+
}
|
|
29131
|
+
async copyIn(relativePath, sourcePath, options = {}) {
|
|
29132
|
+
assertValidRootRelativePath(relativePath);
|
|
29133
|
+
await copyFileInRoot(this.context, {
|
|
29134
|
+
sourcePath,
|
|
29135
|
+
relativePath,
|
|
29136
|
+
maxBytes: this.defaults.maxBytes,
|
|
29137
|
+
mkdir: this.defaults.mkdir,
|
|
29138
|
+
mode: this.defaults.mode,
|
|
29139
|
+
...options,
|
|
29140
|
+
});
|
|
29141
|
+
}
|
|
29142
|
+
async exists(relativePath) {
|
|
29143
|
+
try {
|
|
29144
|
+
await this.stat(relativePath);
|
|
29145
|
+
return true;
|
|
29146
|
+
}
|
|
29147
|
+
catch (err) {
|
|
29148
|
+
if (err instanceof errors_FsSafeError && err.code === "not-found") {
|
|
29149
|
+
return false;
|
|
26515
29150
|
}
|
|
29151
|
+
throw err;
|
|
29152
|
+
}
|
|
29153
|
+
}
|
|
29154
|
+
async stat(relativePath) {
|
|
29155
|
+
assertValidRootRelativePath(relativePath);
|
|
29156
|
+
try {
|
|
29157
|
+
return await helperStat(this.rootReal, relativePath);
|
|
29158
|
+
}
|
|
29159
|
+
catch (error) {
|
|
29160
|
+
if (canFallbackFromPythonError(error)) {
|
|
29161
|
+
return await statPathFallback(this.context, relativePath);
|
|
29162
|
+
}
|
|
29163
|
+
throw error;
|
|
29164
|
+
}
|
|
29165
|
+
}
|
|
29166
|
+
async list(relativePath, options = {}) {
|
|
29167
|
+
assertValidRootRelativePath(relativePath);
|
|
29168
|
+
try {
|
|
29169
|
+
return options.withFileTypes === true
|
|
29170
|
+
? await helperReaddir(this.rootReal, relativePath, true)
|
|
29171
|
+
: await helperReaddir(this.rootReal, relativePath, false);
|
|
29172
|
+
}
|
|
29173
|
+
catch (error) {
|
|
29174
|
+
if (canFallbackFromPythonError(error)) {
|
|
29175
|
+
return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
|
|
29176
|
+
}
|
|
29177
|
+
throw error;
|
|
29178
|
+
}
|
|
29179
|
+
}
|
|
29180
|
+
async move(fromRelative, toRelative, options = {}) {
|
|
29181
|
+
assertValidRootRelativePath(fromRelative);
|
|
29182
|
+
assertValidRootRelativePath(toRelative);
|
|
29183
|
+
try {
|
|
29184
|
+
await runPinnedHelper("rename", this.rootReal, {
|
|
29185
|
+
from: fromRelative,
|
|
29186
|
+
overwrite: options.overwrite ?? false,
|
|
29187
|
+
to: toRelative,
|
|
29188
|
+
});
|
|
29189
|
+
}
|
|
29190
|
+
catch (error) {
|
|
29191
|
+
if (canFallbackFromPythonError(error)) {
|
|
29192
|
+
await movePathFallback(this.context, {
|
|
29193
|
+
fromRelative,
|
|
29194
|
+
overwrite: options.overwrite ?? false,
|
|
29195
|
+
toRelative,
|
|
29196
|
+
});
|
|
29197
|
+
return;
|
|
29198
|
+
}
|
|
29199
|
+
throw error;
|
|
26516
29200
|
}
|
|
26517
|
-
return result;
|
|
26518
29201
|
}
|
|
26519
29202
|
}
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
29203
|
+
function readDefaults(defaults) {
|
|
29204
|
+
return {
|
|
29205
|
+
hardlinks: defaults.hardlinks,
|
|
29206
|
+
maxBytes: defaults.maxBytes ?? DEFAULT_ROOT_MAX_BYTES,
|
|
29207
|
+
nonBlockingRead: defaults.nonBlockingRead,
|
|
29208
|
+
symlinks: defaults.symlinks,
|
|
29209
|
+
};
|
|
29210
|
+
}
|
|
29211
|
+
async function root_impl_root(rootDir, defaults = {}) {
|
|
29212
|
+
return new RootHandle(await resolveRootContext(rootDir), defaults);
|
|
29213
|
+
}
|
|
29214
|
+
async function openFileInRoot(root, params) {
|
|
29215
|
+
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
29216
|
+
let opened;
|
|
29217
|
+
try {
|
|
29218
|
+
opened = await openVerifiedLocalFile(resolved, {
|
|
29219
|
+
nonBlockingRead: params.nonBlockingRead,
|
|
29220
|
+
symlinks: params.symlinks,
|
|
29221
|
+
});
|
|
29222
|
+
}
|
|
29223
|
+
catch (err) {
|
|
29224
|
+
if (err instanceof errors_FsSafeError) {
|
|
29225
|
+
throw err;
|
|
29226
|
+
}
|
|
29227
|
+
throw err;
|
|
29228
|
+
}
|
|
29229
|
+
if (params.hardlinks !== "allow" && opened.stat.nlink > 1) {
|
|
29230
|
+
await opened.handle.close().catch(() => { });
|
|
29231
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
29232
|
+
}
|
|
29233
|
+
if (!path_isPathInside(rootWithSep, opened.realPath)) {
|
|
29234
|
+
await opened.handle.close().catch(() => { });
|
|
29235
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29236
|
+
}
|
|
29237
|
+
return opened;
|
|
29238
|
+
}
|
|
29239
|
+
async function readFileInRoot(root, params) {
|
|
29240
|
+
const opened = await openFileInRoot(root, params);
|
|
29241
|
+
try {
|
|
29242
|
+
return await readOpenedFileSafely({ opened, maxBytes: params.maxBytes });
|
|
29243
|
+
}
|
|
29244
|
+
finally {
|
|
29245
|
+
await opened.handle.close().catch(() => { });
|
|
29246
|
+
}
|
|
29247
|
+
}
|
|
29248
|
+
async function readPathInRoot(root, params) {
|
|
29249
|
+
const rootDir = root.rootDir;
|
|
29250
|
+
const candidatePath = external_node_path_.isAbsolute(params.filePath)
|
|
29251
|
+
? external_node_path_.resolve(params.filePath)
|
|
29252
|
+
: external_node_path_.resolve(rootDir, params.filePath);
|
|
29253
|
+
const relativePath = external_node_path_.relative(rootDir, candidatePath);
|
|
29254
|
+
return await readFileInRoot(root, {
|
|
29255
|
+
relativePath,
|
|
29256
|
+
hardlinks: params.hardlinks,
|
|
29257
|
+
maxBytes: params.maxBytes,
|
|
29258
|
+
nonBlockingRead: params.nonBlockingRead,
|
|
29259
|
+
symlinks: params.symlinks,
|
|
29260
|
+
});
|
|
29261
|
+
}
|
|
29262
|
+
async function readLocalFileSafely(params) {
|
|
29263
|
+
const opened = await openLocalFileSafely({ filePath: params.filePath });
|
|
29264
|
+
try {
|
|
29265
|
+
return await readOpenedFileSafely({ opened, maxBytes: params.maxBytes });
|
|
29266
|
+
}
|
|
29267
|
+
finally {
|
|
29268
|
+
await opened.handle.close().catch(() => { });
|
|
29269
|
+
}
|
|
29270
|
+
}
|
|
29271
|
+
async function openLocalFileSafely(params) {
|
|
29272
|
+
assertNoNulPathInput(params.filePath, "file path contains a NUL byte");
|
|
29273
|
+
return await openVerifiedLocalFile(params.filePath);
|
|
29274
|
+
}
|
|
29275
|
+
async function readOpenedFileSafely(params) {
|
|
29276
|
+
if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
|
|
29277
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
|
|
29278
|
+
}
|
|
29279
|
+
const buffer = await params.opened.handle.readFile();
|
|
29280
|
+
if (params.maxBytes !== undefined && buffer.byteLength > params.maxBytes) {
|
|
29281
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${buffer.byteLength})`);
|
|
29282
|
+
}
|
|
29283
|
+
return {
|
|
29284
|
+
buffer,
|
|
29285
|
+
realPath: params.opened.realPath,
|
|
29286
|
+
stat: params.opened.stat,
|
|
29287
|
+
};
|
|
29288
|
+
}
|
|
29289
|
+
function emitWriteBoundaryWarning(reason) {
|
|
29290
|
+
logWarn(`security: fs-safe write boundary warning (${reason})`);
|
|
29291
|
+
}
|
|
29292
|
+
function buildAtomicWriteTempPath(targetPath) {
|
|
29293
|
+
const dir = external_node_path_.dirname(targetPath);
|
|
29294
|
+
const base = external_node_path_.basename(targetPath);
|
|
29295
|
+
return external_node_path_.join(dir, `.${base}.${process.pid}.${(0,external_node_crypto_.randomUUID)()}.tmp`);
|
|
29296
|
+
}
|
|
29297
|
+
function rootWriteQueueKey(root, relativePath) {
|
|
29298
|
+
return `${root.rootReal}\0${relativePath}`;
|
|
29299
|
+
}
|
|
29300
|
+
async function writeTempFileForAtomicReplace(params) {
|
|
29301
|
+
const tempHandle = await promises_.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
|
|
29302
|
+
try {
|
|
29303
|
+
if (typeof params.data === "string") {
|
|
29304
|
+
await tempHandle.writeFile(params.data, params.encoding ?? "utf8");
|
|
29305
|
+
}
|
|
29306
|
+
else {
|
|
29307
|
+
await tempHandle.writeFile(params.data);
|
|
29308
|
+
}
|
|
29309
|
+
return await tempHandle.stat();
|
|
29310
|
+
}
|
|
29311
|
+
finally {
|
|
29312
|
+
await tempHandle.close().catch(() => { });
|
|
29313
|
+
}
|
|
29314
|
+
}
|
|
29315
|
+
async function verifyAtomicWriteResult(params) {
|
|
29316
|
+
const opened = await openVerifiedLocalFile(params.targetPath, { hardlinks: "reject" });
|
|
29317
|
+
try {
|
|
29318
|
+
if (!file_identity_sameFileIdentity(opened.stat, params.expectedIdentity)) {
|
|
29319
|
+
throw new errors_FsSafeError("path-mismatch", "path changed during write");
|
|
29320
|
+
}
|
|
29321
|
+
if (!path_isPathInside(params.root.rootWithSep, opened.realPath)) {
|
|
29322
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29323
|
+
}
|
|
29324
|
+
}
|
|
29325
|
+
finally {
|
|
29326
|
+
await opened.handle.close().catch(() => { });
|
|
29327
|
+
}
|
|
29328
|
+
}
|
|
29329
|
+
async function resolveOpenedFileRealPathForHandle(handle, ioPath) {
|
|
29330
|
+
const handleStat = await handle.stat();
|
|
29331
|
+
const fdCandidates = process.platform === "linux"
|
|
29332
|
+
? [`/proc/self/fd/${handle.fd}`, `/dev/fd/${handle.fd}`]
|
|
29333
|
+
: process.platform === "win32"
|
|
29334
|
+
? []
|
|
29335
|
+
: [`/dev/fd/${handle.fd}`];
|
|
29336
|
+
for (const fdPath of fdCandidates) {
|
|
29337
|
+
try {
|
|
29338
|
+
const fdRealPath = await promises_.realpath(fdPath);
|
|
29339
|
+
const fdRealStat = await promises_.stat(fdRealPath);
|
|
29340
|
+
if (file_identity_sameFileIdentity(handleStat, fdRealStat)) {
|
|
29341
|
+
return fdRealPath;
|
|
29342
|
+
}
|
|
29343
|
+
}
|
|
29344
|
+
catch {
|
|
29345
|
+
// try next fd path
|
|
29346
|
+
}
|
|
29347
|
+
}
|
|
29348
|
+
try {
|
|
29349
|
+
const ioRealPath = await promises_.realpath(ioPath);
|
|
29350
|
+
const ioRealStat = await promises_.stat(ioRealPath);
|
|
29351
|
+
if (file_identity_sameFileIdentity(handleStat, ioRealStat)) {
|
|
29352
|
+
return ioRealPath;
|
|
29353
|
+
}
|
|
29354
|
+
}
|
|
29355
|
+
catch (err) {
|
|
29356
|
+
if (!path_isNotFoundPathError(err)) {
|
|
29357
|
+
throw err;
|
|
29358
|
+
}
|
|
29359
|
+
}
|
|
29360
|
+
const parentResolved = await resolveOpenedFileRealPathFromParent(handleStat, ioPath);
|
|
29361
|
+
if (parentResolved) {
|
|
29362
|
+
return parentResolved;
|
|
29363
|
+
}
|
|
29364
|
+
throw new errors_FsSafeError("path-mismatch", "unable to resolve opened file path");
|
|
29365
|
+
}
|
|
29366
|
+
async function resolveOpenedFileRealPathFromParent(handleStat, ioPath) {
|
|
29367
|
+
let parentReal;
|
|
29368
|
+
try {
|
|
29369
|
+
parentReal = await promises_.realpath(external_node_path_.dirname(ioPath));
|
|
29370
|
+
}
|
|
29371
|
+
catch (err) {
|
|
29372
|
+
if (path_isNotFoundPathError(err)) {
|
|
29373
|
+
return null;
|
|
29374
|
+
}
|
|
29375
|
+
throw err;
|
|
29376
|
+
}
|
|
29377
|
+
let entries;
|
|
29378
|
+
try {
|
|
29379
|
+
entries = await promises_.readdir(parentReal);
|
|
29380
|
+
}
|
|
29381
|
+
catch (err) {
|
|
29382
|
+
if (path_isNotFoundPathError(err)) {
|
|
29383
|
+
return null;
|
|
29384
|
+
}
|
|
29385
|
+
throw err;
|
|
29386
|
+
}
|
|
29387
|
+
for (const entry of entries.toSorted()) {
|
|
29388
|
+
const candidatePath = external_node_path_.join(parentReal, entry);
|
|
29389
|
+
try {
|
|
29390
|
+
const candidateStat = await promises_.lstat(candidatePath);
|
|
29391
|
+
if (candidateStat.isFile() && file_identity_sameFileIdentity(handleStat, candidateStat)) {
|
|
29392
|
+
return await promises_.realpath(candidatePath);
|
|
29393
|
+
}
|
|
29394
|
+
}
|
|
29395
|
+
catch (err) {
|
|
29396
|
+
if (!path_isNotFoundPathError(err)) {
|
|
29397
|
+
throw err;
|
|
29398
|
+
}
|
|
29399
|
+
}
|
|
29400
|
+
}
|
|
29401
|
+
return null;
|
|
29402
|
+
}
|
|
29403
|
+
async function openWritableFileInRoot(root, params) {
|
|
29404
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
29405
|
+
try {
|
|
29406
|
+
await assertNoPathAliasEscape({
|
|
29407
|
+
absolutePath: resolved,
|
|
29408
|
+
rootPath: rootReal,
|
|
29409
|
+
boundaryLabel: "root",
|
|
29410
|
+
});
|
|
29411
|
+
}
|
|
29412
|
+
catch (err) {
|
|
29413
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
29414
|
+
}
|
|
29415
|
+
if (params.mkdir !== false) {
|
|
29416
|
+
const parentGuard = await directory_guard_createNearestExistingDirectoryGuard(rootReal, external_node_path_.dirname(resolved));
|
|
29417
|
+
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
29418
|
+
await promises_.mkdir(external_node_path_.dirname(resolved), { recursive: true });
|
|
29419
|
+
});
|
|
29420
|
+
}
|
|
29421
|
+
let ioPath = resolved;
|
|
29422
|
+
try {
|
|
29423
|
+
const resolvedRealPath = await promises_.realpath(resolved);
|
|
29424
|
+
if (!path_isPathInside(rootWithSep, resolvedRealPath)) {
|
|
29425
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29426
|
+
}
|
|
29427
|
+
ioPath = resolvedRealPath;
|
|
29428
|
+
}
|
|
29429
|
+
catch (err) {
|
|
29430
|
+
if (err instanceof errors_FsSafeError) {
|
|
29431
|
+
throw err;
|
|
29432
|
+
}
|
|
29433
|
+
if (!path_isNotFoundPathError(err)) {
|
|
29434
|
+
throw err;
|
|
29435
|
+
}
|
|
29436
|
+
}
|
|
29437
|
+
const mode = params.mode ?? 0o600;
|
|
29438
|
+
let handle;
|
|
29439
|
+
let createdForWrite = false;
|
|
29440
|
+
const existingFlags = params.append ? OPEN_APPEND_EXISTING_FLAGS : OPEN_WRITE_EXISTING_FLAGS;
|
|
29441
|
+
const createFlags = params.append ? OPEN_APPEND_CREATE_FLAGS : OPEN_WRITE_CREATE_FLAGS;
|
|
29442
|
+
try {
|
|
29443
|
+
try {
|
|
29444
|
+
handle = await promises_.open(ioPath, existingFlags, mode);
|
|
29445
|
+
}
|
|
29446
|
+
catch (err) {
|
|
29447
|
+
if (!path_isNotFoundPathError(err)) {
|
|
29448
|
+
throw err;
|
|
29449
|
+
}
|
|
29450
|
+
handle = await promises_.open(ioPath, createFlags, mode);
|
|
29451
|
+
createdForWrite = true;
|
|
29452
|
+
}
|
|
29453
|
+
}
|
|
29454
|
+
catch (err) {
|
|
29455
|
+
if (path_isNotFoundPathError(err)) {
|
|
29456
|
+
throw new errors_FsSafeError("not-found", "file not found");
|
|
29457
|
+
}
|
|
29458
|
+
if (isSymlinkOpenError(err)) {
|
|
29459
|
+
throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
|
|
29460
|
+
}
|
|
29461
|
+
if (hasNodeErrorCode(err, "EISDIR")) {
|
|
29462
|
+
throw new errors_FsSafeError("not-file", "not a file", { cause: err });
|
|
29463
|
+
}
|
|
29464
|
+
throw err;
|
|
29465
|
+
}
|
|
29466
|
+
let realPathForCleanup = null;
|
|
29467
|
+
try {
|
|
29468
|
+
const stat = await handle.stat();
|
|
29469
|
+
if (!stat.isFile()) {
|
|
29470
|
+
throw new errors_FsSafeError("invalid-path", "path is not a regular file under root");
|
|
29471
|
+
}
|
|
29472
|
+
if (stat.nlink > 1) {
|
|
29473
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
29474
|
+
}
|
|
29475
|
+
try {
|
|
29476
|
+
const lstat = await promises_.lstat(ioPath);
|
|
29477
|
+
if (lstat.isSymbolicLink() || !lstat.isFile()) {
|
|
29478
|
+
throw new errors_FsSafeError(lstat.isSymbolicLink() ? "symlink" : "not-file", "path is not a regular file under root");
|
|
29479
|
+
}
|
|
29480
|
+
if (!file_identity_sameFileIdentity(stat, lstat)) {
|
|
29481
|
+
throw new errors_FsSafeError("path-mismatch", "path changed during write");
|
|
29482
|
+
}
|
|
29483
|
+
}
|
|
29484
|
+
catch (err) {
|
|
29485
|
+
if (!path_isNotFoundPathError(err)) {
|
|
29486
|
+
throw err;
|
|
29487
|
+
}
|
|
29488
|
+
}
|
|
29489
|
+
const realPath = await resolveOpenedFileRealPathForHandle(handle, ioPath);
|
|
29490
|
+
realPathForCleanup = realPath;
|
|
29491
|
+
const realStat = await promises_.stat(realPath);
|
|
29492
|
+
if (!file_identity_sameFileIdentity(stat, realStat)) {
|
|
29493
|
+
throw new errors_FsSafeError("path-mismatch", "path mismatch");
|
|
29494
|
+
}
|
|
29495
|
+
if (realStat.nlink > 1) {
|
|
29496
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
29497
|
+
}
|
|
29498
|
+
if (!path_isPathInside(rootWithSep, realPath)) {
|
|
29499
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29500
|
+
}
|
|
29501
|
+
// Truncate only after boundary and identity checks complete. This avoids
|
|
29502
|
+
// irreversible side effects if a symlink target changes before validation.
|
|
29503
|
+
if (params.append !== true && params.truncateExisting !== false && !createdForWrite) {
|
|
29504
|
+
await handle.truncate(0);
|
|
29505
|
+
}
|
|
29506
|
+
return {
|
|
29507
|
+
handle,
|
|
29508
|
+
createdForWrite,
|
|
29509
|
+
realPath,
|
|
29510
|
+
stat,
|
|
29511
|
+
[Symbol.asyncDispose]: async () => {
|
|
29512
|
+
await closeHandleForDispose(handle);
|
|
29513
|
+
},
|
|
29514
|
+
};
|
|
29515
|
+
}
|
|
29516
|
+
catch (err) {
|
|
29517
|
+
const cleanupCreatedPath = createdForWrite && err instanceof errors_FsSafeError;
|
|
29518
|
+
const cleanupPath = realPathForCleanup ?? ioPath;
|
|
29519
|
+
await handle.close().catch(() => { });
|
|
29520
|
+
if (cleanupCreatedPath) {
|
|
29521
|
+
await promises_.rm(cleanupPath, { force: true }).catch(() => { });
|
|
29522
|
+
}
|
|
29523
|
+
throw err;
|
|
29524
|
+
}
|
|
29525
|
+
}
|
|
29526
|
+
async function appendFileInRoot(root, params) {
|
|
29527
|
+
const target = await openWritableFileInRoot(root, {
|
|
29528
|
+
relativePath: params.relativePath,
|
|
29529
|
+
mkdir: params.mkdir,
|
|
29530
|
+
mode: params.mode,
|
|
29531
|
+
truncateExisting: false,
|
|
29532
|
+
append: true,
|
|
29533
|
+
});
|
|
29534
|
+
try {
|
|
29535
|
+
let prefix = "";
|
|
29536
|
+
if (params.prependNewlineIfNeeded === true &&
|
|
29537
|
+
!target.createdForWrite &&
|
|
29538
|
+
target.stat.size > 0 &&
|
|
29539
|
+
((typeof params.data === "string" && !params.data.startsWith("\n")) ||
|
|
29540
|
+
(Buffer.isBuffer(params.data) && params.data.length > 0 && params.data[0] !== 0x0a))) {
|
|
29541
|
+
const lastByte = Buffer.alloc(1);
|
|
29542
|
+
const { bytesRead } = await target.handle.read(lastByte, 0, 1, target.stat.size - 1);
|
|
29543
|
+
if (bytesRead === 1 && lastByte[0] !== 0x0a) {
|
|
29544
|
+
prefix = "\n";
|
|
29545
|
+
}
|
|
29546
|
+
}
|
|
29547
|
+
if (typeof params.data === "string") {
|
|
29548
|
+
await target.handle.appendFile(`${prefix}${params.data}`, params.encoding ?? "utf8");
|
|
29549
|
+
return;
|
|
29550
|
+
}
|
|
29551
|
+
const payload = prefix.length > 0 ? Buffer.concat([Buffer.from(prefix, "utf8"), params.data]) : params.data;
|
|
29552
|
+
await target.handle.appendFile(payload);
|
|
29553
|
+
}
|
|
29554
|
+
finally {
|
|
29555
|
+
await target.handle.close().catch(() => { });
|
|
29556
|
+
}
|
|
29557
|
+
}
|
|
29558
|
+
async function removePathInRoot(root, relativePath) {
|
|
29559
|
+
const resolved = await resolvePinnedRemovePathInRoot(root, relativePath);
|
|
29560
|
+
if (process.platform === "win32") {
|
|
29561
|
+
await removePathFallback(resolved);
|
|
29562
|
+
return;
|
|
29563
|
+
}
|
|
29564
|
+
try {
|
|
29565
|
+
await runPinnedPathHelper({
|
|
29566
|
+
operation: "remove",
|
|
29567
|
+
rootPath: resolved.rootReal,
|
|
29568
|
+
relativePath: resolved.relativePosix,
|
|
29569
|
+
});
|
|
29570
|
+
}
|
|
29571
|
+
catch (error) {
|
|
29572
|
+
if (isPinnedPathHelperSpawnError(error)) {
|
|
29573
|
+
await removePathFallback(resolved);
|
|
29574
|
+
return;
|
|
29575
|
+
}
|
|
29576
|
+
throw normalizePinnedPathError(error);
|
|
29577
|
+
}
|
|
29578
|
+
}
|
|
29579
|
+
async function mkdirPathInRoot(root, params) {
|
|
29580
|
+
const resolved = await resolvePinnedPathInRoot(root, params);
|
|
29581
|
+
if (process.platform === "win32") {
|
|
29582
|
+
await mkdirPathFallback(resolved);
|
|
29583
|
+
return;
|
|
29584
|
+
}
|
|
29585
|
+
try {
|
|
29586
|
+
await runPinnedPathHelper({
|
|
29587
|
+
operation: "mkdirp",
|
|
29588
|
+
rootPath: resolved.rootReal,
|
|
29589
|
+
relativePath: resolved.relativePosix,
|
|
29590
|
+
});
|
|
29591
|
+
}
|
|
29592
|
+
catch (error) {
|
|
29593
|
+
if (isPinnedPathHelperSpawnError(error)) {
|
|
29594
|
+
await mkdirPathFallback(resolved);
|
|
29595
|
+
return;
|
|
29596
|
+
}
|
|
29597
|
+
throw normalizePinnedPathError(error);
|
|
29598
|
+
}
|
|
29599
|
+
}
|
|
29600
|
+
async function writeFileInRoot(root, params) {
|
|
29601
|
+
if (process.platform === "win32") {
|
|
29602
|
+
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
29603
|
+
await writeFileFallback(root, params);
|
|
29604
|
+
});
|
|
29605
|
+
return;
|
|
29606
|
+
}
|
|
29607
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
|
|
29608
|
+
await serializePathWrite(pinned.targetPath, async () => {
|
|
29609
|
+
let identity;
|
|
29610
|
+
try {
|
|
29611
|
+
identity = await runPinnedWriteHelper({
|
|
29612
|
+
rootPath: pinned.rootReal,
|
|
29613
|
+
relativeParentPath: pinned.relativeParentPath,
|
|
29614
|
+
basename: pinned.basename,
|
|
29615
|
+
mkdir: params.mkdir !== false,
|
|
29616
|
+
mode: params.mode ?? pinned.mode,
|
|
29617
|
+
overwrite: params.overwrite,
|
|
29618
|
+
input: {
|
|
29619
|
+
kind: "buffer",
|
|
29620
|
+
data: params.data,
|
|
29621
|
+
encoding: params.encoding,
|
|
29622
|
+
},
|
|
29623
|
+
});
|
|
29624
|
+
}
|
|
29625
|
+
catch (error) {
|
|
29626
|
+
if (params.overwrite === false && isAlreadyExistsError(error)) {
|
|
29627
|
+
throw new errors_FsSafeError("already-exists", "file already exists", {
|
|
29628
|
+
cause: error instanceof Error ? error : undefined,
|
|
29629
|
+
});
|
|
29630
|
+
}
|
|
29631
|
+
throw normalizePinnedWriteError(error);
|
|
29632
|
+
}
|
|
29633
|
+
try {
|
|
29634
|
+
await verifyAtomicWriteResult({
|
|
29635
|
+
root,
|
|
29636
|
+
targetPath: pinned.targetPath,
|
|
29637
|
+
expectedIdentity: identity,
|
|
29638
|
+
});
|
|
29639
|
+
}
|
|
29640
|
+
catch (err) {
|
|
29641
|
+
emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
|
|
29642
|
+
throw err;
|
|
29643
|
+
}
|
|
29644
|
+
});
|
|
29645
|
+
}
|
|
29646
|
+
async function copyFileInRoot(root, params) {
|
|
29647
|
+
assertValidRootRelativePath(params.relativePath);
|
|
29648
|
+
path_assertNoNulPathInput(params.sourcePath, "source path contains a NUL byte");
|
|
29649
|
+
const source = await openVerifiedLocalFile(params.sourcePath, {
|
|
29650
|
+
hardlinks: params.sourceHardlinks,
|
|
29651
|
+
});
|
|
29652
|
+
if (params.maxBytes !== undefined && source.stat.size > params.maxBytes) {
|
|
29653
|
+
await source.handle.close().catch(() => { });
|
|
29654
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
|
|
29655
|
+
}
|
|
29656
|
+
try {
|
|
29657
|
+
if (process.platform === "win32") {
|
|
29658
|
+
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
29659
|
+
await copyFileFallback(root, params, source);
|
|
29660
|
+
});
|
|
29661
|
+
return;
|
|
29662
|
+
}
|
|
29663
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode);
|
|
29664
|
+
await serializePathWrite(pinned.targetPath, async () => {
|
|
29665
|
+
let identity;
|
|
29666
|
+
try {
|
|
29667
|
+
if (getFsSafePythonConfig().mode === "off") {
|
|
29668
|
+
await copyFileFallback(root, params, source);
|
|
29669
|
+
return;
|
|
29670
|
+
}
|
|
29671
|
+
identity = await runPinnedCopyHelper({
|
|
29672
|
+
rootPath: pinned.rootReal,
|
|
29673
|
+
relativeParentPath: pinned.relativeParentPath,
|
|
29674
|
+
basename: pinned.basename,
|
|
29675
|
+
mkdir: params.mkdir !== false,
|
|
29676
|
+
mode: pinned.mode,
|
|
29677
|
+
overwrite: true,
|
|
29678
|
+
maxBytes: params.maxBytes,
|
|
29679
|
+
sourcePath: source.realPath,
|
|
29680
|
+
sourceIdentity: { dev: source.stat.dev, ino: source.stat.ino },
|
|
29681
|
+
});
|
|
29682
|
+
}
|
|
29683
|
+
catch (error) {
|
|
29684
|
+
if (canFallbackFromPythonError(error)) {
|
|
29685
|
+
await copyFileFallback(root, params, source);
|
|
29686
|
+
return;
|
|
29687
|
+
}
|
|
29688
|
+
throw normalizePinnedWriteError(error);
|
|
29689
|
+
}
|
|
29690
|
+
try {
|
|
29691
|
+
await verifyAtomicWriteResult({
|
|
29692
|
+
root,
|
|
29693
|
+
targetPath: pinned.targetPath,
|
|
29694
|
+
expectedIdentity: identity,
|
|
29695
|
+
});
|
|
29696
|
+
}
|
|
29697
|
+
catch (err) {
|
|
29698
|
+
emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
|
|
29699
|
+
throw err;
|
|
29700
|
+
}
|
|
29701
|
+
});
|
|
29702
|
+
}
|
|
29703
|
+
finally {
|
|
29704
|
+
await source.handle.close().catch(() => { });
|
|
29705
|
+
}
|
|
29706
|
+
}
|
|
29707
|
+
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode) {
|
|
29708
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath);
|
|
29709
|
+
try {
|
|
29710
|
+
await assertNoPathAliasEscape({
|
|
29711
|
+
absolutePath: resolved,
|
|
29712
|
+
rootPath: rootReal,
|
|
29713
|
+
boundaryLabel: "root",
|
|
29714
|
+
});
|
|
29715
|
+
}
|
|
29716
|
+
catch (err) {
|
|
29717
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
29718
|
+
}
|
|
29719
|
+
// resolvePathInRoot already enforces isPathInside, so any actual escape
|
|
29720
|
+
// is rejected upstream.
|
|
29721
|
+
const relativeResolved = external_node_path_.relative(rootReal, resolved);
|
|
29722
|
+
if (external_node_path_.isAbsolute(relativeResolved)) {
|
|
29723
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29724
|
+
}
|
|
29725
|
+
const relativePosix = relativeResolved
|
|
29726
|
+
? relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep)
|
|
29727
|
+
: "";
|
|
29728
|
+
const basename = external_node_path_.posix.basename(relativePosix);
|
|
29729
|
+
if (!basename || basename === "." || basename === "/") {
|
|
29730
|
+
throw new errors_FsSafeError("invalid-path", "invalid target path");
|
|
29731
|
+
}
|
|
29732
|
+
let mode = requestedMode ?? 0o600;
|
|
29733
|
+
try {
|
|
29734
|
+
const opened = await openFileInRoot(root, {
|
|
29735
|
+
relativePath,
|
|
29736
|
+
hardlinks: "reject",
|
|
29737
|
+
nonBlockingRead: true,
|
|
29738
|
+
});
|
|
29739
|
+
try {
|
|
29740
|
+
mode = requestedMode ?? (opened.stat.mode & 0o777);
|
|
29741
|
+
if (!path_isPathInside(rootWithSep, opened.realPath)) {
|
|
29742
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29743
|
+
}
|
|
29744
|
+
}
|
|
29745
|
+
finally {
|
|
29746
|
+
await opened.handle.close().catch(() => { });
|
|
29747
|
+
}
|
|
29748
|
+
}
|
|
29749
|
+
catch (err) {
|
|
29750
|
+
if (!(err instanceof errors_FsSafeError) || err.code !== "not-found") {
|
|
29751
|
+
throw err;
|
|
29752
|
+
}
|
|
29753
|
+
}
|
|
29754
|
+
return {
|
|
29755
|
+
rootReal,
|
|
29756
|
+
targetPath: resolved,
|
|
29757
|
+
relativeParentPath: external_node_path_.posix.dirname(relativePosix) === "." ? "" : external_node_path_.posix.dirname(relativePosix),
|
|
29758
|
+
basename,
|
|
29759
|
+
mode: mode || 0o600,
|
|
29760
|
+
};
|
|
29761
|
+
}
|
|
29762
|
+
async function resolvePinnedPathInRoot(root, params) {
|
|
29763
|
+
return await resolvePinnedOperationPathInRoot(root, {
|
|
29764
|
+
allowRoot: params.allowRoot,
|
|
29765
|
+
relativePath: params.relativePath,
|
|
29766
|
+
policy: PATH_ALIAS_POLICIES.strict,
|
|
29767
|
+
});
|
|
29768
|
+
}
|
|
29769
|
+
async function resolvePinnedRemovePathInRoot(root, relativePath) {
|
|
29770
|
+
return await resolvePinnedOperationPathInRoot(root, {
|
|
29771
|
+
relativePath,
|
|
29772
|
+
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
29773
|
+
});
|
|
29774
|
+
}
|
|
29775
|
+
async function resolvePinnedOperationPathInRoot(root, params) {
|
|
29776
|
+
const resolved = await resolvePinnedRootPathInRoot(root, {
|
|
29777
|
+
relativePath: params.relativePath,
|
|
29778
|
+
policy: params.policy,
|
|
29779
|
+
});
|
|
29780
|
+
const relativeResolved = external_node_path_.relative(resolved.rootReal, resolved.canonicalPath);
|
|
29781
|
+
if ((relativeResolved === "" || relativeResolved === ".") && params.allowRoot === true) {
|
|
29782
|
+
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix: "" };
|
|
29783
|
+
}
|
|
29784
|
+
const firstSegment = relativeResolved.split(external_node_path_.sep)[0];
|
|
29785
|
+
if (relativeResolved === "" ||
|
|
29786
|
+
relativeResolved === "." ||
|
|
29787
|
+
firstSegment === ".." ||
|
|
29788
|
+
external_node_path_.isAbsolute(relativeResolved)) {
|
|
29789
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29790
|
+
}
|
|
29791
|
+
const relativePosix = relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep);
|
|
29792
|
+
if (!path_isPathInside(resolved.rootWithSep, resolved.canonicalPath)) {
|
|
29793
|
+
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
29794
|
+
}
|
|
29795
|
+
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix };
|
|
29796
|
+
}
|
|
29797
|
+
async function resolvePinnedRootPathInRoot(root, params) {
|
|
29798
|
+
const rootReal = root.rootReal;
|
|
29799
|
+
let resolved;
|
|
29800
|
+
try {
|
|
29801
|
+
resolved = await resolveRootPath({
|
|
29802
|
+
absolutePath: external_node_path_.resolve(rootReal, await expandRelativePathWithHome(params.relativePath)),
|
|
29803
|
+
rootPath: rootReal,
|
|
29804
|
+
rootCanonicalPath: rootReal,
|
|
29805
|
+
boundaryLabel: "root",
|
|
29806
|
+
policy: params.policy,
|
|
29807
|
+
});
|
|
29808
|
+
}
|
|
29809
|
+
catch (err) {
|
|
29810
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
29811
|
+
}
|
|
29812
|
+
const rootWithSep = ensureTrailingSep(resolved.rootCanonicalPath);
|
|
29813
|
+
return {
|
|
29814
|
+
rootReal: resolved.rootCanonicalPath,
|
|
29815
|
+
rootWithSep,
|
|
29816
|
+
canonicalPath: resolved.canonicalPath,
|
|
29817
|
+
};
|
|
29818
|
+
}
|
|
29819
|
+
async function removePathFallback(resolved) {
|
|
29820
|
+
const guard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(resolved.resolved));
|
|
29821
|
+
await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("remove", resolved.resolved);
|
|
29822
|
+
await assertAsyncDirectoryGuard(guard);
|
|
29823
|
+
await ((await promises_.lstat(resolved.resolved)).isDirectory() ? promises_.rmdir(resolved.resolved) : promises_.rm(resolved.resolved));
|
|
29824
|
+
await assertAsyncDirectoryGuard(guard).catch(() => undefined);
|
|
29825
|
+
}
|
|
29826
|
+
async function mkdirPathFallback(resolved) {
|
|
29827
|
+
await mkdirPathComponentsWithGuards({
|
|
29828
|
+
rootReal: resolved.rootReal, targetPath: resolved.resolved,
|
|
29829
|
+
beforeComponent: async (componentPath) => await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("mkdir", componentPath),
|
|
29830
|
+
});
|
|
29831
|
+
}
|
|
29832
|
+
async function statPathFallback(root, relativePath) {
|
|
29833
|
+
const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
|
|
29834
|
+
try {
|
|
29835
|
+
return pathStatFromStats(await promises_.lstat(resolved.resolved));
|
|
29836
|
+
}
|
|
29837
|
+
catch (error) {
|
|
29838
|
+
if (path_isNotFoundPathError(error)) {
|
|
29839
|
+
throw new errors_FsSafeError("not-found", "file not found", {
|
|
29840
|
+
cause: error instanceof Error ? error : undefined,
|
|
29841
|
+
});
|
|
29842
|
+
}
|
|
29843
|
+
throw error;
|
|
29844
|
+
}
|
|
29845
|
+
}
|
|
29846
|
+
async function listPathFallback(root, relativePath, withFileTypes) {
|
|
29847
|
+
const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
|
|
29848
|
+
try {
|
|
29849
|
+
const names = await promises_.readdir(resolved.resolved);
|
|
29850
|
+
const sortedNames = names.toSorted();
|
|
29851
|
+
if (!withFileTypes) {
|
|
29852
|
+
return sortedNames;
|
|
29853
|
+
}
|
|
29854
|
+
const entries = [];
|
|
29855
|
+
for (const name of sortedNames) {
|
|
29856
|
+
entries.push({
|
|
29857
|
+
name,
|
|
29858
|
+
...pathStatFromStats(await promises_.lstat(external_node_path_.join(resolved.resolved, name))),
|
|
29859
|
+
});
|
|
29860
|
+
}
|
|
29861
|
+
return entries;
|
|
29862
|
+
}
|
|
29863
|
+
catch (error) {
|
|
29864
|
+
if (path_isNotFoundPathError(error)) {
|
|
29865
|
+
throw new errors_FsSafeError("not-found", "directory not found", {
|
|
29866
|
+
cause: error instanceof Error ? error : undefined,
|
|
29867
|
+
});
|
|
29868
|
+
}
|
|
29869
|
+
throw error;
|
|
29870
|
+
}
|
|
29871
|
+
}
|
|
29872
|
+
async function movePathFallback(root, params) {
|
|
29873
|
+
const source = await resolvePathInRoot(root, params.fromRelative);
|
|
29874
|
+
await resolvePinnedRootPathInRoot(root, {
|
|
29875
|
+
relativePath: params.fromRelative,
|
|
29876
|
+
policy: PATH_ALIAS_POLICIES.strict,
|
|
29877
|
+
});
|
|
29878
|
+
const target = await resolvePathInRoot(root, params.toRelative);
|
|
29879
|
+
await resolvePinnedRootPathInRoot(root, {
|
|
29880
|
+
relativePath: params.toRelative,
|
|
29881
|
+
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
29882
|
+
});
|
|
29883
|
+
try {
|
|
29884
|
+
await assertNoPathAliasEscape({
|
|
29885
|
+
absolutePath: target.resolved,
|
|
29886
|
+
rootPath: target.rootReal,
|
|
29887
|
+
boundaryLabel: "root",
|
|
29888
|
+
});
|
|
29889
|
+
}
|
|
29890
|
+
catch (error) {
|
|
29891
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", {
|
|
29892
|
+
cause: error instanceof Error ? error : undefined,
|
|
29893
|
+
});
|
|
29894
|
+
}
|
|
29895
|
+
let sourceStat;
|
|
29896
|
+
try {
|
|
29897
|
+
sourceStat = await promises_.lstat(source.resolved);
|
|
29898
|
+
}
|
|
29899
|
+
catch (error) {
|
|
29900
|
+
if (path_isNotFoundPathError(error)) {
|
|
29901
|
+
throw new errors_FsSafeError("not-found", "file not found", {
|
|
29902
|
+
cause: error instanceof Error ? error : undefined,
|
|
29903
|
+
});
|
|
29904
|
+
}
|
|
29905
|
+
throw error;
|
|
29906
|
+
}
|
|
29907
|
+
if (sourceStat.isSymbolicLink()) {
|
|
29908
|
+
throw new errors_FsSafeError("symlink", "symlink not allowed");
|
|
29909
|
+
}
|
|
29910
|
+
if (sourceStat.isFile() && sourceStat.nlink > 1) {
|
|
29911
|
+
throw new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
29912
|
+
}
|
|
29913
|
+
if (!params.overwrite) {
|
|
29914
|
+
try {
|
|
29915
|
+
await promises_.lstat(target.resolved);
|
|
29916
|
+
throw new errors_FsSafeError("already-exists", "destination exists");
|
|
29917
|
+
}
|
|
29918
|
+
catch (error) {
|
|
29919
|
+
if (error instanceof errors_FsSafeError) {
|
|
29920
|
+
throw error;
|
|
29921
|
+
}
|
|
29922
|
+
if (!path_isNotFoundPathError(error)) {
|
|
29923
|
+
throw error;
|
|
29924
|
+
}
|
|
29925
|
+
}
|
|
29926
|
+
}
|
|
29927
|
+
const sourceParentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(source.resolved));
|
|
29928
|
+
const targetParentGuard = await directory_guard_createNearestExistingDirectoryGuard(target.rootReal, external_node_path_.dirname(target.resolved));
|
|
29929
|
+
await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("move", target.resolved);
|
|
29930
|
+
await assertAsyncDirectoryGuard(sourceParentGuard);
|
|
29931
|
+
await assertAsyncDirectoryGuard(targetParentGuard);
|
|
29932
|
+
try {
|
|
29933
|
+
await promises_.rename(source.resolved, target.resolved);
|
|
29934
|
+
}
|
|
29935
|
+
catch (error) {
|
|
29936
|
+
if (path_isNotFoundPathError(error)) {
|
|
29937
|
+
throw new errors_FsSafeError("not-found", "file not found", {
|
|
29938
|
+
cause: error instanceof Error ? error : undefined,
|
|
29939
|
+
});
|
|
29940
|
+
}
|
|
29941
|
+
if (hasNodeErrorCode(error, "EEXIST")) {
|
|
29942
|
+
throw new errors_FsSafeError("already-exists", "destination exists", {
|
|
29943
|
+
cause: error instanceof Error ? error : undefined,
|
|
29944
|
+
});
|
|
29945
|
+
}
|
|
29946
|
+
throw error;
|
|
29947
|
+
}
|
|
29948
|
+
await assertAsyncDirectoryGuard(targetParentGuard).catch(() => undefined);
|
|
29949
|
+
}
|
|
29950
|
+
async function writeFileFallback(root, params) {
|
|
29951
|
+
if (params.overwrite === false) {
|
|
29952
|
+
await writeMissingFileFallback(root, params);
|
|
29953
|
+
return;
|
|
29954
|
+
}
|
|
29955
|
+
const target = await openWritableFileInRoot(root, {
|
|
29956
|
+
relativePath: params.relativePath,
|
|
29957
|
+
mkdir: params.mkdir,
|
|
29958
|
+
mode: params.mode,
|
|
29959
|
+
truncateExisting: false,
|
|
29960
|
+
});
|
|
29961
|
+
const destinationPath = target.realPath;
|
|
29962
|
+
const mode = params.mode ?? (target.stat.mode & 0o777);
|
|
29963
|
+
await target.handle.close().catch(() => { });
|
|
29964
|
+
const destinationGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(destinationPath));
|
|
29965
|
+
let tempPath = null;
|
|
29966
|
+
let unregisterTempPath = null;
|
|
29967
|
+
try {
|
|
29968
|
+
tempPath = buildAtomicWriteTempPath(destinationPath);
|
|
29969
|
+
unregisterTempPath = registerTempPathForExit(tempPath);
|
|
29970
|
+
const writtenStat = await writeTempFileForAtomicReplace({
|
|
29971
|
+
tempPath,
|
|
29972
|
+
data: params.data,
|
|
29973
|
+
encoding: params.encoding,
|
|
29974
|
+
mode: mode || 0o600,
|
|
29975
|
+
});
|
|
29976
|
+
const commitTempPath = tempPath;
|
|
29977
|
+
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
29978
|
+
await promises_.rename(commitTempPath, destinationPath);
|
|
29979
|
+
});
|
|
29980
|
+
tempPath = null;
|
|
29981
|
+
unregisterTempPath();
|
|
29982
|
+
unregisterTempPath = null;
|
|
29983
|
+
try {
|
|
29984
|
+
await verifyAtomicWriteResult({
|
|
29985
|
+
root,
|
|
29986
|
+
targetPath: destinationPath,
|
|
29987
|
+
expectedIdentity: writtenStat,
|
|
29988
|
+
});
|
|
29989
|
+
}
|
|
29990
|
+
catch (err) {
|
|
29991
|
+
emitWriteBoundaryWarning(`post-write verification failed: ${String(err)}`);
|
|
29992
|
+
throw err;
|
|
29993
|
+
}
|
|
29994
|
+
}
|
|
29995
|
+
finally {
|
|
29996
|
+
if (tempPath) {
|
|
29997
|
+
await promises_.rm(tempPath, { force: true }).catch(() => { });
|
|
29998
|
+
}
|
|
29999
|
+
unregisterTempPath?.();
|
|
30000
|
+
}
|
|
30001
|
+
}
|
|
30002
|
+
async function writeMissingFileFallback(root, params) {
|
|
30003
|
+
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
30004
|
+
try {
|
|
30005
|
+
await assertNoPathAliasEscape({
|
|
30006
|
+
absolutePath: resolved,
|
|
30007
|
+
rootPath: rootReal,
|
|
30008
|
+
boundaryLabel: "root",
|
|
30009
|
+
});
|
|
30010
|
+
}
|
|
30011
|
+
catch (err) {
|
|
30012
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
30013
|
+
}
|
|
30014
|
+
if (params.mkdir !== false) {
|
|
30015
|
+
await promises_.mkdir(external_node_path_.dirname(resolved), { recursive: true });
|
|
30016
|
+
}
|
|
30017
|
+
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(resolved));
|
|
30018
|
+
let created = false;
|
|
30019
|
+
try {
|
|
30020
|
+
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
30021
|
+
const handle = await promises_.open(resolved, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
30022
|
+
created = true;
|
|
30023
|
+
try {
|
|
30024
|
+
if (typeof params.data === "string") {
|
|
30025
|
+
await handle.writeFile(params.data, params.encoding ?? "utf8");
|
|
30026
|
+
}
|
|
30027
|
+
else {
|
|
30028
|
+
await handle.writeFile(params.data);
|
|
30029
|
+
}
|
|
30030
|
+
return { handle, writtenStat: await handle.stat() };
|
|
30031
|
+
}
|
|
30032
|
+
catch (error) {
|
|
30033
|
+
await handle.close().catch(() => undefined);
|
|
30034
|
+
throw error;
|
|
30035
|
+
}
|
|
30036
|
+
}, {
|
|
30037
|
+
onPostGuardFailure: async ({ handle }) => {
|
|
30038
|
+
created = false; // Parent is untrusted now; skip outer path cleanup by name.
|
|
30039
|
+
await handle.close().catch(() => undefined);
|
|
30040
|
+
},
|
|
30041
|
+
});
|
|
30042
|
+
await handle.close();
|
|
30043
|
+
await verifyAtomicWriteResult({
|
|
30044
|
+
root,
|
|
30045
|
+
targetPath: resolved,
|
|
30046
|
+
expectedIdentity: writtenStat,
|
|
30047
|
+
});
|
|
30048
|
+
created = false;
|
|
30049
|
+
}
|
|
30050
|
+
catch (err) {
|
|
30051
|
+
if (hasNodeErrorCode(err, "EEXIST")) {
|
|
30052
|
+
throw new errors_FsSafeError("already-exists", "file already exists", {
|
|
30053
|
+
cause: err instanceof Error ? err : undefined,
|
|
30054
|
+
});
|
|
30055
|
+
}
|
|
30056
|
+
throw err;
|
|
30057
|
+
}
|
|
30058
|
+
finally {
|
|
30059
|
+
if (created) {
|
|
30060
|
+
await promises_.rm(resolved, { force: true }).catch(() => undefined);
|
|
30061
|
+
}
|
|
30062
|
+
}
|
|
30063
|
+
}
|
|
30064
|
+
async function copyFileFallback(root, params, source) {
|
|
30065
|
+
let target = null;
|
|
30066
|
+
let sourceClosedByStream = false;
|
|
30067
|
+
let targetClosedByUs = false;
|
|
30068
|
+
let tempHandle = null;
|
|
30069
|
+
let tempPath = null;
|
|
30070
|
+
let unregisterTempPath = null;
|
|
30071
|
+
let tempClosedByStream = false;
|
|
30072
|
+
try {
|
|
30073
|
+
target = await openWritableFileInRoot(root, {
|
|
30074
|
+
relativePath: params.relativePath,
|
|
30075
|
+
mkdir: params.mkdir,
|
|
30076
|
+
mode: params.mode,
|
|
30077
|
+
truncateExisting: false,
|
|
30078
|
+
});
|
|
30079
|
+
const destinationPath = target.realPath;
|
|
30080
|
+
const mode = params.mode ?? (target.stat.mode & 0o777);
|
|
30081
|
+
await target.handle.close().catch(() => { });
|
|
30082
|
+
targetClosedByUs = true;
|
|
30083
|
+
const destinationGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(destinationPath));
|
|
30084
|
+
tempPath = buildAtomicWriteTempPath(destinationPath);
|
|
30085
|
+
unregisterTempPath = registerTempPathForExit(tempPath);
|
|
30086
|
+
tempHandle = await promises_.open(tempPath, OPEN_WRITE_CREATE_FLAGS, mode || 0o600);
|
|
30087
|
+
const sourceStream = createBoundedReadStream(source, params.maxBytes);
|
|
30088
|
+
const targetStream = tempHandle.createWriteStream();
|
|
30089
|
+
sourceStream.once("close", () => {
|
|
30090
|
+
sourceClosedByStream = true;
|
|
30091
|
+
});
|
|
30092
|
+
targetStream.once("close", () => {
|
|
30093
|
+
tempClosedByStream = true;
|
|
30094
|
+
});
|
|
30095
|
+
await (0,external_node_stream_promises_.pipeline)(sourceStream, targetStream);
|
|
30096
|
+
const writtenStat = await promises_.stat(tempPath);
|
|
30097
|
+
if (!tempClosedByStream) {
|
|
30098
|
+
await tempHandle.close().catch(() => { });
|
|
30099
|
+
tempClosedByStream = true;
|
|
30100
|
+
}
|
|
30101
|
+
tempHandle = null;
|
|
30102
|
+
const commitTempPath = tempPath;
|
|
30103
|
+
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
30104
|
+
await promises_.rename(commitTempPath, destinationPath);
|
|
30105
|
+
});
|
|
30106
|
+
tempPath = null;
|
|
30107
|
+
unregisterTempPath();
|
|
30108
|
+
unregisterTempPath = null;
|
|
30109
|
+
try {
|
|
30110
|
+
await verifyAtomicWriteResult({
|
|
30111
|
+
root,
|
|
30112
|
+
targetPath: destinationPath,
|
|
30113
|
+
expectedIdentity: writtenStat,
|
|
30114
|
+
});
|
|
30115
|
+
}
|
|
30116
|
+
catch (err) {
|
|
30117
|
+
emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
|
|
30118
|
+
throw err;
|
|
30119
|
+
}
|
|
30120
|
+
}
|
|
30121
|
+
catch (err) {
|
|
30122
|
+
if (target?.createdForWrite) {
|
|
30123
|
+
await promises_.rm(target.realPath, { force: true }).catch(() => { });
|
|
30124
|
+
}
|
|
30125
|
+
throw err;
|
|
30126
|
+
}
|
|
30127
|
+
finally {
|
|
30128
|
+
if (!sourceClosedByStream) {
|
|
30129
|
+
await source.handle.close().catch(() => { });
|
|
30130
|
+
}
|
|
30131
|
+
if (tempHandle && !tempClosedByStream) {
|
|
30132
|
+
await tempHandle.close().catch(() => { });
|
|
30133
|
+
}
|
|
30134
|
+
if (tempPath) {
|
|
30135
|
+
await promises_.rm(tempPath, { force: true }).catch(() => { });
|
|
30136
|
+
}
|
|
30137
|
+
unregisterTempPath?.();
|
|
30138
|
+
if (target && !targetClosedByUs) {
|
|
30139
|
+
await target.handle.close().catch(() => { });
|
|
30140
|
+
}
|
|
30141
|
+
}
|
|
26524
30142
|
}
|
|
26525
|
-
/**
|
|
26526
|
-
* List of known action names for convenience
|
|
26527
|
-
*/ const KNOWN_ACTIONS = Object.keys(DEFAULT_ACTION_VERSIONS);
|
|
26528
30143
|
|
|
26529
|
-
// EXTERNAL MODULE: external "node:fs"
|
|
26530
|
-
var external_node_fs_ = __webpack_require__(3024);
|
|
26531
30144
|
;// CONCATENATED MODULE: ../core/src/gateways/file-system-utils.ts
|
|
26532
30145
|
/**
|
|
26533
30146
|
* Shared file system utilities for gateways.
|
|
26534
30147
|
*/
|
|
26535
30148
|
|
|
26536
30149
|
|
|
30150
|
+
|
|
26537
30151
|
/**
|
|
26538
30152
|
* Checks if a file or directory exists.
|
|
26539
30153
|
*/ async function file_system_utils_fileExists(path) {
|
|
@@ -26547,9 +30161,92 @@ var external_node_fs_ = __webpack_require__(3024);
|
|
|
26547
30161
|
function isPathWithinRoot(rootPath, candidatePath) {
|
|
26548
30162
|
return candidatePath === rootPath || candidatePath.startsWith(`${rootPath}${external_node_path_.sep}`);
|
|
26549
30163
|
}
|
|
30164
|
+
function mapFsSafeError(error, relativePath) {
|
|
30165
|
+
if (error instanceof errors_FsSafeError) {
|
|
30166
|
+
switch(error.code){
|
|
30167
|
+
case "outside-workspace":
|
|
30168
|
+
case "invalid-path":
|
|
30169
|
+
throw new Error(`Resolved path is outside target directory: ${relativePath}`, {
|
|
30170
|
+
cause: error
|
|
30171
|
+
});
|
|
30172
|
+
case "path-alias":
|
|
30173
|
+
case "symlink":
|
|
30174
|
+
throw new Error(`Symlinks are not allowed: path contains symbolic link: ${relativePath}`, {
|
|
30175
|
+
cause: error
|
|
30176
|
+
});
|
|
30177
|
+
case "too-large":
|
|
30178
|
+
throw new Error(`File ${relativePath} exceeds size limit: ${error.message}`, {
|
|
30179
|
+
cause: error
|
|
30180
|
+
});
|
|
30181
|
+
default:
|
|
30182
|
+
throw error;
|
|
30183
|
+
}
|
|
30184
|
+
}
|
|
30185
|
+
throw error;
|
|
30186
|
+
}
|
|
30187
|
+
async function createSafeRoot(targetDir, maxBytes) {
|
|
30188
|
+
return root_impl_root(targetDir, {
|
|
30189
|
+
hardlinks: "reject",
|
|
30190
|
+
maxBytes,
|
|
30191
|
+
symlinks: "reject"
|
|
30192
|
+
});
|
|
30193
|
+
}
|
|
30194
|
+
async function file_system_utils_readTextWithinRoot(targetDir, relativePath, maxBytes) {
|
|
30195
|
+
try {
|
|
30196
|
+
const safeRoot = await createSafeRoot(targetDir, maxBytes);
|
|
30197
|
+
return await safeRoot.readText(relativePath, {
|
|
30198
|
+
maxBytes
|
|
30199
|
+
});
|
|
30200
|
+
} catch (error) {
|
|
30201
|
+
mapFsSafeError(error, relativePath);
|
|
30202
|
+
}
|
|
30203
|
+
}
|
|
30204
|
+
async function file_system_utils_listDirectoryWithinRoot(targetDir, relativePath) {
|
|
30205
|
+
try {
|
|
30206
|
+
const safeRoot = await createSafeRoot(targetDir);
|
|
30207
|
+
const entries = await safeRoot.list(relativePath, {
|
|
30208
|
+
withFileTypes: true
|
|
30209
|
+
});
|
|
30210
|
+
return entries.map(toSafeDirEntry);
|
|
30211
|
+
} catch (error) {
|
|
30212
|
+
mapFsSafeError(error, relativePath);
|
|
30213
|
+
}
|
|
30214
|
+
}
|
|
30215
|
+
function toSafeDirEntry(entry) {
|
|
30216
|
+
return {
|
|
30217
|
+
name: entry.name,
|
|
30218
|
+
isDirectory: ()=>entry.isDirectory,
|
|
30219
|
+
isFile: ()=>entry.isFile,
|
|
30220
|
+
isSymbolicLink: ()=>entry.isSymbolicLink
|
|
30221
|
+
};
|
|
30222
|
+
}
|
|
30223
|
+
async function file_system_utils_pathExistsWithinRoot(targetDir, relativePath) {
|
|
30224
|
+
try {
|
|
30225
|
+
const safeRoot = await createSafeRoot(targetDir);
|
|
30226
|
+
return await safeRoot.exists(relativePath);
|
|
30227
|
+
} catch (error) {
|
|
30228
|
+
mapFsSafeError(error, relativePath);
|
|
30229
|
+
}
|
|
30230
|
+
}
|
|
30231
|
+
/**
|
|
30232
|
+
* Returns true if the error originated from an fs-safe security check
|
|
30233
|
+
* (symlink, traversal, or size-limit violation). These errors carry a
|
|
30234
|
+
* FsSafeError as their `cause` and should be re-thrown rather than
|
|
30235
|
+
* silently swallowed by per-file error handlers.
|
|
30236
|
+
*/ function isFsSafeViolation(error) {
|
|
30237
|
+
return error instanceof Error && error.cause instanceof errors_FsSafeError;
|
|
30238
|
+
}
|
|
30239
|
+
async function file_system_utils_statWithinRoot(targetDir, relativePath) {
|
|
30240
|
+
try {
|
|
30241
|
+
const safeRoot = await createSafeRoot(targetDir);
|
|
30242
|
+
return await safeRoot.stat(relativePath);
|
|
30243
|
+
} catch (error) {
|
|
30244
|
+
mapFsSafeError(error, relativePath);
|
|
30245
|
+
}
|
|
30246
|
+
}
|
|
26550
30247
|
/**
|
|
26551
30248
|
* Resolves a relative path under targetDir and rejects traversal outside the root.
|
|
26552
|
-
*/ async function
|
|
30249
|
+
*/ async function file_system_utils_resolvePathWithinRoot(targetDir, relativePath) {
|
|
26553
30250
|
if (!relativePath) {
|
|
26554
30251
|
throw new Error("Path must not be empty");
|
|
26555
30252
|
}
|
|
@@ -26591,14 +30288,14 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
26591
30288
|
/**
|
|
26592
30289
|
* Resolves a relative path under targetDir and validates it does not pass through symlinks.
|
|
26593
30290
|
*/ async function file_system_utils_resolveSafePath(targetDir, relativePath) {
|
|
26594
|
-
const resolvedPath = await
|
|
30291
|
+
const resolvedPath = await file_system_utils_resolvePathWithinRoot(targetDir, relativePath);
|
|
26595
30292
|
await file_system_utils_assertPathHasNoSymbolicLinks(targetDir, resolvedPath);
|
|
26596
30293
|
return resolvedPath;
|
|
26597
30294
|
}
|
|
26598
30295
|
/**
|
|
26599
30296
|
* Enforces a maximum file size before reading/parsing.
|
|
26600
|
-
*/ async function
|
|
26601
|
-
const fileStats = await
|
|
30297
|
+
*/ async function assertFileSizeWithinLimit(filePath, maxBytes, context) {
|
|
30298
|
+
const fileStats = await stat(filePath);
|
|
26602
30299
|
if (fileStats.size > maxBytes) {
|
|
26603
30300
|
throw new Error(`${context} exceeds size limit (${fileStats.size} bytes > ${maxBytes} bytes)`);
|
|
26604
30301
|
}
|
|
@@ -26684,16 +30381,17 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
26684
30381
|
* Handles reading and writing .claude/settings.json and CLAUDE.md files.
|
|
26685
30382
|
*/
|
|
26686
30383
|
|
|
30384
|
+
const MAX_CLAUDE_SETTINGS_BYTES = 1_048_576;
|
|
30385
|
+
const MAX_CLAUDE_DOC_BYTES = 1_048_576;
|
|
26687
30386
|
/**
|
|
26688
30387
|
* File system implementation of ClaudeFileGateway
|
|
26689
30388
|
*/ class FileSystemClaudeFileGateway {
|
|
26690
30389
|
async readSettings(targetDir) {
|
|
26691
|
-
|
|
26692
|
-
if (!await file_system_utils_fileExists(settingsPath)) {
|
|
30390
|
+
if (!await file_system_utils_pathExistsWithinRoot(targetDir, ".claude/settings.json")) {
|
|
26693
30391
|
return null;
|
|
26694
30392
|
}
|
|
30393
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, ".claude/settings.json", MAX_CLAUDE_SETTINGS_BYTES);
|
|
26695
30394
|
try {
|
|
26696
|
-
const content = await (0,promises_.readFile)(settingsPath, "utf-8");
|
|
26697
30395
|
return JSON.parse(content);
|
|
26698
30396
|
} catch {
|
|
26699
30397
|
// If JSON parsing fails, treat as if file doesn't exist
|
|
@@ -26712,11 +30410,10 @@ function isPathWithinRoot(rootPath, candidatePath) {
|
|
|
26712
30410
|
await (0,promises_.writeFile)(settingsPath, content, "utf-8");
|
|
26713
30411
|
}
|
|
26714
30412
|
async readDocumentation(targetDir) {
|
|
26715
|
-
|
|
26716
|
-
if (!await file_system_utils_fileExists(docPath)) {
|
|
30413
|
+
if (!await file_system_utils_pathExistsWithinRoot(targetDir, "CLAUDE.md")) {
|
|
26717
30414
|
return null;
|
|
26718
30415
|
}
|
|
26719
|
-
return (
|
|
30416
|
+
return file_system_utils_readTextWithinRoot(targetDir, "CLAUDE.md", MAX_CLAUDE_DOC_BYTES);
|
|
26720
30417
|
}
|
|
26721
30418
|
async writeDocumentation(targetDir, content) {
|
|
26722
30419
|
const docPath = await file_system_utils_resolveSafePath(targetDir, "CLAUDE.md");
|
|
@@ -26779,7 +30476,7 @@ const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
|
|
|
26779
30476
|
const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
|
|
26780
30477
|
const _PATH_ROOT_RE = /^[/\\]|^[a-zA-Z]:[/\\]/;
|
|
26781
30478
|
const pathe_M_eThtNZ_sep = "/";
|
|
26782
|
-
const
|
|
30479
|
+
const pathe_M_eThtNZ_normalize = function(path) {
|
|
26783
30480
|
if (path.length === 0) {
|
|
26784
30481
|
return ".";
|
|
26785
30482
|
}
|
|
@@ -26827,7 +30524,7 @@ const pathe_M_eThtNZ_join = function(...segments) {
|
|
|
26827
30524
|
path += seg;
|
|
26828
30525
|
}
|
|
26829
30526
|
}
|
|
26830
|
-
return
|
|
30527
|
+
return pathe_M_eThtNZ_normalize(path);
|
|
26831
30528
|
};
|
|
26832
30529
|
function pathe_M_eThtNZ_cwd() {
|
|
26833
30530
|
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
@@ -26980,7 +30677,7 @@ const pathe_M_eThtNZ_parse = function(p) {
|
|
|
26980
30677
|
};
|
|
26981
30678
|
};
|
|
26982
30679
|
const matchesGlob = (path, pattern) => {
|
|
26983
|
-
return zeptomatch(pattern,
|
|
30680
|
+
return zeptomatch(pattern, pathe_M_eThtNZ_normalize(path));
|
|
26984
30681
|
};
|
|
26985
30682
|
|
|
26986
30683
|
const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
|
|
@@ -26992,7 +30689,7 @@ const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
|
|
|
26992
30689
|
isAbsolute: pathe_M_eThtNZ_isAbsolute,
|
|
26993
30690
|
join: pathe_M_eThtNZ_join,
|
|
26994
30691
|
matchesGlob: matchesGlob,
|
|
26995
|
-
normalize:
|
|
30692
|
+
normalize: pathe_M_eThtNZ_normalize,
|
|
26996
30693
|
normalizeString: normalizeString,
|
|
26997
30694
|
parse: pathe_M_eThtNZ_parse,
|
|
26998
30695
|
relative: pathe_M_eThtNZ_relative,
|
|
@@ -27007,8 +30704,6 @@ const pathe_M_eThtNZ_path = (/* unused pure expression or super */ null && ({
|
|
|
27007
30704
|
var lib_main = __webpack_require__(5599);
|
|
27008
30705
|
// EXTERNAL MODULE: external "node:url"
|
|
27009
30706
|
var external_node_url_ = __webpack_require__(3136);
|
|
27010
|
-
// EXTERNAL MODULE: external "node:os"
|
|
27011
|
-
var external_node_os_ = __webpack_require__(8161);
|
|
27012
30707
|
// EXTERNAL MODULE: external "node:assert"
|
|
27013
30708
|
var external_node_assert_ = __webpack_require__(4589);
|
|
27014
30709
|
// EXTERNAL MODULE: external "node:v8"
|
|
@@ -28545,8 +32240,8 @@ function findFarthestFile(filename, options = {}) {
|
|
|
28545
32240
|
});
|
|
28546
32241
|
}
|
|
28547
32242
|
function _resolvePath(id, opts = {}) {
|
|
28548
|
-
if (id instanceof URL || id.startsWith("file://")) return
|
|
28549
|
-
if (pathe_M_eThtNZ_isAbsolute(id)) return
|
|
32243
|
+
if (id instanceof URL || id.startsWith("file://")) return pathe_M_eThtNZ_normalize((0,external_node_url_.fileURLToPath)(id));
|
|
32244
|
+
if (pathe_M_eThtNZ_isAbsolute(id)) return pathe_M_eThtNZ_normalize(id);
|
|
28550
32245
|
return resolveModulePath(id, {
|
|
28551
32246
|
...opts,
|
|
28552
32247
|
from: opts.from || opts.parent || opts.url
|
|
@@ -29155,7 +32850,7 @@ function tryResolve(id, options) {
|
|
|
29155
32850
|
extensions: SUPPORTED_EXTENSIONS,
|
|
29156
32851
|
cache: false
|
|
29157
32852
|
});
|
|
29158
|
-
return res ?
|
|
32853
|
+
return res ? pathe_M_eThtNZ_normalize(res) : void 0;
|
|
29159
32854
|
}
|
|
29160
32855
|
//#endregion
|
|
29161
32856
|
//#region src/types.ts
|
|
@@ -29603,11 +33298,9 @@ const CopilotSetupConfigSchema = schemas_object({
|
|
|
29603
33298
|
|
|
29604
33299
|
|
|
29605
33300
|
|
|
29606
|
-
|
|
29607
33301
|
const MAX_VERSION_FILE_BYTES = 16 * 1024;
|
|
29608
|
-
async function readVersionFileContent(
|
|
29609
|
-
await
|
|
29610
|
-
const content = await (0,promises_.readFile)(filePath, "utf-8");
|
|
33302
|
+
async function readVersionFileContent(targetDir, relativePath) {
|
|
33303
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, relativePath, MAX_VERSION_FILE_BYTES);
|
|
29611
33304
|
return content.trim();
|
|
29612
33305
|
}
|
|
29613
33306
|
/**
|
|
@@ -29636,16 +33329,14 @@ async function readVersionFileContent(filePath) {
|
|
|
29636
33329
|
};
|
|
29637
33330
|
}
|
|
29638
33331
|
async detectMise(targetDir) {
|
|
29639
|
-
|
|
29640
|
-
return file_system_utils_fileExists(miseTomlPath);
|
|
33332
|
+
return file_system_utils_pathExistsWithinRoot(targetDir, "mise.toml");
|
|
29641
33333
|
}
|
|
29642
33334
|
async detectVersionFiles(targetDir, config) {
|
|
29643
33335
|
const filenameToType = getVersionFilenameToTypeMap(config);
|
|
29644
33336
|
const versionFiles = [];
|
|
29645
33337
|
for (const fileConfig of config.versionFiles){
|
|
29646
|
-
|
|
29647
|
-
|
|
29648
|
-
const version = await readVersionFileContent(filePath);
|
|
33338
|
+
if (await file_system_utils_pathExistsWithinRoot(targetDir, fileConfig.filename)) {
|
|
33339
|
+
const version = await readVersionFileContent(targetDir, fileConfig.filename);
|
|
29649
33340
|
versionFiles.push({
|
|
29650
33341
|
type: filenameToType[fileConfig.filename],
|
|
29651
33342
|
filename: fileConfig.filename,
|
|
@@ -29674,8 +33365,7 @@ async function readVersionFileContent(filePath) {
|
|
|
29674
33365
|
return packageManagers;
|
|
29675
33366
|
}
|
|
29676
33367
|
async detectNodePackageManager(targetDir, nodePackageManagers) {
|
|
29677
|
-
|
|
29678
|
-
if (!await file_system_utils_fileExists(packageJsonPath)) {
|
|
33368
|
+
if (!await file_system_utils_pathExistsWithinRoot(targetDir, "package.json")) {
|
|
29679
33369
|
return null;
|
|
29680
33370
|
}
|
|
29681
33371
|
const lockfileOrder = [
|
|
@@ -29688,8 +33378,7 @@ async function readVersionFileContent(filePath) {
|
|
|
29688
33378
|
if (!pmConfig?.lockfile) {
|
|
29689
33379
|
continue;
|
|
29690
33380
|
}
|
|
29691
|
-
|
|
29692
|
-
if (await file_system_utils_fileExists(lockfilePath)) {
|
|
33381
|
+
if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.lockfile)) {
|
|
29693
33382
|
return {
|
|
29694
33383
|
type: pmConfig.type,
|
|
29695
33384
|
filename: pmConfig.manifestFile,
|
|
@@ -29713,10 +33402,9 @@ async function readVersionFileContent(filePath) {
|
|
|
29713
33402
|
if (!pmConfig) {
|
|
29714
33403
|
continue;
|
|
29715
33404
|
}
|
|
29716
|
-
|
|
29717
|
-
if (await file_system_utils_fileExists(manifestPath)) {
|
|
33405
|
+
if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
|
|
29718
33406
|
const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
|
|
29719
|
-
const hasLockfile = lockfilePath ? await
|
|
33407
|
+
const hasLockfile = lockfilePath ? await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.lockfile) : false;
|
|
29720
33408
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
29721
33409
|
continue;
|
|
29722
33410
|
}
|
|
@@ -29732,10 +33420,9 @@ async function readVersionFileContent(filePath) {
|
|
|
29732
33420
|
async detectOtherPackageManagers(targetDir, otherPackageManagers) {
|
|
29733
33421
|
const packageManagers = [];
|
|
29734
33422
|
for (const pmConfig of otherPackageManagers){
|
|
29735
|
-
|
|
29736
|
-
if (await file_system_utils_fileExists(manifestPath)) {
|
|
33423
|
+
if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
|
|
29737
33424
|
const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
|
|
29738
|
-
const hasLockfile = lockfilePath ? await
|
|
33425
|
+
const hasLockfile = lockfilePath ? await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.lockfile) : false;
|
|
29739
33426
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
29740
33427
|
continue;
|
|
29741
33428
|
}
|
|
@@ -29756,8 +33443,6 @@ async function readVersionFileContent(filePath) {
|
|
|
29756
33443
|
return new FileSystemEnvironmentGateway(cwd);
|
|
29757
33444
|
}
|
|
29758
33445
|
|
|
29759
|
-
// EXTERNAL MODULE: external "node:child_process"
|
|
29760
|
-
var external_node_child_process_ = __webpack_require__(1421);
|
|
29761
33446
|
;// CONCATENATED MODULE: ../core/src/gateways/github-ruleset-gateway.ts
|
|
29762
33447
|
/**
|
|
29763
33448
|
* Gateway for interacting with GitHub repository rulesets via Octokit.
|
|
@@ -29936,8 +33621,6 @@ function defaultExec(command, args, options) {
|
|
|
29936
33621
|
return new OctokitRulesetGateway(octokit);
|
|
29937
33622
|
}
|
|
29938
33623
|
|
|
29939
|
-
// EXTERNAL MODULE: external "node:crypto"
|
|
29940
|
-
var external_node_crypto_ = __webpack_require__(7598);
|
|
29941
33624
|
;// CONCATENATED MODULE: ../core/src/gateways/init-hooks-config-gateway.ts
|
|
29942
33625
|
|
|
29943
33626
|
|
|
@@ -30175,7 +33858,7 @@ function createInitHooksConfigGateway() {
|
|
|
30175
33858
|
* Gateway for analyzing repository instructions for feedback loop coverage
|
|
30176
33859
|
*/
|
|
30177
33860
|
|
|
30178
|
-
|
|
33861
|
+
const MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
|
|
30179
33862
|
/**
|
|
30180
33863
|
* File system implementation of instruction analysis gateway
|
|
30181
33864
|
*/ class FileSystemInstructionAnalysisGateway {
|
|
@@ -30184,17 +33867,17 @@ function createInitHooksConfigGateway() {
|
|
|
30184
33867
|
const references = [];
|
|
30185
33868
|
const documentedTargets = new Set();
|
|
30186
33869
|
for (const file of instructionFiles){
|
|
30187
|
-
const content = await (
|
|
33870
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, file, MAX_INSTRUCTION_FILE_BYTES);
|
|
30188
33871
|
const lines = content.split("\n");
|
|
30189
33872
|
for (const script of scripts){
|
|
30190
|
-
const scriptRefs = this.findReferencesInContent(script.name, content, lines, file, targetDir);
|
|
33873
|
+
const scriptRefs = this.findReferencesInContent(script.name, content, lines, (0,external_node_path_.join)(targetDir, file), targetDir);
|
|
30191
33874
|
if (scriptRefs.length > 0) {
|
|
30192
33875
|
references.push(...scriptRefs);
|
|
30193
33876
|
documentedTargets.add(script.name);
|
|
30194
33877
|
}
|
|
30195
33878
|
}
|
|
30196
33879
|
for (const tool of tools){
|
|
30197
|
-
const toolRefs = this.findReferencesInContent(tool.name, content, lines, file, targetDir);
|
|
33880
|
+
const toolRefs = this.findReferencesInContent(tool.name, content, lines, (0,external_node_path_.join)(targetDir, file), targetDir);
|
|
30198
33881
|
if (toolRefs.length > 0) {
|
|
30199
33882
|
references.push(...toolRefs);
|
|
30200
33883
|
documentedTargets.add(tool.name);
|
|
@@ -30223,19 +33906,26 @@ function createInitHooksConfigGateway() {
|
|
|
30223
33906
|
}
|
|
30224
33907
|
};
|
|
30225
33908
|
}
|
|
33909
|
+
async safePathExists(targetDir, relativePath) {
|
|
33910
|
+
try {
|
|
33911
|
+
return await file_system_utils_pathExistsWithinRoot(targetDir, relativePath);
|
|
33912
|
+
} catch {
|
|
33913
|
+
return false;
|
|
33914
|
+
}
|
|
33915
|
+
}
|
|
30226
33916
|
async findInstructionFiles(targetDir) {
|
|
30227
33917
|
const files = [];
|
|
30228
|
-
const copilotInstructions = (0,external_node_path_.join)(
|
|
30229
|
-
if (await
|
|
33918
|
+
const copilotInstructions = (0,external_node_path_.join)(".github", "copilot-instructions.md");
|
|
33919
|
+
if (await this.safePathExists(targetDir, copilotInstructions)) {
|
|
30230
33920
|
files.push(copilotInstructions);
|
|
30231
33921
|
}
|
|
30232
|
-
const instructionsDir = (0,external_node_path_.join)(
|
|
30233
|
-
if (await
|
|
33922
|
+
const instructionsDir = (0,external_node_path_.join)(".github", "instructions");
|
|
33923
|
+
if (await this.safePathExists(targetDir, instructionsDir)) {
|
|
30234
33924
|
try {
|
|
30235
|
-
const instructionFiles = await (
|
|
33925
|
+
const instructionFiles = await file_system_utils_listDirectoryWithinRoot(targetDir, instructionsDir);
|
|
30236
33926
|
for (const file of instructionFiles){
|
|
30237
|
-
if (file.endsWith(".md")) {
|
|
30238
|
-
files.push((0,external_node_path_.join)(instructionsDir, file));
|
|
33927
|
+
if (file.isFile() && file.name.endsWith(".md")) {
|
|
33928
|
+
files.push((0,external_node_path_.join)(instructionsDir, file.name));
|
|
30239
33929
|
}
|
|
30240
33930
|
}
|
|
30241
33931
|
} catch {}
|
|
@@ -30390,7 +34080,6 @@ const lesson_schema_LessonFrontmatterSchema = schemas_object({
|
|
|
30390
34080
|
|
|
30391
34081
|
|
|
30392
34082
|
|
|
30393
|
-
|
|
30394
34083
|
const MAX_LESSON_FILE_BYTES = 1_048_576; // 1 MB
|
|
30395
34084
|
const MAX_LESSON_FILES = 500;
|
|
30396
34085
|
const MAX_AGGREGATE_BYTES = (/* unused pure expression or super */ null && (20 * 1024 * 1024)); // 20 MB
|
|
@@ -30420,12 +34109,16 @@ const LESSONS_RELATIVE_PATH = (0,external_node_path_.join)(".lousy-agents", "les
|
|
|
30420
34109
|
}
|
|
30421
34110
|
class LessonFileGateway {
|
|
30422
34111
|
async readLessons(rootDir) {
|
|
30423
|
-
const
|
|
30424
|
-
|
|
30425
|
-
|
|
30426
|
-
|
|
34112
|
+
const lessonsDir = join(rootDir, LESSONS_RELATIVE_PATH);
|
|
34113
|
+
if (!await pathExistsWithinRoot(rootDir, LESSONS_RELATIVE_PATH)) {
|
|
34114
|
+
return {
|
|
34115
|
+
lessons: [],
|
|
34116
|
+
errors: []
|
|
34117
|
+
};
|
|
34118
|
+
}
|
|
34119
|
+
let lessonsStat;
|
|
30427
34120
|
try {
|
|
30428
|
-
|
|
34121
|
+
lessonsStat = await statWithinRoot(rootDir, LESSONS_RELATIVE_PATH);
|
|
30429
34122
|
} catch (error) {
|
|
30430
34123
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
30431
34124
|
return {
|
|
@@ -30435,14 +34128,12 @@ class LessonFileGateway {
|
|
|
30435
34128
|
}
|
|
30436
34129
|
throw error;
|
|
30437
34130
|
}
|
|
30438
|
-
if (!
|
|
34131
|
+
if (!lessonsStat.isDirectory) {
|
|
30439
34132
|
throw new Error(`Lessons path exists but is not a directory: ${lessonsDir}`);
|
|
30440
34133
|
}
|
|
30441
34134
|
let entries;
|
|
30442
34135
|
try {
|
|
30443
|
-
entries = await
|
|
30444
|
-
withFileTypes: true
|
|
30445
|
-
});
|
|
34136
|
+
entries = await listDirectoryWithinRoot(rootDir, LESSONS_RELATIVE_PATH);
|
|
30446
34137
|
} catch (error) {
|
|
30447
34138
|
const reason = error instanceof Error ? error.message : String(error);
|
|
30448
34139
|
const wrapped = new Error(`Cannot read lessons directory ${lessonsDir}: ${reason}`);
|
|
@@ -30453,7 +34144,7 @@ class LessonFileGateway {
|
|
|
30453
34144
|
}
|
|
30454
34145
|
throw wrapped;
|
|
30455
34146
|
}
|
|
30456
|
-
const mdFiles = entries.filter((e)=>e.isFile() && e.name.endsWith(".md")).map((e)=>join(
|
|
34147
|
+
const mdFiles = entries.filter((e)=>e.isFile() && e.name.endsWith(".md")).map((e)=>join(LESSONS_RELATIVE_PATH, e.name)).sort();
|
|
30457
34148
|
if (mdFiles.length > MAX_LESSON_FILES) {
|
|
30458
34149
|
throw new Error(`Lesson file count exceeds limit: ${mdFiles.length} files found (max ${MAX_LESSON_FILES})`);
|
|
30459
34150
|
}
|
|
@@ -30461,13 +34152,14 @@ class LessonFileGateway {
|
|
|
30461
34152
|
const errors = [];
|
|
30462
34153
|
let aggregateBytes = 0;
|
|
30463
34154
|
for (const filePath of mdFiles){
|
|
34155
|
+
const absoluteFilePath = join(rootDir, filePath);
|
|
30464
34156
|
let content;
|
|
30465
34157
|
try {
|
|
30466
|
-
content = await
|
|
34158
|
+
content = await readTextWithinRoot(rootDir, filePath, MAX_LESSON_FILE_BYTES);
|
|
30467
34159
|
} catch (error) {
|
|
30468
34160
|
const reason = error instanceof Error ? error.message : String(error);
|
|
30469
34161
|
errors.push({
|
|
30470
|
-
filePath,
|
|
34162
|
+
filePath: absoluteFilePath,
|
|
30471
34163
|
reason
|
|
30472
34164
|
});
|
|
30473
34165
|
continue;
|
|
@@ -30481,7 +34173,7 @@ class LessonFileGateway {
|
|
|
30481
34173
|
if (!parseResult.ok) {
|
|
30482
34174
|
const reason = parseResult.reason === "invalid" ? `Invalid YAML frontmatter: ${parseResult.detail}` : "Missing YAML frontmatter";
|
|
30483
34175
|
errors.push({
|
|
30484
|
-
filePath,
|
|
34176
|
+
filePath: absoluteFilePath,
|
|
30485
34177
|
reason
|
|
30486
34178
|
});
|
|
30487
34179
|
continue;
|
|
@@ -30490,7 +34182,7 @@ class LessonFileGateway {
|
|
|
30490
34182
|
if (!schemaResult.success) {
|
|
30491
34183
|
const reason = schemaResult.error.issues.map((i)=>`${i.path.join(".")}: ${i.message}`).join("; ");
|
|
30492
34184
|
errors.push({
|
|
30493
|
-
filePath,
|
|
34185
|
+
filePath: absoluteFilePath,
|
|
30494
34186
|
reason
|
|
30495
34187
|
});
|
|
30496
34188
|
continue;
|
|
@@ -30513,7 +34205,7 @@ class LessonFileGateway {
|
|
|
30513
34205
|
};
|
|
30514
34206
|
lessons.push({
|
|
30515
34207
|
lesson,
|
|
30516
|
-
filePath
|
|
34208
|
+
filePath: absoluteFilePath
|
|
30517
34209
|
});
|
|
30518
34210
|
}
|
|
30519
34211
|
return {
|
|
@@ -31123,11 +34815,11 @@ var external_node_tty_namespaceObject = /*#__PURE__*/__webpack_require__.t(exter
|
|
|
31123
34815
|
const {
|
|
31124
34816
|
env: consola_DXBYu_KD_env = {},
|
|
31125
34817
|
argv = [],
|
|
31126
|
-
platform = ""
|
|
34818
|
+
platform: consola_DXBYu_KD_platform = ""
|
|
31127
34819
|
} = typeof process === "undefined" ? {} : process;
|
|
31128
34820
|
const isDisabled = "NO_COLOR" in consola_DXBYu_KD_env || argv.includes("--no-color");
|
|
31129
34821
|
const isForced = "FORCE_COLOR" in consola_DXBYu_KD_env || argv.includes("--color");
|
|
31130
|
-
const consola_DXBYu_KD_isWindows =
|
|
34822
|
+
const consola_DXBYu_KD_isWindows = consola_DXBYu_KD_platform === "win32";
|
|
31131
34823
|
const isDumbTerminal = consola_DXBYu_KD_env.TERM === "dumb";
|
|
31132
34824
|
const isCompatibleTerminal = external_node_tty_namespaceObject && external_node_tty_.isatty && external_node_tty_.isatty(1) && consola_DXBYu_KD_env.TERM && !isDumbTerminal;
|
|
31133
34825
|
const isCI = "CI" in consola_DXBYu_KD_env && ("GITHUB_ACTIONS" in consola_DXBYu_KD_env || "GITLAB_CI" in consola_DXBYu_KD_env || "CIRCLECI" in consola_DXBYu_KD_env);
|
|
@@ -32077,12 +35769,10 @@ const MAX_NPMRC_BYTES = (/* unused pure expression or super */ null && (64 * 102
|
|
|
32077
35769
|
this.dryRun = dryRun;
|
|
32078
35770
|
}
|
|
32079
35771
|
async readNpmrc(targetDir) {
|
|
32080
|
-
|
|
32081
|
-
if (!await fileExists(npmrcPath)) {
|
|
35772
|
+
if (!await pathExistsWithinRoot(targetDir, ".npmrc")) {
|
|
32082
35773
|
return null;
|
|
32083
35774
|
}
|
|
32084
|
-
|
|
32085
|
-
return readFile(npmrcPath, "utf-8");
|
|
35775
|
+
return readTextWithinRoot(targetDir, ".npmrc", MAX_NPMRC_BYTES);
|
|
32086
35776
|
}
|
|
32087
35777
|
async writeNpmrc(targetDir, content) {
|
|
32088
35778
|
const npmrcPath = await resolveSafePath(targetDir, ".npmrc");
|
|
@@ -32341,7 +36031,6 @@ const PackageJsonSchema = schemas_object({
|
|
|
32341
36031
|
*/
|
|
32342
36032
|
|
|
32343
36033
|
|
|
32344
|
-
|
|
32345
36034
|
/** Maximum skill file size: 1 MB */ const MAX_SKILL_FILE_BYTES = 1_048_576;
|
|
32346
36035
|
/**
|
|
32347
36036
|
* Skill directory locations to search for SKILL.md files.
|
|
@@ -32356,30 +36045,22 @@ const PackageJsonSchema = schemas_object({
|
|
|
32356
36045
|
async discoverSkills(targetDir) {
|
|
32357
36046
|
const skills = [];
|
|
32358
36047
|
for (const relativeDir of SKILL_DIRECTORIES){
|
|
32359
|
-
const
|
|
32360
|
-
const discovered = await this.discoverSkillsInDir(skillsDir);
|
|
36048
|
+
const discovered = await this.discoverSkillsInDir(targetDir, relativeDir);
|
|
32361
36049
|
skills.push(...discovered);
|
|
32362
36050
|
}
|
|
32363
36051
|
return skills;
|
|
32364
36052
|
}
|
|
32365
|
-
async discoverSkillsInDir(skillsDir) {
|
|
32366
|
-
let dirStats;
|
|
36053
|
+
async discoverSkillsInDir(targetDir, skillsDir) {
|
|
32367
36054
|
try {
|
|
32368
|
-
|
|
32369
|
-
} catch (error) {
|
|
32370
|
-
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
36055
|
+
if (!await pathExistsWithinRoot(targetDir, skillsDir)) {
|
|
32371
36056
|
return [];
|
|
32372
36057
|
}
|
|
32373
|
-
|
|
32374
|
-
}
|
|
32375
|
-
if (dirStats.isSymbolicLink() || !dirStats.isDirectory()) {
|
|
36058
|
+
} catch {
|
|
32376
36059
|
return [];
|
|
32377
36060
|
}
|
|
32378
36061
|
let entries;
|
|
32379
36062
|
try {
|
|
32380
|
-
entries = await
|
|
32381
|
-
withFileTypes: true
|
|
32382
|
-
});
|
|
36063
|
+
entries = await listDirectoryWithinRoot(targetDir, skillsDir);
|
|
32383
36064
|
} catch (error) {
|
|
32384
36065
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
32385
36066
|
return [];
|
|
@@ -32394,20 +36075,16 @@ const PackageJsonSchema = schemas_object({
|
|
|
32394
36075
|
if (entry.name.includes("..") || entry.name.includes("/") || entry.name.includes("\\")) {
|
|
32395
36076
|
continue;
|
|
32396
36077
|
}
|
|
32397
|
-
const
|
|
32398
|
-
let
|
|
36078
|
+
const skillRelativePath = join(skillsDir, entry.name, "SKILL.md");
|
|
36079
|
+
let hasSkillFile = false;
|
|
32399
36080
|
try {
|
|
32400
|
-
|
|
32401
|
-
} catch
|
|
32402
|
-
|
|
32403
|
-
skillStat = null;
|
|
32404
|
-
} else {
|
|
32405
|
-
throw error;
|
|
32406
|
-
}
|
|
36081
|
+
hasSkillFile = await pathExistsWithinRoot(targetDir, skillRelativePath);
|
|
36082
|
+
} catch {
|
|
36083
|
+
hasSkillFile = false;
|
|
32407
36084
|
}
|
|
32408
|
-
if (
|
|
36085
|
+
if (hasSkillFile) {
|
|
32409
36086
|
skills.push({
|
|
32410
|
-
filePath:
|
|
36087
|
+
filePath: join(targetDir, skillRelativePath),
|
|
32411
36088
|
skillName: entry.name
|
|
32412
36089
|
});
|
|
32413
36090
|
}
|
|
@@ -32434,26 +36111,30 @@ const PackageJsonSchema = schemas_object({
|
|
|
32434
36111
|
|
|
32435
36112
|
|
|
32436
36113
|
|
|
32437
|
-
|
|
36114
|
+
const MAX_WORKFLOW_FILE_BYTES = 1_048_576;
|
|
32438
36115
|
/**
|
|
32439
36116
|
* File system implementation of tool discovery gateway
|
|
32440
36117
|
*/ class FileSystemToolDiscoveryGateway {
|
|
32441
36118
|
async discoverTools(targetDir) {
|
|
32442
|
-
const workflowsDir = (0,external_node_path_.join)(
|
|
32443
|
-
if (!await
|
|
36119
|
+
const workflowsDir = (0,external_node_path_.join)(".github", "workflows");
|
|
36120
|
+
if (!await file_system_utils_pathExistsWithinRoot(targetDir, workflowsDir)) {
|
|
32444
36121
|
return [];
|
|
32445
36122
|
}
|
|
32446
|
-
const files = await (
|
|
32447
|
-
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
36123
|
+
const files = await file_system_utils_listDirectoryWithinRoot(targetDir, workflowsDir);
|
|
36124
|
+
const yamlFiles = files.filter((f)=>f.isFile() && (f.name.endsWith(".yml") || f.name.endsWith(".yaml")));
|
|
32448
36125
|
const allTools = [];
|
|
32449
36126
|
for (const file of yamlFiles){
|
|
32450
|
-
const filePath = (0,external_node_path_.join)(workflowsDir, file);
|
|
36127
|
+
const filePath = (0,external_node_path_.join)(workflowsDir, file.name);
|
|
32451
36128
|
try {
|
|
32452
|
-
const content = await (
|
|
36129
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, filePath, MAX_WORKFLOW_FILE_BYTES);
|
|
32453
36130
|
const workflow = (0,yaml_dist/* .parse */.qg)(content);
|
|
32454
|
-
const tools = this.extractToolsFromWorkflow(workflow, file);
|
|
36131
|
+
const tools = this.extractToolsFromWorkflow(workflow, file.name);
|
|
32455
36132
|
allTools.push(...tools);
|
|
32456
|
-
} catch
|
|
36133
|
+
} catch (error) {
|
|
36134
|
+
if (isFsSafeViolation(error)) {
|
|
36135
|
+
throw error;
|
|
36136
|
+
}
|
|
36137
|
+
}
|
|
32457
36138
|
}
|
|
32458
36139
|
return this.deduplicateTools(allTools);
|
|
32459
36140
|
}
|
|
@@ -32768,7 +36449,7 @@ const COPILOT_SETUP_WORKFLOW_FILENAMES = [
|
|
|
32768
36449
|
"copilot-setup-steps.yml",
|
|
32769
36450
|
"copilot-setup-steps.yaml"
|
|
32770
36451
|
];
|
|
32771
|
-
const
|
|
36452
|
+
const file_system_workflow_gateway_MAX_WORKFLOW_FILE_BYTES = 1024 * 1024;
|
|
32772
36453
|
class FileSystemWorkflowGateway {
|
|
32773
36454
|
cwd;
|
|
32774
36455
|
config = null;
|
|
@@ -32783,31 +36464,35 @@ class FileSystemWorkflowGateway {
|
|
|
32783
36464
|
}
|
|
32784
36465
|
async findCopilotSetupWorkflowPath(targetDir) {
|
|
32785
36466
|
for (const filename of COPILOT_SETUP_WORKFLOW_FILENAMES){
|
|
32786
|
-
const
|
|
32787
|
-
if (await
|
|
32788
|
-
return
|
|
36467
|
+
const relativePath = `.github/workflows/${filename}`;
|
|
36468
|
+
if (await file_system_utils_pathExistsWithinRoot(targetDir, relativePath)) {
|
|
36469
|
+
return file_system_utils_resolveSafePath(targetDir, relativePath);
|
|
32789
36470
|
}
|
|
32790
36471
|
}
|
|
32791
36472
|
return null;
|
|
32792
36473
|
}
|
|
32793
36474
|
async parseWorkflowsForSetupActions(targetDir) {
|
|
32794
|
-
const
|
|
32795
|
-
if (!await
|
|
36475
|
+
const workflowsRelativeDir = ".github/workflows";
|
|
36476
|
+
if (!await file_system_utils_pathExistsWithinRoot(targetDir, workflowsRelativeDir)) {
|
|
32796
36477
|
return [];
|
|
32797
36478
|
}
|
|
32798
|
-
const files = await (
|
|
32799
|
-
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
36479
|
+
const files = await file_system_utils_listDirectoryWithinRoot(targetDir, workflowsRelativeDir);
|
|
36480
|
+
const yamlFiles = files.filter((f)=>f.isFile() && (f.name.endsWith(".yml") || f.name.endsWith(".yaml")));
|
|
32800
36481
|
const config = await this.getConfig();
|
|
32801
36482
|
const allCandidates = [];
|
|
32802
36483
|
for (const file of yamlFiles){
|
|
32803
|
-
const
|
|
32804
|
-
await file_system_utils_assertFileSizeWithinLimit(filePath, MAX_WORKFLOW_FILE_BYTES, `Workflow file '${file}'`);
|
|
36484
|
+
const relativePath = `.github/workflows/${file.name}`;
|
|
32805
36485
|
try {
|
|
32806
|
-
const content = await (
|
|
36486
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, relativePath, file_system_workflow_gateway_MAX_WORKFLOW_FILE_BYTES);
|
|
32807
36487
|
const workflow = (0,yaml_dist/* .parse */.qg)(content);
|
|
32808
36488
|
const candidates = extractSetupStepsFromWorkflow(workflow, config.setupActionPatterns);
|
|
32809
36489
|
allCandidates.push(...candidates);
|
|
32810
|
-
} catch
|
|
36490
|
+
} catch (error) {
|
|
36491
|
+
if (isFsSafeViolation(error)) {
|
|
36492
|
+
throw error;
|
|
36493
|
+
}
|
|
36494
|
+
// Skip workflow files with YAML parse errors or transient I/O failures
|
|
36495
|
+
}
|
|
32811
36496
|
}
|
|
32812
36497
|
return deduplicateCandidates(allCandidates);
|
|
32813
36498
|
}
|
|
@@ -32827,8 +36512,8 @@ class FileSystemWorkflowGateway {
|
|
|
32827
36512
|
if (!workflowPath) {
|
|
32828
36513
|
return null;
|
|
32829
36514
|
}
|
|
32830
|
-
|
|
32831
|
-
const content = await (
|
|
36515
|
+
const workflowRelativePath = workflowPath.endsWith("copilot-setup-steps.yaml") ? ".github/workflows/copilot-setup-steps.yaml" : ".github/workflows/copilot-setup-steps.yml";
|
|
36516
|
+
const content = await file_system_utils_readTextWithinRoot(targetDir, workflowRelativePath, file_system_workflow_gateway_MAX_WORKFLOW_FILE_BYTES);
|
|
32832
36517
|
return (0,yaml_dist/* .parse */.qg)(content);
|
|
32833
36518
|
}
|
|
32834
36519
|
async writeCopilotSetupWorkflow(targetDir, content) {
|
|
@@ -32907,12 +36592,11 @@ function createWorkflowGateway(cwd) {
|
|
|
32907
36592
|
|
|
32908
36593
|
|
|
32909
36594
|
|
|
32910
|
-
|
|
32911
36595
|
/**
|
|
32912
36596
|
* Parses a workflow file and extracts action references.
|
|
32913
|
-
*/ async function parseWorkflowFile(
|
|
36597
|
+
*/ async function parseWorkflowFile(rootDir, relativePath) {
|
|
32914
36598
|
try {
|
|
32915
|
-
const content = await (
|
|
36599
|
+
const content = await file_system_utils_readTextWithinRoot(rootDir, relativePath, 1_048_576);
|
|
32916
36600
|
const workflow = (0,yaml_dist/* .parse */.qg)(content);
|
|
32917
36601
|
return extractActionsFromWorkflow(workflow);
|
|
32918
36602
|
} catch {
|
|
@@ -32941,8 +36625,8 @@ function createWorkflowGateway(cwd) {
|
|
|
32941
36625
|
if (!await file_system_utils_fileExists(dir)) {
|
|
32942
36626
|
return types_errorResponse(`Target directory does not exist: ${dir}`);
|
|
32943
36627
|
}
|
|
32944
|
-
const
|
|
32945
|
-
if (!await
|
|
36628
|
+
const workflowsRelativeDir = (0,external_node_path_.join)(".github", "workflows");
|
|
36629
|
+
if (!await file_system_utils_pathExistsWithinRoot(dir, workflowsRelativeDir)) {
|
|
32946
36630
|
return successResponse({
|
|
32947
36631
|
workflows: [],
|
|
32948
36632
|
uniqueActions: [],
|
|
@@ -32950,12 +36634,13 @@ function createWorkflowGateway(cwd) {
|
|
|
32950
36634
|
});
|
|
32951
36635
|
}
|
|
32952
36636
|
// Read all workflow files
|
|
32953
|
-
const
|
|
36637
|
+
const entries = await file_system_utils_listDirectoryWithinRoot(dir, workflowsRelativeDir);
|
|
36638
|
+
const files = entries.filter((entry)=>entry.isFile()).map((entry)=>entry.name);
|
|
32954
36639
|
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
32955
36640
|
const workflows = [];
|
|
32956
36641
|
for (const file of yamlFiles){
|
|
32957
|
-
const filePath = (0,external_node_path_.join)(
|
|
32958
|
-
const actions = await parseWorkflowFile(filePath);
|
|
36642
|
+
const filePath = (0,external_node_path_.join)(workflowsRelativeDir, file);
|
|
36643
|
+
const actions = await parseWorkflowFile(dir, filePath);
|
|
32959
36644
|
if (actions && actions.length > 0) {
|
|
32960
36645
|
workflows.push({
|
|
32961
36646
|
file,
|
|
@@ -32983,53 +36668,57 @@ function createWorkflowGateway(cwd) {
|
|
|
32983
36668
|
* Supports GitHub Copilot, Claude Code, and community agent instruction formats.
|
|
32984
36669
|
*/
|
|
32985
36670
|
|
|
32986
|
-
|
|
32987
36671
|
/**
|
|
32988
36672
|
* File system implementation of the instruction file discovery gateway.
|
|
32989
36673
|
*/ class FileSystemInstructionFileDiscoveryGateway {
|
|
32990
36674
|
async discoverInstructionFiles(targetDir) {
|
|
32991
36675
|
const files = [];
|
|
32992
36676
|
// .github/copilot-instructions.md
|
|
32993
|
-
const copilotInstructions = (0,external_node_path_.join)(
|
|
32994
|
-
if (await
|
|
36677
|
+
const copilotInstructions = (0,external_node_path_.join)(".github", "copilot-instructions.md");
|
|
36678
|
+
if (await this.safePathExists(targetDir, copilotInstructions)) {
|
|
32995
36679
|
files.push({
|
|
32996
|
-
filePath: copilotInstructions,
|
|
36680
|
+
filePath: (0,external_node_path_.join)(targetDir, copilotInstructions),
|
|
32997
36681
|
format: "copilot-instructions"
|
|
32998
36682
|
});
|
|
32999
36683
|
}
|
|
33000
36684
|
// .github/instructions/*.md
|
|
33001
|
-
const instructionsDir = (0,external_node_path_.join)(
|
|
33002
|
-
await this.discoverMdFilesInDir(instructionsDir, "copilot-scoped", files);
|
|
36685
|
+
const instructionsDir = (0,external_node_path_.join)(".github", "instructions");
|
|
36686
|
+
await this.discoverMdFilesInDir(targetDir, instructionsDir, "copilot-scoped", files);
|
|
33003
36687
|
// .github/agents/*.md
|
|
33004
|
-
const agentsDir = (0,external_node_path_.join)(
|
|
33005
|
-
await this.discoverMdFilesInDir(agentsDir, "copilot-agent", files);
|
|
36688
|
+
const agentsDir = (0,external_node_path_.join)(".github", "agents");
|
|
36689
|
+
await this.discoverMdFilesInDir(targetDir, agentsDir, "copilot-agent", files);
|
|
33006
36690
|
// AGENTS.md at repo root
|
|
33007
|
-
const agentsMd =
|
|
33008
|
-
if (await
|
|
36691
|
+
const agentsMd = "AGENTS.md";
|
|
36692
|
+
if (await this.safePathExists(targetDir, agentsMd)) {
|
|
33009
36693
|
files.push({
|
|
33010
|
-
filePath: agentsMd,
|
|
36694
|
+
filePath: (0,external_node_path_.join)(targetDir, agentsMd),
|
|
33011
36695
|
format: "agents-md"
|
|
33012
36696
|
});
|
|
33013
36697
|
}
|
|
33014
36698
|
// CLAUDE.md at repo root
|
|
33015
|
-
const claudeMd =
|
|
33016
|
-
if (await
|
|
36699
|
+
const claudeMd = "CLAUDE.md";
|
|
36700
|
+
if (await this.safePathExists(targetDir, claudeMd)) {
|
|
33017
36701
|
files.push({
|
|
33018
|
-
filePath: claudeMd,
|
|
36702
|
+
filePath: (0,external_node_path_.join)(targetDir, claudeMd),
|
|
33019
36703
|
format: "claude-md"
|
|
33020
36704
|
});
|
|
33021
36705
|
}
|
|
33022
36706
|
return files;
|
|
33023
36707
|
}
|
|
33024
|
-
async
|
|
33025
|
-
|
|
36708
|
+
async safePathExists(targetDir, relativePath) {
|
|
36709
|
+
try {
|
|
36710
|
+
return await file_system_utils_pathExistsWithinRoot(targetDir, relativePath);
|
|
36711
|
+
} catch {
|
|
36712
|
+
return false;
|
|
36713
|
+
}
|
|
36714
|
+
}
|
|
36715
|
+
async discoverMdFilesInDir(targetDir, dirPath, format, files) {
|
|
36716
|
+
if (!await this.safePathExists(targetDir, dirPath)) {
|
|
33026
36717
|
return;
|
|
33027
36718
|
}
|
|
33028
36719
|
try {
|
|
33029
|
-
const entries = await (
|
|
33030
|
-
|
|
33031
|
-
});
|
|
33032
|
-
const resolvedDirPath = (0,external_node_path_.resolve)(dirPath);
|
|
36720
|
+
const entries = await file_system_utils_listDirectoryWithinRoot(targetDir, dirPath);
|
|
36721
|
+
const resolvedDirPath = (0,external_node_path_.resolve)(targetDir, dirPath);
|
|
33033
36722
|
for (const entry of entries){
|
|
33034
36723
|
if (entry.isSymbolicLink()) {
|
|
33035
36724
|
continue;
|
|
@@ -33042,7 +36731,7 @@ function createWorkflowGateway(cwd) {
|
|
|
33042
36731
|
continue;
|
|
33043
36732
|
}
|
|
33044
36733
|
if (name.endsWith(".md")) {
|
|
33045
|
-
const filePath = (0,external_node_path_.join)(dirPath, name);
|
|
36734
|
+
const filePath = (0,external_node_path_.join)(targetDir, dirPath, name);
|
|
33046
36735
|
const resolvedFilePath = (0,external_node_path_.resolve)(filePath);
|
|
33047
36736
|
const rel = (0,external_node_path_.relative)(resolvedDirPath, resolvedFilePath);
|
|
33048
36737
|
if (rel.startsWith("..") || rel.startsWith(external_node_path_.sep)) {
|
|
@@ -33058,14 +36747,6 @@ function createWorkflowGateway(cwd) {
|
|
|
33058
36747
|
// Skip directory if we can't read it
|
|
33059
36748
|
}
|
|
33060
36749
|
}
|
|
33061
|
-
async isSymlink(filePath) {
|
|
33062
|
-
try {
|
|
33063
|
-
const stats = await (0,promises_.lstat)(filePath);
|
|
33064
|
-
return stats.isSymbolicLink();
|
|
33065
|
-
} catch {
|
|
33066
|
-
return false;
|
|
33067
|
-
}
|
|
33068
|
-
}
|
|
33069
36750
|
}
|
|
33070
36751
|
/**
|
|
33071
36752
|
* Creates and returns the default instruction file discovery gateway.
|
|
@@ -51906,12 +55587,12 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
51906
55587
|
*/
|
|
51907
55588
|
|
|
51908
55589
|
|
|
51909
|
-
/** Maximum instruction file size: 1 MB */ const
|
|
55590
|
+
/** Maximum instruction file size: 1 MB */ const markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
|
|
51910
55591
|
/**
|
|
51911
55592
|
* Remark-based implementation of the Markdown AST gateway.
|
|
51912
55593
|
*/ class RemarkMarkdownAstGateway {
|
|
51913
55594
|
async parseFile(filePath) {
|
|
51914
|
-
const content = await file_system_utils_readFileNoFollow(filePath,
|
|
55595
|
+
const content = await file_system_utils_readFileNoFollow(filePath, markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES);
|
|
51915
55596
|
return this.parseContent(content);
|
|
51916
55597
|
}
|
|
51917
55598
|
parseContent(content) {
|
|
@@ -64368,4 +68049,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
64368
68049
|
// module factories are used so entry inlining is disabled
|
|
64369
68050
|
// startup
|
|
64370
68051
|
// Load entry module and return exports
|
|
64371
|
-
var __webpack_exports__ = __webpack_require__(
|
|
68052
|
+
var __webpack_exports__ = __webpack_require__(2415);
|