@lousy-agents/mcp 5.18.0 → 5.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-server.js +1669 -1526
- package/package.json +3 -2
package/dist/mcp-server.js
CHANGED
|
@@ -6559,7 +6559,7 @@ function escapeJsonPtr(str) {
|
|
|
6559
6559
|
|
|
6560
6560
|
|
|
6561
6561
|
},
|
|
6562
|
-
|
|
6562
|
+
7489(__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);
|
|
@@ -26549,8 +26549,6 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
26549
26549
|
* List of known action names for convenience
|
|
26550
26550
|
*/ const KNOWN_ACTIONS = Object.keys(DEFAULT_ACTION_VERSIONS);
|
|
26551
26551
|
|
|
26552
|
-
// EXTERNAL MODULE: external "node:fs"
|
|
26553
|
-
var external_node_fs_ = __webpack_require__(3024);
|
|
26554
26552
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/errors.js
|
|
26555
26553
|
const OPERATIONAL_CODES = new Set([
|
|
26556
26554
|
"helper-failed",
|
|
@@ -26565,42 +26563,20 @@ function categorizeFsSafeError(code) {
|
|
|
26565
26563
|
class errors_FsSafeError extends Error {
|
|
26566
26564
|
code;
|
|
26567
26565
|
category;
|
|
26566
|
+
details;
|
|
26568
26567
|
constructor(code, message, options = {}) {
|
|
26569
26568
|
super(message, options);
|
|
26570
26569
|
this.name = "FsSafeError";
|
|
26571
26570
|
this.code = code;
|
|
26572
26571
|
this.category = categorizeFsSafeError(code);
|
|
26572
|
+
this.details = options.details;
|
|
26573
26573
|
}
|
|
26574
26574
|
}
|
|
26575
26575
|
|
|
26576
26576
|
// EXTERNAL MODULE: external "node:crypto"
|
|
26577
26577
|
var external_node_crypto_ = __webpack_require__(7598);
|
|
26578
|
-
// EXTERNAL MODULE: external "node:
|
|
26579
|
-
var
|
|
26580
|
-
// EXTERNAL MODULE: external "node:stream"
|
|
26581
|
-
var external_node_stream_ = __webpack_require__(7075);
|
|
26582
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read-stream.js
|
|
26583
|
-
|
|
26584
|
-
|
|
26585
|
-
function createMaxBytesTransform(maxBytes) {
|
|
26586
|
-
let bytes = 0;
|
|
26587
|
-
return new external_node_stream_.Transform({
|
|
26588
|
-
transform(chunk, _encoding, callback) {
|
|
26589
|
-
const buffer = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
|
|
26590
|
-
bytes += buffer.byteLength;
|
|
26591
|
-
if (bytes > maxBytes) {
|
|
26592
|
-
callback(new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`));
|
|
26593
|
-
return;
|
|
26594
|
-
}
|
|
26595
|
-
callback(null, buffer);
|
|
26596
|
-
},
|
|
26597
|
-
});
|
|
26598
|
-
}
|
|
26599
|
-
function createBoundedReadStream(opened, maxBytes) {
|
|
26600
|
-
const stream = opened.handle.createReadStream();
|
|
26601
|
-
return maxBytes === undefined ? stream : stream.pipe(createMaxBytesTransform(maxBytes));
|
|
26602
|
-
}
|
|
26603
|
-
|
|
26578
|
+
// EXTERNAL MODULE: external "node:fs"
|
|
26579
|
+
var external_node_fs_ = __webpack_require__(3024);
|
|
26604
26580
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/file-identity.js
|
|
26605
26581
|
|
|
26606
26582
|
function isZero(value) {
|
|
@@ -26625,86 +26601,11 @@ function file_identity_sameFileIdentity(left, right, platform = process.platform
|
|
|
26625
26601
|
return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
|
|
26626
26602
|
}
|
|
26627
26603
|
|
|
26628
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/string-coerce.js
|
|
26629
|
-
function readStringValue(value) {
|
|
26630
|
-
return typeof value === "string" ? value : undefined;
|
|
26631
|
-
}
|
|
26632
|
-
function normalizeNullableString(value) {
|
|
26633
|
-
if (typeof value !== "string") {
|
|
26634
|
-
return null;
|
|
26635
|
-
}
|
|
26636
|
-
const trimmed = value.trim();
|
|
26637
|
-
return trimmed ? trimmed : null;
|
|
26638
|
-
}
|
|
26639
|
-
function normalizeOptionalString(value) {
|
|
26640
|
-
return normalizeNullableString(value) ?? undefined;
|
|
26641
|
-
}
|
|
26642
|
-
function normalizeStringifiedOptionalString(value) {
|
|
26643
|
-
if (typeof value === "string") {
|
|
26644
|
-
return normalizeOptionalString(value);
|
|
26645
|
-
}
|
|
26646
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
26647
|
-
return normalizeOptionalString(String(value));
|
|
26648
|
-
}
|
|
26649
|
-
return undefined;
|
|
26650
|
-
}
|
|
26651
|
-
function normalizeOptionalLowercaseString(value) {
|
|
26652
|
-
return normalizeOptionalString(value)?.toLowerCase();
|
|
26653
|
-
}
|
|
26654
|
-
function normalizeLowercaseStringOrEmpty(value) {
|
|
26655
|
-
return normalizeOptionalLowercaseString(value) ?? "";
|
|
26656
|
-
}
|
|
26657
|
-
function normalizeFastMode(raw) {
|
|
26658
|
-
if (typeof raw === "boolean") {
|
|
26659
|
-
return raw;
|
|
26660
|
-
}
|
|
26661
|
-
if (!raw) {
|
|
26662
|
-
return undefined;
|
|
26663
|
-
}
|
|
26664
|
-
const key = normalizeLowercaseStringOrEmpty(raw);
|
|
26665
|
-
if (["off", "false", "no", "0", "disable", "disabled", "normal"].includes(key)) {
|
|
26666
|
-
return false;
|
|
26667
|
-
}
|
|
26668
|
-
if (["on", "true", "yes", "1", "enable", "enabled", "fast"].includes(key)) {
|
|
26669
|
-
return true;
|
|
26670
|
-
}
|
|
26671
|
-
return undefined;
|
|
26672
|
-
}
|
|
26673
|
-
function lowercasePreservingWhitespace(value) {
|
|
26674
|
-
return value.toLowerCase();
|
|
26675
|
-
}
|
|
26676
|
-
function localeLowercasePreservingWhitespace(value) {
|
|
26677
|
-
return value.toLocaleLowerCase();
|
|
26678
|
-
}
|
|
26679
|
-
function resolvePrimaryStringValue(value) {
|
|
26680
|
-
if (typeof value === "string") {
|
|
26681
|
-
return normalizeOptionalString(value);
|
|
26682
|
-
}
|
|
26683
|
-
if (!value || typeof value !== "object") {
|
|
26684
|
-
return undefined;
|
|
26685
|
-
}
|
|
26686
|
-
return normalizeOptionalString(value.primary);
|
|
26687
|
-
}
|
|
26688
|
-
function normalizeOptionalThreadValue(value) {
|
|
26689
|
-
if (typeof value === "number") {
|
|
26690
|
-
return Number.isFinite(value) ? Math.trunc(value) : undefined;
|
|
26691
|
-
}
|
|
26692
|
-
return normalizeOptionalString(value);
|
|
26693
|
-
}
|
|
26694
|
-
function normalizeOptionalStringifiedId(value) {
|
|
26695
|
-
const normalized = normalizeOptionalThreadValue(value);
|
|
26696
|
-
return normalized == null ? undefined : String(normalized);
|
|
26697
|
-
}
|
|
26698
|
-
function hasNonEmptyString(value) {
|
|
26699
|
-
return normalizeOptionalString(value) !== undefined;
|
|
26700
|
-
}
|
|
26701
|
-
|
|
26702
26604
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path.js
|
|
26703
26605
|
|
|
26704
26606
|
|
|
26705
26607
|
|
|
26706
26608
|
|
|
26707
|
-
|
|
26708
26609
|
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
26709
26610
|
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
|
|
26710
26611
|
const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
|
|
@@ -26716,7 +26617,9 @@ function normalizeWindowsPathForComparison(input) {
|
|
|
26716
26617
|
normalized = `\\\\${normalized.slice(4)}`;
|
|
26717
26618
|
}
|
|
26718
26619
|
}
|
|
26719
|
-
|
|
26620
|
+
// Lowercase only: this value feeds Windows containment math, so surrounding
|
|
26621
|
+
// whitespace is part of the path and must not be trimmed away.
|
|
26622
|
+
return normalized.replaceAll("/", "\\").toLowerCase();
|
|
26720
26623
|
}
|
|
26721
26624
|
function isNodeError(value) {
|
|
26722
26625
|
return Boolean(value && typeof value === "object" && "code" in value);
|
|
@@ -27215,6 +27118,27 @@ function isSameOrChildPath(candidate, parent) {
|
|
|
27215
27118
|
const parentPrefix = parent.endsWith(external_node_path_.sep) ? parent : `${parent}${external_node_path_.sep}`;
|
|
27216
27119
|
return candidate === parent || candidate.startsWith(parentPrefix);
|
|
27217
27120
|
}
|
|
27121
|
+
async function realpathOrThrowNotFile(target) {
|
|
27122
|
+
try {
|
|
27123
|
+
return external_node_path_.resolve(await promises_.realpath(target));
|
|
27124
|
+
}
|
|
27125
|
+
catch (error) {
|
|
27126
|
+
if (path_isNotFoundPathError(error)) {
|
|
27127
|
+
// A dangling symlink (or a component removed between lstat and
|
|
27128
|
+
// realpath) is not a usable directory component.
|
|
27129
|
+
throw new errors_FsSafeError("not-file", "directory component must be a directory", {
|
|
27130
|
+
cause: error instanceof Error ? error : undefined,
|
|
27131
|
+
});
|
|
27132
|
+
}
|
|
27133
|
+
throw error;
|
|
27134
|
+
}
|
|
27135
|
+
}
|
|
27136
|
+
/**
|
|
27137
|
+
* Creates each missing path component from `rootReal` down to `targetPath`,
|
|
27138
|
+
* guarding every step. Returns the real (symlink-resolved) path of the final
|
|
27139
|
+
* component so callers can guard/use that path directly instead of
|
|
27140
|
+
* re-deriving it from the original, possibly-symlinked, lexical path.
|
|
27141
|
+
*/
|
|
27218
27142
|
async function mkdirPathComponentsWithGuards(params) {
|
|
27219
27143
|
const root = external_node_path_.resolve(params.rootReal);
|
|
27220
27144
|
const rootCanonical = external_node_path_.resolve(await promises_.realpath(root));
|
|
@@ -27238,18 +27162,39 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
27238
27162
|
}
|
|
27239
27163
|
}
|
|
27240
27164
|
const stat = await promises_.lstat(next);
|
|
27241
|
-
if (stat.isSymbolicLink()
|
|
27165
|
+
if (!stat.isSymbolicLink() && !stat.isDirectory()) {
|
|
27242
27166
|
throw new errors_FsSafeError("not-file", "directory component must be a directory");
|
|
27243
27167
|
}
|
|
27244
27168
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
27245
27169
|
// segment at a time and realpath-check each segment before descending.
|
|
27246
|
-
|
|
27170
|
+
const nextReal = await realpathOrThrowNotFile(next);
|
|
27171
|
+
if (!isSameOrChildPath(nextReal, rootCanonical)) {
|
|
27247
27172
|
throw new errors_FsSafeError("outside-workspace", "directory escaped workspace root");
|
|
27248
27173
|
}
|
|
27174
|
+
if (stat.isSymbolicLink()) {
|
|
27175
|
+
// An existing path component may legitimately be a symlink to a real
|
|
27176
|
+
// directory inside the root (e.g. a skill-bank layout). We already
|
|
27177
|
+
// verified above that it resolves inside the root, so treat the
|
|
27178
|
+
// resolved real path as the directory for the rest of this walk
|
|
27179
|
+
// instead of rejecting it outright. Guard checks from here on operate
|
|
27180
|
+
// on the real (non-symlink) path, preserving TOCTOU protection for
|
|
27181
|
+
// every subsequent segment. Callers that need the final directory
|
|
27182
|
+
// (e.g. to guard it themselves after this function returns) must use
|
|
27183
|
+
// the returned resolved path, not their own lexical parent path.
|
|
27184
|
+
const targetStat = await promises_.stat(nextReal);
|
|
27185
|
+
if (!targetStat.isDirectory()) {
|
|
27186
|
+
throw new errors_FsSafeError("not-file", "directory component must be a directory");
|
|
27187
|
+
}
|
|
27188
|
+
await directory_guard_createAsyncDirectoryGuard(nextReal);
|
|
27189
|
+
await assertAsyncDirectoryGuard(parentGuard);
|
|
27190
|
+
current = nextReal;
|
|
27191
|
+
continue;
|
|
27192
|
+
}
|
|
27249
27193
|
await directory_guard_createAsyncDirectoryGuard(next);
|
|
27250
27194
|
await assertAsyncDirectoryGuard(parentGuard);
|
|
27251
27195
|
current = next;
|
|
27252
27196
|
}
|
|
27197
|
+
return current;
|
|
27253
27198
|
}
|
|
27254
27199
|
|
|
27255
27200
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/guarded-mutation.js
|
|
@@ -27515,8 +27460,20 @@ async function resolveOpenedFileRealPathFromParent(handleStat, ioPath) {
|
|
|
27515
27460
|
return null;
|
|
27516
27461
|
}
|
|
27517
27462
|
|
|
27518
|
-
|
|
27463
|
+
// EXTERNAL MODULE: external "node:module"
|
|
27464
|
+
var external_node_module_ = __webpack_require__(8995);
|
|
27465
|
+
// EXTERNAL MODULE: external "node:url"
|
|
27466
|
+
var external_node_url_ = __webpack_require__(3136);
|
|
27467
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/native-config.js
|
|
27519
27468
|
let overrideConfig = {};
|
|
27469
|
+
let legacyWarningEmitted = false;
|
|
27470
|
+
const legacyModeEnvKeys = ["FS_SAFE_PYTHON_MODE", "OPENCLAW_FS_SAFE_PYTHON_MODE"];
|
|
27471
|
+
const legacyPathEnvKeys = [
|
|
27472
|
+
"FS_SAFE_PYTHON",
|
|
27473
|
+
"OPENCLAW_FS_SAFE_PYTHON",
|
|
27474
|
+
"OPENCLAW_PINNED_PYTHON",
|
|
27475
|
+
"OPENCLAW_PINNED_WRITE_PYTHON",
|
|
27476
|
+
];
|
|
27520
27477
|
function parseMode(value) {
|
|
27521
27478
|
if (!value) {
|
|
27522
27479
|
return undefined;
|
|
@@ -27533,682 +27490,495 @@ function parseMode(value) {
|
|
|
27533
27490
|
}
|
|
27534
27491
|
return undefined;
|
|
27535
27492
|
}
|
|
27536
|
-
function
|
|
27493
|
+
function configureFsSafeNative(config) {
|
|
27537
27494
|
overrideConfig = { ...overrideConfig, ...config };
|
|
27538
27495
|
}
|
|
27539
|
-
function
|
|
27496
|
+
function warnLegacyPythonConfiguration(source, mappedMode) {
|
|
27497
|
+
if (legacyWarningEmitted) {
|
|
27498
|
+
return;
|
|
27499
|
+
}
|
|
27500
|
+
legacyWarningEmitted = true;
|
|
27501
|
+
process.emitWarning(`${source} is a deprecated 0.4 compatibility bridge; mapped to native mode ` +
|
|
27502
|
+
`"${mappedMode}". Use configureFsSafeNative({ mode: "${mappedMode}" }) or ` +
|
|
27503
|
+
`FS_SAFE_NATIVE_MODE=${mappedMode}. Python interpreter paths are no longer used.`, { code: "FS_SAFE_PYTHON_DEPRECATED", type: "DeprecationWarning" });
|
|
27504
|
+
}
|
|
27505
|
+
function readLegacyPythonMode() {
|
|
27506
|
+
const configuredKeys = [...legacyModeEnvKeys, ...legacyPathEnvKeys].filter((key) => process.env[key] !== undefined);
|
|
27507
|
+
if (configuredKeys.length === 0) {
|
|
27508
|
+
return undefined;
|
|
27509
|
+
}
|
|
27510
|
+
const rawMode = legacyModeEnvKeys.map((key) => process.env[key]).find((value) => value !== undefined);
|
|
27511
|
+
const mappedMode = parseMode(rawMode) ?? "auto";
|
|
27512
|
+
warnLegacyPythonConfiguration(`Legacy ${configuredKeys.join(", ")}`, mappedMode);
|
|
27513
|
+
return mappedMode;
|
|
27514
|
+
}
|
|
27515
|
+
/**
|
|
27516
|
+
* @deprecated Compatibility bridge for 0.4 upgrades. Use configureFsSafeNative.
|
|
27517
|
+
*/
|
|
27518
|
+
function configureFsSafePython(config) {
|
|
27519
|
+
const mappedMode = config.mode ?? "auto";
|
|
27520
|
+
warnLegacyPythonConfiguration("configureFsSafePython()", mappedMode);
|
|
27521
|
+
if (config.mode !== undefined) {
|
|
27522
|
+
configureFsSafeNative({ mode: config.mode });
|
|
27523
|
+
}
|
|
27524
|
+
}
|
|
27525
|
+
function getFsSafeNativeConfig() {
|
|
27526
|
+
const legacyMode = readLegacyPythonMode();
|
|
27540
27527
|
return {
|
|
27541
27528
|
mode: overrideConfig.mode ??
|
|
27542
|
-
parseMode(process.env.
|
|
27543
|
-
parseMode(process.env.
|
|
27529
|
+
parseMode(process.env.FS_SAFE_NATIVE_MODE) ??
|
|
27530
|
+
parseMode(process.env.OPENCLAW_FS_SAFE_NATIVE_MODE) ??
|
|
27531
|
+
legacyMode ??
|
|
27544
27532
|
"auto",
|
|
27545
|
-
pythonPath: overrideConfig.pythonPath ??
|
|
27546
|
-
process.env.FS_SAFE_PYTHON ??
|
|
27547
|
-
process.env.OPENCLAW_FS_SAFE_PYTHON ??
|
|
27548
|
-
process.env.OPENCLAW_PINNED_PYTHON ??
|
|
27549
|
-
process.env.OPENCLAW_PINNED_WRITE_PYTHON,
|
|
27550
27533
|
};
|
|
27551
27534
|
}
|
|
27552
|
-
function
|
|
27553
|
-
|
|
27554
|
-
|
|
27555
|
-
(code === "helper-unavailable" || code === "unsupported-platform"));
|
|
27535
|
+
function __resetFsSafeNativeConfigForTest() {
|
|
27536
|
+
overrideConfig = {};
|
|
27537
|
+
legacyWarningEmitted = false;
|
|
27556
27538
|
}
|
|
27557
27539
|
|
|
27558
|
-
|
|
27559
|
-
|
|
27560
|
-
|
|
27561
|
-
|
|
27562
|
-
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
const
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
27573
|
-
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
27574
|
-
if hasattr(os, "O_NOFOLLOW"):
|
|
27575
|
-
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
27576
|
-
def split_relative(value):
|
|
27577
|
-
if value in ("", "."):
|
|
27578
|
-
return []
|
|
27579
|
-
if "\x00" in value or value.startswith("/") or value.startswith("//"):
|
|
27580
|
-
raise OSError(errno.EPERM, "invalid relative path")
|
|
27581
|
-
if value.startswith("..\\"):
|
|
27582
|
-
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
27583
|
-
parts = [part for part in value.split("/") if part and part != "."]
|
|
27584
|
-
for part in parts:
|
|
27585
|
-
if part == "..":
|
|
27586
|
-
raise OSError(errno.EPERM, "path traversal is not allowed")
|
|
27587
|
-
return parts
|
|
27588
|
-
def open_dir(path_value, dir_fd=None):
|
|
27589
|
-
return os.open(path_value, DIR_FLAGS, dir_fd=dir_fd)
|
|
27590
|
-
def walk_dir(root_fd, segments, mkdir_enabled=False):
|
|
27591
|
-
current_fd = os.dup(root_fd)
|
|
27592
|
-
try:
|
|
27593
|
-
for segment in segments:
|
|
27594
|
-
try:
|
|
27595
|
-
next_fd = open_dir(segment, dir_fd=current_fd)
|
|
27596
|
-
except FileNotFoundError:
|
|
27597
|
-
if not mkdir_enabled:
|
|
27598
|
-
raise
|
|
27599
|
-
os.mkdir(segment, 0o777, dir_fd=current_fd)
|
|
27600
|
-
next_fd = open_dir(segment, dir_fd=current_fd)
|
|
27601
|
-
os.close(current_fd)
|
|
27602
|
-
current_fd = next_fd
|
|
27603
|
-
return current_fd
|
|
27604
|
-
except Exception:
|
|
27605
|
-
os.close(current_fd)
|
|
27606
|
-
raise
|
|
27607
|
-
def parent_and_basename(root_fd, relative):
|
|
27608
|
-
segments = split_relative(relative)
|
|
27609
|
-
if not segments:
|
|
27610
|
-
raise OSError(errno.EPERM, "operation requires a non-root path")
|
|
27611
|
-
parent_fd = walk_dir(root_fd, segments[:-1])
|
|
27612
|
-
return parent_fd, segments[-1]
|
|
27613
|
-
def encode_stat(st):
|
|
27614
|
-
mode = st.st_mode
|
|
27615
|
-
return {
|
|
27616
|
-
"dev": st.st_dev,
|
|
27617
|
-
"gid": st.st_gid,
|
|
27618
|
-
"ino": st.st_ino,
|
|
27619
|
-
"isDirectory": stat.S_ISDIR(mode),
|
|
27620
|
-
"isFile": stat.S_ISREG(mode),
|
|
27621
|
-
"isSymbolicLink": stat.S_ISLNK(mode),
|
|
27622
|
-
"mode": mode,
|
|
27623
|
-
"mtimeMs": st.st_mtime * 1000,
|
|
27624
|
-
"nlink": st.st_nlink,
|
|
27625
|
-
"size": st.st_size,
|
|
27626
|
-
"uid": st.st_uid,
|
|
27627
|
-
}
|
|
27628
|
-
def reject_unsafe_endpoint(st):
|
|
27629
|
-
mode = st.st_mode
|
|
27630
|
-
if stat.S_ISLNK(mode):
|
|
27631
|
-
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
27632
|
-
if stat.S_ISREG(mode) and st.st_nlink > 1:
|
|
27633
|
-
raise OSError(errno.EPERM, "hardlinked file endpoint is not allowed")
|
|
27634
|
-
def copy_bytes(source_fd, dest_fd):
|
|
27635
|
-
while True:
|
|
27636
|
-
chunk = os.read(source_fd, 65536)
|
|
27637
|
-
if not chunk:
|
|
27638
|
-
break
|
|
27639
|
-
view = memoryview(chunk)
|
|
27640
|
-
while view:
|
|
27641
|
-
written = os.write(dest_fd, view)
|
|
27642
|
-
if written <= 0:
|
|
27643
|
-
raise OSError(errno.EIO, "short write")
|
|
27644
|
-
view = view[written:]
|
|
27645
|
-
def write_all(fd, data):
|
|
27646
|
-
view = memoryview(data)
|
|
27647
|
-
while view:
|
|
27648
|
-
written = os.write(fd, view)
|
|
27649
|
-
if written <= 0:
|
|
27650
|
-
raise OSError(errno.EIO, "short write")
|
|
27651
|
-
view = view[written:]
|
|
27652
|
-
def fsync_best_effort(fd):
|
|
27653
|
-
try: os.fsync(fd)
|
|
27654
|
-
except OSError as error:
|
|
27655
|
-
if error.errno != errno.EPERM: raise
|
|
27656
|
-
def link_unsupported(exc):
|
|
27657
|
-
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
27658
|
-
return getattr(exc, "errno", None) in unsupported
|
|
27659
|
-
def link_no_replace(name, new_name, source_fd, target_fd):
|
|
27660
|
-
linked = False
|
|
27661
|
-
try:
|
|
27662
|
-
os.link(name, new_name, src_dir_fd=source_fd, dst_dir_fd=target_fd, follow_symlinks=False)
|
|
27663
|
-
linked = True
|
|
27664
|
-
os.unlink(name, dir_fd=source_fd)
|
|
27665
|
-
except Exception:
|
|
27666
|
-
if linked:
|
|
27667
|
-
try: os.unlink(new_name, dir_fd=target_fd)
|
|
27668
|
-
except FileNotFoundError: pass
|
|
27669
|
-
raise
|
|
27670
|
-
os.fsync(source_fd)
|
|
27671
|
-
if source_fd != target_fd:
|
|
27672
|
-
os.fsync(target_fd)
|
|
27673
|
-
def copy_file_no_replace(source_parent_fd, source_name, target_parent_fd, basename, mode, expected=None, unlink_source=False):
|
|
27674
|
-
source_fd = os.open(source_name, READ_FLAGS, dir_fd=source_parent_fd)
|
|
27675
|
-
dest_fd = None; success = False; dest_stat = None
|
|
27676
|
-
try:
|
|
27677
|
-
if expected is not None:
|
|
27678
|
-
source_stat = os.fstat(source_fd)
|
|
27679
|
-
if source_stat.st_dev != expected.st_dev or source_stat.st_ino != expected.st_ino:
|
|
27680
|
-
raise RuntimeError("fs-safe-source-mismatch")
|
|
27681
|
-
dest_fd = os.open(basename, WRITE_FLAGS, mode, dir_fd=target_parent_fd)
|
|
27682
|
-
copy_bytes(source_fd, dest_fd)
|
|
27683
|
-
os.fsync(dest_fd)
|
|
27684
|
-
dest_stat = os.fstat(dest_fd)
|
|
27685
|
-
success = True
|
|
27686
|
-
finally:
|
|
27687
|
-
os.close(source_fd)
|
|
27688
|
-
if dest_fd is not None:
|
|
27689
|
-
os.close(dest_fd)
|
|
27690
|
-
if dest_fd is not None and not success:
|
|
27691
|
-
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
27692
|
-
except FileNotFoundError: pass
|
|
27693
|
-
if unlink_source:
|
|
27694
|
-
try:
|
|
27695
|
-
os.unlink(source_name, dir_fd=source_parent_fd)
|
|
27696
|
-
except Exception:
|
|
27697
|
-
try: os.unlink(basename, dir_fd=target_parent_fd)
|
|
27698
|
-
except FileNotFoundError: pass
|
|
27699
|
-
raise
|
|
27700
|
-
return dest_stat
|
|
27701
|
-
def same_identity(left, right):
|
|
27702
|
-
return left.st_dev == right.st_dev and left.st_ino == right.st_ino
|
|
27703
|
-
def verify_temp_name(parent_fd, temp_name, expected_stat):
|
|
27704
|
-
current_stat = os.lstat(temp_name, dir_fd=parent_fd)
|
|
27705
|
-
if stat.S_ISLNK(current_stat.st_mode) or not same_identity(current_stat, expected_stat):
|
|
27706
|
-
raise RuntimeError("fs-safe-temp-mismatch")
|
|
27707
|
-
def verify_committed_temp(parent_fd, basename, expected_stat):
|
|
27708
|
-
final_stat = os.lstat(basename, dir_fd=parent_fd)
|
|
27709
|
-
if not stat.S_ISLNK(final_stat.st_mode) and same_identity(final_stat, expected_stat):
|
|
27710
|
-
return final_stat
|
|
27711
|
-
try: os.unlink(basename, dir_fd=parent_fd)
|
|
27712
|
-
except FileNotFoundError: pass
|
|
27713
|
-
raise RuntimeError("fs-safe-temp-mismatch")
|
|
27714
|
-
def commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, expected_stat):
|
|
27715
|
-
verify_temp_name(parent_fd, temp_name, expected_stat)
|
|
27716
|
-
if overwrite:
|
|
27717
|
-
os.replace(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd)
|
|
27718
|
-
return verify_committed_temp(parent_fd, basename, expected_stat)
|
|
27719
|
-
else:
|
|
27720
|
-
try:
|
|
27721
|
-
os.link(temp_name, basename, src_dir_fd=parent_fd, dst_dir_fd=parent_fd, follow_symlinks=False)
|
|
27722
|
-
final_stat = verify_committed_temp(parent_fd, basename, expected_stat)
|
|
27723
|
-
os.unlink(temp_name, dir_fd=parent_fd)
|
|
27724
|
-
return final_stat
|
|
27725
|
-
except OSError as exc:
|
|
27726
|
-
if not link_unsupported(exc):
|
|
27727
|
-
raise
|
|
27728
|
-
return copy_file_no_replace(parent_fd, temp_name, parent_fd, basename, mode, expected_stat, True)
|
|
27729
|
-
def assert_expected_root(root_fd, payload):
|
|
27730
|
-
if "rootDev" in payload or "rootIno" in payload:
|
|
27731
|
-
root_stat = os.fstat(root_fd)
|
|
27732
|
-
if root_stat.st_dev != int(payload["rootDev"]) or root_stat.st_ino != int(payload["rootIno"]):
|
|
27733
|
-
raise RuntimeError("fs-safe-root-mismatch")
|
|
27734
|
-
def stat_path(root_fd, payload):
|
|
27735
|
-
relative = payload.get("relativePath", "")
|
|
27736
|
-
segments = split_relative(relative)
|
|
27737
|
-
if not segments:
|
|
27738
|
-
return encode_stat(os.fstat(root_fd))
|
|
27739
|
-
parent_fd, basename = parent_and_basename(root_fd, relative)
|
|
27740
|
-
try:
|
|
27741
|
-
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27742
|
-
if payload.get("rejectSymlink", True) and stat.S_ISLNK(st.st_mode):
|
|
27743
|
-
raise OSError(errno.ELOOP, "symlink endpoint is not allowed")
|
|
27744
|
-
return encode_stat(st)
|
|
27745
|
-
finally:
|
|
27746
|
-
os.close(parent_fd)
|
|
27747
|
-
def readdir_path(root_fd, payload):
|
|
27748
|
-
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")))
|
|
27749
|
-
try:
|
|
27750
|
-
names = sorted(os.listdir(dir_fd))
|
|
27751
|
-
if not payload.get("withFileTypes", False):
|
|
27752
|
-
return names
|
|
27753
|
-
entries = []
|
|
27754
|
-
for name in names:
|
|
27755
|
-
st = os.lstat(name, dir_fd=dir_fd)
|
|
27756
|
-
entry = encode_stat(st)
|
|
27757
|
-
entry["name"] = name
|
|
27758
|
-
entries.append(entry)
|
|
27759
|
-
return entries
|
|
27760
|
-
finally:
|
|
27761
|
-
os.close(dir_fd)
|
|
27762
|
-
def mkdirp_path(root_fd, payload):
|
|
27763
|
-
dir_fd = walk_dir(root_fd, split_relative(payload.get("relativePath", "")), mkdir_enabled=True)
|
|
27764
|
-
os.close(dir_fd); return None
|
|
27765
|
-
def remove_tree(parent_fd, basename):
|
|
27766
|
-
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27767
|
-
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
27768
|
-
dir_fd = open_dir(basename, dir_fd=parent_fd)
|
|
27769
|
-
try:
|
|
27770
|
-
for child in os.listdir(dir_fd):
|
|
27771
|
-
remove_tree(dir_fd, child)
|
|
27772
|
-
finally:
|
|
27773
|
-
os.close(dir_fd)
|
|
27774
|
-
os.rmdir(basename, dir_fd=parent_fd)
|
|
27775
|
-
else:
|
|
27776
|
-
os.unlink(basename, dir_fd=parent_fd)
|
|
27777
|
-
def remove_path(root_fd, payload):
|
|
27778
|
-
parent_fd, basename = parent_and_basename(root_fd, payload.get("relativePath", ""))
|
|
27779
|
-
try:
|
|
27780
|
-
try:
|
|
27781
|
-
st = os.lstat(basename, dir_fd=parent_fd)
|
|
27782
|
-
except FileNotFoundError:
|
|
27783
|
-
if payload.get("force", True):
|
|
27784
|
-
return None
|
|
27785
|
-
raise
|
|
27786
|
-
if stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode):
|
|
27787
|
-
if payload.get("recursive", False):
|
|
27788
|
-
remove_tree(parent_fd, basename)
|
|
27789
|
-
else:
|
|
27790
|
-
os.rmdir(basename, dir_fd=parent_fd)
|
|
27791
|
-
else:
|
|
27792
|
-
os.unlink(basename, dir_fd=parent_fd)
|
|
27793
|
-
return None
|
|
27794
|
-
finally:
|
|
27795
|
-
os.close(parent_fd)
|
|
27796
|
-
|
|
27797
|
-
def rename_path(root_fd, payload):
|
|
27798
|
-
from_parent_fd, from_base = parent_and_basename(root_fd, payload["from"])
|
|
27799
|
-
to_parent_fd, to_base = parent_and_basename(root_fd, payload["to"])
|
|
27800
|
-
try:
|
|
27801
|
-
from_stat = os.lstat(from_base, dir_fd=from_parent_fd)
|
|
27802
|
-
reject_unsafe_endpoint(from_stat)
|
|
27803
|
-
overwrite = payload.get("overwrite", True)
|
|
27804
|
-
if not overwrite and stat.S_ISREG(from_stat.st_mode):
|
|
27805
|
-
try:
|
|
27806
|
-
link_no_replace(from_base, to_base, from_parent_fd, to_parent_fd)
|
|
27807
|
-
except OSError as exc:
|
|
27808
|
-
if not link_unsupported(exc):
|
|
27809
|
-
raise
|
|
27810
|
-
copy_file_no_replace(from_parent_fd, from_base, to_parent_fd, to_base, stat.S_IMODE(from_stat.st_mode), from_stat, True)
|
|
27811
|
-
return None
|
|
27812
|
-
if not overwrite and stat.S_ISDIR(from_stat.st_mode):
|
|
27813
|
-
raise RuntimeError("fs-safe-directory-noreplace-unsupported")
|
|
27814
|
-
if not overwrite:
|
|
27815
|
-
try:
|
|
27816
|
-
os.lstat(to_base, dir_fd=to_parent_fd)
|
|
27817
|
-
raise FileExistsError(errno.EEXIST, "destination exists", to_base)
|
|
27818
|
-
except FileNotFoundError:
|
|
27819
|
-
pass
|
|
27820
|
-
os.rename(from_base, to_base, src_dir_fd=from_parent_fd, dst_dir_fd=to_parent_fd)
|
|
27821
|
-
os.fsync(from_parent_fd)
|
|
27822
|
-
if from_parent_fd != to_parent_fd:
|
|
27823
|
-
os.fsync(to_parent_fd)
|
|
27824
|
-
return None
|
|
27825
|
-
finally:
|
|
27826
|
-
os.close(from_parent_fd)
|
|
27827
|
-
os.close(to_parent_fd)
|
|
27828
|
-
|
|
27829
|
-
def create_temp_file(parent_fd, basename, mode):
|
|
27830
|
-
prefix = "." + basename + "."
|
|
27831
|
-
for _ in range(128):
|
|
27832
|
-
candidate = prefix + secrets.token_hex(6) + ".tmp"
|
|
27833
|
-
try:
|
|
27834
|
-
fd = os.open(candidate, WRITE_FLAGS, mode, dir_fd=parent_fd)
|
|
27835
|
-
return candidate, fd
|
|
27836
|
-
except FileExistsError:
|
|
27837
|
-
continue
|
|
27838
|
-
raise RuntimeError("failed to allocate pinned temp file")
|
|
27839
|
-
|
|
27840
|
-
def write_path(root_fd, payload):
|
|
27841
|
-
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
27842
|
-
temp_fd = None
|
|
27843
|
-
temp_name = None
|
|
27844
|
-
basename = payload["basename"]
|
|
27845
|
-
mode = int(payload.get("mode", 0o600))
|
|
27846
|
-
overwrite = bool(payload.get("overwrite", True))
|
|
27847
|
-
max_bytes = int(payload.get("maxBytes", -1))
|
|
27848
|
-
data = base64.b64decode(payload.get("base64", ""))
|
|
27849
|
-
try:
|
|
27850
|
-
if max_bytes >= 0 and len(data) > max_bytes:
|
|
27851
|
-
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, len(data)))
|
|
27852
|
-
if not overwrite:
|
|
27853
|
-
try:
|
|
27854
|
-
os.lstat(basename, dir_fd=parent_fd)
|
|
27855
|
-
raise FileExistsError(errno.EEXIST, "destination exists", basename)
|
|
27856
|
-
except FileNotFoundError:
|
|
27857
|
-
pass
|
|
27858
|
-
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
27859
|
-
os.fchmod(temp_fd, mode)
|
|
27860
|
-
write_all(temp_fd, data)
|
|
27861
|
-
fsync_best_effort(temp_fd)
|
|
27862
|
-
temp_stat = os.fstat(temp_fd)
|
|
27863
|
-
os.close(temp_fd)
|
|
27864
|
-
temp_fd = None
|
|
27865
|
-
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
27866
|
-
temp_name = None
|
|
27867
|
-
fsync_best_effort(parent_fd)
|
|
27868
|
-
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
27869
|
-
finally:
|
|
27870
|
-
if temp_fd is not None:
|
|
27871
|
-
os.close(temp_fd)
|
|
27872
|
-
if temp_name is not None:
|
|
27873
|
-
try:
|
|
27874
|
-
os.unlink(temp_name, dir_fd=parent_fd)
|
|
27875
|
-
except FileNotFoundError:
|
|
27876
|
-
pass
|
|
27877
|
-
os.close(parent_fd)
|
|
27878
|
-
|
|
27879
|
-
def copy_path(root_fd, payload):
|
|
27880
|
-
source_fd = os.open(payload["sourcePath"], READ_FLAGS)
|
|
27881
|
-
parent_fd = None
|
|
27882
|
-
temp_fd = None
|
|
27883
|
-
temp_name = None
|
|
27884
|
-
try:
|
|
27885
|
-
source_stat = os.fstat(source_fd)
|
|
27886
|
-
if not stat.S_ISREG(source_stat.st_mode):
|
|
27887
|
-
raise RuntimeError("fs-safe-not-file")
|
|
27888
|
-
if source_stat.st_dev != int(payload["sourceDev"]) or source_stat.st_ino != int(payload["sourceIno"]):
|
|
27889
|
-
raise RuntimeError("fs-safe-source-mismatch")
|
|
27890
|
-
basename = payload["basename"]
|
|
27891
|
-
mode = int(payload.get("mode", 0o600))
|
|
27892
|
-
overwrite = bool(payload.get("overwrite", True))
|
|
27893
|
-
max_bytes = int(payload.get("maxBytes", -1))
|
|
27894
|
-
if max_bytes >= 0 and source_stat.st_size > max_bytes:
|
|
27895
|
-
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, source_stat.st_size))
|
|
27896
|
-
parent_fd = walk_dir(root_fd, split_relative(payload.get("relativeParentPath", "")), bool(payload.get("mkdir", True)))
|
|
27897
|
-
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
27898
|
-
os.fchmod(temp_fd, mode)
|
|
27899
|
-
written_bytes = 0
|
|
27900
|
-
while True:
|
|
27901
|
-
chunk = os.read(source_fd, 65536)
|
|
27902
|
-
if not chunk:
|
|
27903
|
-
break
|
|
27904
|
-
written_bytes += len(chunk)
|
|
27905
|
-
if max_bytes >= 0 and written_bytes > max_bytes:
|
|
27906
|
-
raise RuntimeError("fs-safe-too-large:%d:%d" % (max_bytes, written_bytes))
|
|
27907
|
-
view = memoryview(chunk)
|
|
27908
|
-
while view:
|
|
27909
|
-
written = os.write(temp_fd, view)
|
|
27910
|
-
if written <= 0:
|
|
27911
|
-
raise OSError(errno.EIO, "short write")
|
|
27912
|
-
view = view[written:]
|
|
27913
|
-
os.fsync(temp_fd)
|
|
27914
|
-
temp_stat = os.fstat(temp_fd)
|
|
27915
|
-
os.close(temp_fd)
|
|
27916
|
-
temp_fd = None
|
|
27917
|
-
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
27918
|
-
temp_name = None
|
|
27919
|
-
os.fsync(parent_fd)
|
|
27920
|
-
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
27921
|
-
finally:
|
|
27922
|
-
os.close(source_fd)
|
|
27923
|
-
if temp_fd is not None:
|
|
27924
|
-
os.close(temp_fd)
|
|
27925
|
-
if temp_name is not None and parent_fd is not None:
|
|
27926
|
-
try:
|
|
27927
|
-
os.unlink(temp_name, dir_fd=parent_fd)
|
|
27928
|
-
except FileNotFoundError:
|
|
27929
|
-
pass
|
|
27930
|
-
if parent_fd is not None:
|
|
27931
|
-
os.close(parent_fd)
|
|
27932
|
-
|
|
27933
|
-
def run_operation(operation, root_path, payload):
|
|
27934
|
-
root_fd = open_dir(root_path)
|
|
27935
|
-
try:
|
|
27936
|
-
assert_expected_root(root_fd, payload)
|
|
27937
|
-
if operation == "stat":
|
|
27938
|
-
return stat_path(root_fd, payload)
|
|
27939
|
-
if operation == "readdir":
|
|
27940
|
-
return readdir_path(root_fd, payload)
|
|
27941
|
-
if operation == "mkdirp":
|
|
27942
|
-
return mkdirp_path(root_fd, payload)
|
|
27943
|
-
if operation == "remove":
|
|
27944
|
-
return remove_path(root_fd, payload)
|
|
27945
|
-
if operation == "rename":
|
|
27946
|
-
return rename_path(root_fd, payload)
|
|
27947
|
-
if operation == "write":
|
|
27948
|
-
return write_path(root_fd, payload)
|
|
27949
|
-
if operation == "copy":
|
|
27950
|
-
return copy_path(root_fd, payload)
|
|
27951
|
-
raise RuntimeError("unknown operation: " + operation)
|
|
27952
|
-
finally:
|
|
27953
|
-
os.close(root_fd)
|
|
27954
|
-
|
|
27955
|
-
for line in sys.stdin:
|
|
27956
|
-
try:
|
|
27957
|
-
request = json.loads(line)
|
|
27958
|
-
result = run_operation(request["operation"], request["rootPath"], request.get("payload") or {})
|
|
27959
|
-
response = {"id": request["id"], "ok": True, "result": result}
|
|
27960
|
-
except Exception as exc:
|
|
27961
|
-
response = {
|
|
27962
|
-
"id": request.get("id") if isinstance(locals().get("request"), dict) else None,
|
|
27963
|
-
"ok": False,
|
|
27964
|
-
"code": exc.__class__.__name__,
|
|
27965
|
-
"errno": getattr(exc, "errno", None),
|
|
27966
|
-
"message": str(exc),
|
|
27967
|
-
}
|
|
27968
|
-
print(json.dumps(response, separators=(",", ":")), flush=True)
|
|
27969
|
-
`;
|
|
27970
|
-
let nextRequestId = 1;
|
|
27971
|
-
let worker = null;
|
|
27972
|
-
function __resetPinnedPythonWorkerForTest() {
|
|
27973
|
-
const currentWorker = worker;
|
|
27974
|
-
worker = null;
|
|
27975
|
-
if (!currentWorker) {
|
|
27976
|
-
return;
|
|
27977
|
-
}
|
|
27978
|
-
currentWorker.pending.clear();
|
|
27979
|
-
currentWorker.child.kill("SIGTERM");
|
|
27540
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/native.js
|
|
27541
|
+
|
|
27542
|
+
|
|
27543
|
+
|
|
27544
|
+
|
|
27545
|
+
|
|
27546
|
+
|
|
27547
|
+
const native_require = (0,external_node_module_.createRequire)(import.meta.url);
|
|
27548
|
+
const bundledNativeDirectory = (0,external_node_url_.fileURLToPath)(new URL("../dist/native/", import.meta.url));
|
|
27549
|
+
let native_binding;
|
|
27550
|
+
let loadError;
|
|
27551
|
+
let attempted = false;
|
|
27552
|
+
let loadBinding = loadBundledBinding;
|
|
27553
|
+
function isMuslFilename(file) {
|
|
27554
|
+
return file.includes("libc.musl-") || file.includes("ld-musl-");
|
|
27980
27555
|
}
|
|
27981
|
-
|
|
27982
|
-
"/usr/bin/python3",
|
|
27983
|
-
"/opt/homebrew/bin/python3",
|
|
27984
|
-
"/usr/local/bin/python3",
|
|
27985
|
-
];
|
|
27986
|
-
function canExecute(binPath) {
|
|
27556
|
+
function isMuslFromReport() {
|
|
27987
27557
|
try {
|
|
27988
|
-
|
|
27989
|
-
|
|
27558
|
+
if (!process.report || typeof process.report.getReport !== "function")
|
|
27559
|
+
return undefined;
|
|
27560
|
+
const report = process.report.getReport();
|
|
27561
|
+
if (report.header?.glibcVersionRuntime)
|
|
27562
|
+
return false;
|
|
27563
|
+
if (report.sharedObjects?.some(isMuslFilename))
|
|
27564
|
+
return true;
|
|
27990
27565
|
}
|
|
27991
27566
|
catch {
|
|
27992
|
-
|
|
27567
|
+
// Continue with filesystem and ELF inspection.
|
|
27993
27568
|
}
|
|
27569
|
+
return undefined;
|
|
27994
27570
|
}
|
|
27995
|
-
function
|
|
27996
|
-
const
|
|
27997
|
-
|
|
27998
|
-
|
|
27999
|
-
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
27571
|
+
function isMuslFromFilesystem() {
|
|
27572
|
+
for (const directory of ["/lib", "/usr/lib"]) {
|
|
27573
|
+
try {
|
|
27574
|
+
if ((0,external_node_fs_.readdirSync)(directory).some(isMuslFilename))
|
|
27575
|
+
return true;
|
|
27576
|
+
}
|
|
27577
|
+
catch {
|
|
27578
|
+
// A missing or unreadable conventional library directory is inconclusive.
|
|
28003
27579
|
}
|
|
28004
27580
|
}
|
|
28005
|
-
return
|
|
27581
|
+
return undefined;
|
|
28006
27582
|
}
|
|
28007
|
-
function
|
|
28008
|
-
if (
|
|
28009
|
-
|
|
27583
|
+
function readUInt(buffer, offset, bytes, littleEndian) {
|
|
27584
|
+
if (offset < 0 || offset + bytes > buffer.length)
|
|
27585
|
+
return undefined;
|
|
27586
|
+
if (bytes === 2)
|
|
27587
|
+
return littleEndian ? buffer.readUInt16LE(offset) : buffer.readUInt16BE(offset);
|
|
27588
|
+
if (bytes === 4)
|
|
27589
|
+
return littleEndian ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset);
|
|
27590
|
+
const value = littleEndian ? buffer.readBigUInt64LE(offset) : buffer.readBigUInt64BE(offset);
|
|
27591
|
+
return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : undefined;
|
|
27592
|
+
}
|
|
27593
|
+
function isMuslFromElfInterpreter() {
|
|
27594
|
+
let fd;
|
|
27595
|
+
try {
|
|
27596
|
+
fd = (0,external_node_fs_.openSync)(process.execPath, "r");
|
|
27597
|
+
const header = Buffer.alloc(64);
|
|
27598
|
+
if ((0,external_node_fs_.readSync)(fd, header, 0, header.length, 0) < 52)
|
|
27599
|
+
return undefined;
|
|
27600
|
+
if (!header.subarray(0, 4).equals(Buffer.from([0x7f, 0x45, 0x4c, 0x46])))
|
|
27601
|
+
return undefined;
|
|
27602
|
+
const elfClass = header[4];
|
|
27603
|
+
const dataEncoding = header[5];
|
|
27604
|
+
if ((elfClass !== 1 && elfClass !== 2) || (dataEncoding !== 1 && dataEncoding !== 2)) {
|
|
27605
|
+
return undefined;
|
|
27606
|
+
}
|
|
27607
|
+
const littleEndian = dataEncoding === 1;
|
|
27608
|
+
const is64Bit = elfClass === 2;
|
|
27609
|
+
const tableOffset = readUInt(header, is64Bit ? 32 : 28, is64Bit ? 8 : 4, littleEndian);
|
|
27610
|
+
const entrySize = readUInt(header, is64Bit ? 54 : 42, 2, littleEndian);
|
|
27611
|
+
const entryCount = readUInt(header, is64Bit ? 56 : 44, 2, littleEndian);
|
|
27612
|
+
if (tableOffset === undefined ||
|
|
27613
|
+
entrySize === undefined ||
|
|
27614
|
+
entryCount === undefined ||
|
|
27615
|
+
entrySize < (is64Bit ? 56 : 32) ||
|
|
27616
|
+
entryCount > 1024) {
|
|
27617
|
+
return undefined;
|
|
27618
|
+
}
|
|
27619
|
+
const programHeader = Buffer.alloc(entrySize);
|
|
27620
|
+
for (let index = 0; index < entryCount; index += 1) {
|
|
27621
|
+
const offset = tableOffset + index * entrySize;
|
|
27622
|
+
if ((0,external_node_fs_.readSync)(fd, programHeader, 0, entrySize, offset) !== entrySize)
|
|
27623
|
+
return undefined;
|
|
27624
|
+
if (readUInt(programHeader, 0, 4, littleEndian) !== 3)
|
|
27625
|
+
continue;
|
|
27626
|
+
const interpreterOffset = readUInt(programHeader, is64Bit ? 8 : 4, is64Bit ? 8 : 4, littleEndian);
|
|
27627
|
+
const interpreterSize = readUInt(programHeader, is64Bit ? 32 : 16, is64Bit ? 8 : 4, littleEndian);
|
|
27628
|
+
if (interpreterOffset === undefined ||
|
|
27629
|
+
interpreterSize === undefined ||
|
|
27630
|
+
interpreterSize < 1 ||
|
|
27631
|
+
interpreterSize > 4096) {
|
|
27632
|
+
return undefined;
|
|
27633
|
+
}
|
|
27634
|
+
const interpreter = Buffer.alloc(interpreterSize);
|
|
27635
|
+
if ((0,external_node_fs_.readSync)(fd, interpreter, 0, interpreterSize, interpreterOffset) !== interpreterSize) {
|
|
27636
|
+
return undefined;
|
|
27637
|
+
}
|
|
27638
|
+
return isMuslFilename(interpreter.toString("utf8"));
|
|
27639
|
+
}
|
|
28010
27640
|
}
|
|
28011
|
-
|
|
28012
|
-
|
|
27641
|
+
catch {
|
|
27642
|
+
return undefined;
|
|
27643
|
+
}
|
|
27644
|
+
finally {
|
|
27645
|
+
if (fd !== undefined) {
|
|
27646
|
+
try {
|
|
27647
|
+
(0,external_node_fs_.closeSync)(fd);
|
|
27648
|
+
}
|
|
27649
|
+
catch {
|
|
27650
|
+
// Best-effort detection must never turn close failure into import failure.
|
|
27651
|
+
}
|
|
27652
|
+
}
|
|
28013
27653
|
}
|
|
27654
|
+
return undefined;
|
|
28014
27655
|
}
|
|
28015
|
-
function
|
|
28016
|
-
if (
|
|
27656
|
+
function isMusl() {
|
|
27657
|
+
if (process.platform !== "linux")
|
|
28017
27658
|
return false;
|
|
27659
|
+
for (const detector of [isMuslFromReport, isMuslFromFilesystem, isMuslFromElfInterpreter]) {
|
|
27660
|
+
const result = detector();
|
|
27661
|
+
if (result !== undefined)
|
|
27662
|
+
return result;
|
|
28018
27663
|
}
|
|
28019
|
-
|
|
28020
|
-
return
|
|
28021
|
-
maybeErrno.syscall.startsWith("spawn") &&
|
|
28022
|
-
["EACCES", "ENOENT", "ENOEXEC"].includes(maybeErrno.code ?? ""));
|
|
27664
|
+
// Unknown Linux libc: try the glibc binary and let its require fail normally.
|
|
27665
|
+
return false;
|
|
28023
27666
|
}
|
|
28024
|
-
function
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
if (
|
|
28032
|
-
|
|
28033
|
-
return new errors_FsSafeError("too-large", `file exceeds limit of ${limit} bytes (got at least ${got})`);
|
|
28034
|
-
}
|
|
28035
|
-
if (message.includes("fs-safe-not-file")) {
|
|
28036
|
-
return new errors_FsSafeError("not-file", "not a file");
|
|
28037
|
-
}
|
|
28038
|
-
if (message.includes("fs-safe-source-mismatch")) {
|
|
28039
|
-
return new errors_FsSafeError("path-mismatch", "source path changed during copy");
|
|
28040
|
-
}
|
|
28041
|
-
if (message.includes("fs-safe-temp-mismatch")) {
|
|
28042
|
-
return new errors_FsSafeError("path-mismatch", "temp path changed during write");
|
|
28043
|
-
}
|
|
28044
|
-
if (message.includes("fs-safe-root-mismatch")) {
|
|
28045
|
-
return new errors_FsSafeError("path-mismatch", "root path changed during operation");
|
|
28046
|
-
}
|
|
28047
|
-
if (message.includes("fs-safe-directory-noreplace-unsupported")) {
|
|
28048
|
-
return new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
27667
|
+
function targetFor(platform, arch, musl) {
|
|
27668
|
+
if (platform === "win32" && arch === "x64")
|
|
27669
|
+
return "win32-x64-msvc";
|
|
27670
|
+
if (platform === "darwin" && arch === "x64")
|
|
27671
|
+
return "darwin-x64";
|
|
27672
|
+
if (platform === "darwin" && arch === "arm64")
|
|
27673
|
+
return "darwin-arm64";
|
|
27674
|
+
if (platform === "linux" && (arch === "x64" || arch === "arm64")) {
|
|
27675
|
+
return `linux-${arch}-${musl ? "musl" : "gnu"}`;
|
|
28049
27676
|
}
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
|
|
28057
|
-
|
|
27677
|
+
return undefined;
|
|
27678
|
+
}
|
|
27679
|
+
function bundledTarget() {
|
|
27680
|
+
return targetFor(process.platform, process.arch, isMusl());
|
|
27681
|
+
}
|
|
27682
|
+
function loadBundledBinding() {
|
|
27683
|
+
const target = bundledTarget();
|
|
27684
|
+
if (!target) {
|
|
27685
|
+
throw new Error(`Unsupported OS or architecture: ${process.platform}-${process.arch}`);
|
|
28058
27686
|
}
|
|
28059
|
-
|
|
28060
|
-
|
|
27687
|
+
return native_require((0,external_node_path_.join)(bundledNativeDirectory, target, "fs-safe-native.node"));
|
|
27688
|
+
}
|
|
27689
|
+
function __loadBundledNativeForTest() {
|
|
27690
|
+
return loadBundledBinding();
|
|
27691
|
+
}
|
|
27692
|
+
function __nativeLoaderDetectorsForTest() {
|
|
27693
|
+
return {
|
|
27694
|
+
report: isMuslFromReport(),
|
|
27695
|
+
filesystem: isMuslFromFilesystem(),
|
|
27696
|
+
elfInterpreter: isMuslFromElfInterpreter(),
|
|
27697
|
+
};
|
|
27698
|
+
}
|
|
27699
|
+
function __nativeTargetForTest(platform, arch, musl = false) {
|
|
27700
|
+
return targetFor(platform, arch, musl);
|
|
27701
|
+
}
|
|
27702
|
+
function getNativeBinding() {
|
|
27703
|
+
const { mode } = getFsSafeNativeConfig();
|
|
27704
|
+
if (mode === "off")
|
|
27705
|
+
return undefined;
|
|
27706
|
+
if (!attempted) {
|
|
27707
|
+
attempted = true;
|
|
27708
|
+
try {
|
|
27709
|
+
native_binding = loadBinding();
|
|
27710
|
+
}
|
|
27711
|
+
catch (error) {
|
|
27712
|
+
loadError = error;
|
|
27713
|
+
}
|
|
28061
27714
|
}
|
|
28062
|
-
if (
|
|
28063
|
-
return
|
|
27715
|
+
if (native_binding)
|
|
27716
|
+
return native_binding;
|
|
27717
|
+
if (mode === "require") {
|
|
27718
|
+
throw new errors_FsSafeError("helper-unavailable", "native fs-safe helper is unavailable", {
|
|
27719
|
+
cause: loadError,
|
|
27720
|
+
});
|
|
28064
27721
|
}
|
|
28065
|
-
return
|
|
27722
|
+
return undefined;
|
|
28066
27723
|
}
|
|
28067
|
-
function
|
|
28068
|
-
|
|
28069
|
-
|
|
27724
|
+
function requireNativeBinding() {
|
|
27725
|
+
const native = getNativeBinding();
|
|
27726
|
+
if (!native) {
|
|
27727
|
+
throw new FsSafeError("helper-unavailable", "native fs-safe helper is unavailable", {
|
|
27728
|
+
cause: loadError,
|
|
27729
|
+
});
|
|
28070
27730
|
}
|
|
28071
|
-
|
|
28072
|
-
|
|
28073
|
-
|
|
27731
|
+
return native;
|
|
27732
|
+
}
|
|
27733
|
+
function __setNativeLoaderForTest(loader) {
|
|
27734
|
+
native_binding = undefined;
|
|
27735
|
+
loadError = undefined;
|
|
27736
|
+
attempted = false;
|
|
27737
|
+
loadBinding = loader;
|
|
27738
|
+
}
|
|
27739
|
+
function __resetNativeLoaderForTest() {
|
|
27740
|
+
native_binding = undefined;
|
|
27741
|
+
loadError = undefined;
|
|
27742
|
+
attempted = false;
|
|
27743
|
+
loadBinding = loadBundledBinding;
|
|
27744
|
+
}
|
|
27745
|
+
|
|
27746
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/native-operations.js
|
|
27747
|
+
|
|
27748
|
+
|
|
27749
|
+
|
|
27750
|
+
|
|
27751
|
+
|
|
27752
|
+
function nativeOpenFlags(flags) {
|
|
27753
|
+
const closeOnExec = external_node_fs_.constants.O_CLOEXEC;
|
|
27754
|
+
return (flags |
|
|
27755
|
+
(closeOnExec ?? 0) |
|
|
27756
|
+
(typeof external_node_fs_.constants.O_NOFOLLOW === "number" ? external_node_fs_.constants.O_NOFOLLOW : 0));
|
|
27757
|
+
}
|
|
27758
|
+
function writeAll(fd, data) {
|
|
27759
|
+
let offset = 0;
|
|
27760
|
+
while (offset < data.byteLength) {
|
|
27761
|
+
const written = external_node_fs_.writeSync(fd, data, offset, data.byteLength - offset);
|
|
27762
|
+
if (written <= 0) {
|
|
27763
|
+
throw Object.assign(new Error("native file write made no progress"), { code: "EIO" });
|
|
27764
|
+
}
|
|
27765
|
+
offset += written;
|
|
28074
27766
|
}
|
|
28075
|
-
targetWorker.pending.clear();
|
|
28076
|
-
worker = null;
|
|
28077
27767
|
}
|
|
28078
|
-
function
|
|
28079
|
-
|
|
27768
|
+
function wrapNativeFd(fd, containment) {
|
|
27769
|
+
let open = true;
|
|
27770
|
+
return {
|
|
27771
|
+
fd,
|
|
27772
|
+
containment,
|
|
27773
|
+
async close() {
|
|
27774
|
+
if (open) {
|
|
27775
|
+
open = false;
|
|
27776
|
+
external_node_fs_.closeSync(fd);
|
|
27777
|
+
}
|
|
27778
|
+
},
|
|
27779
|
+
async stat() {
|
|
27780
|
+
return external_node_fs_.fstatSync(fd);
|
|
27781
|
+
},
|
|
27782
|
+
async writeFile(data, encoding) {
|
|
27783
|
+
writeAll(fd, Buffer.isBuffer(data) ? data : Buffer.from(data, encoding ?? "utf8"));
|
|
27784
|
+
},
|
|
27785
|
+
};
|
|
27786
|
+
}
|
|
27787
|
+
function removeNativeCreatedFileIfStillPinned(params) {
|
|
27788
|
+
if (!params.created) {
|
|
28080
27789
|
return;
|
|
28081
27790
|
}
|
|
28082
|
-
let decoded;
|
|
28083
27791
|
try {
|
|
28084
|
-
|
|
27792
|
+
const parentPathStat = external_node_fs_.lstatSync(params.parentPath);
|
|
27793
|
+
const parentFdStat = params.binding.fstatIdentity(params.parentFd);
|
|
27794
|
+
const targetPath = external_node_path_.join(params.parentPath, params.basename);
|
|
27795
|
+
const target = external_node_fs_.lstatSync(targetPath);
|
|
27796
|
+
if (!parentPathStat.isSymbolicLink() &&
|
|
27797
|
+
parentPathStat.dev === parentFdStat.dev &&
|
|
27798
|
+
parentPathStat.ino === parentFdStat.ino &&
|
|
27799
|
+
!target.isSymbolicLink() &&
|
|
27800
|
+
file_identity_sameFileIdentity(target, params.created)) {
|
|
27801
|
+
external_node_fs_.rmSync(targetPath);
|
|
27802
|
+
}
|
|
28085
27803
|
}
|
|
28086
27804
|
catch {
|
|
28087
|
-
|
|
28088
|
-
|
|
27805
|
+
// Failed cleanup leaves a fail-closed artifact instead of deleting an
|
|
27806
|
+
// unverified pathname.
|
|
28089
27807
|
}
|
|
28090
|
-
|
|
28091
|
-
|
|
28092
|
-
|
|
27808
|
+
}
|
|
27809
|
+
async function createNativeExclusiveFile(targetPath, mode) {
|
|
27810
|
+
const binding = getNativeBinding();
|
|
27811
|
+
if (!binding) {
|
|
27812
|
+
return undefined;
|
|
28093
27813
|
}
|
|
28094
|
-
const
|
|
28095
|
-
const
|
|
28096
|
-
|
|
28097
|
-
|
|
27814
|
+
const parentPath = external_node_path_.dirname(targetPath);
|
|
27815
|
+
const basename = external_node_path_.basename(targetPath);
|
|
27816
|
+
const parent = await promises_.open(parentPath, external_node_fs_.constants.O_RDONLY |
|
|
27817
|
+
(typeof external_node_fs_.constants.O_DIRECTORY === "number" ? external_node_fs_.constants.O_DIRECTORY : 0));
|
|
27818
|
+
let fd;
|
|
27819
|
+
let created;
|
|
27820
|
+
try {
|
|
27821
|
+
const opened = binding.openBeneath(parent.fd, basename, nativeOpenFlags(external_node_fs_.constants.O_WRONLY | external_node_fs_.constants.O_CREAT | external_node_fs_.constants.O_EXCL));
|
|
27822
|
+
fd = opened.fd;
|
|
27823
|
+
external_node_fs_.fchmodSync(fd, mode);
|
|
27824
|
+
created = external_node_fs_.fstatSync(fd);
|
|
27825
|
+
return wrapNativeFd(fd, opened.containment);
|
|
28098
27826
|
}
|
|
28099
|
-
|
|
28100
|
-
|
|
28101
|
-
|
|
27827
|
+
catch (error) {
|
|
27828
|
+
if (fd !== undefined) {
|
|
27829
|
+
try {
|
|
27830
|
+
external_node_fs_.closeSync(fd);
|
|
27831
|
+
}
|
|
27832
|
+
catch {
|
|
27833
|
+
// Preserve the original error.
|
|
27834
|
+
}
|
|
27835
|
+
removeNativeCreatedFileIfStillPinned({
|
|
27836
|
+
binding,
|
|
27837
|
+
parentPath,
|
|
27838
|
+
parentFd: parent.fd,
|
|
27839
|
+
basename,
|
|
27840
|
+
created,
|
|
27841
|
+
});
|
|
27842
|
+
}
|
|
27843
|
+
throw error;
|
|
28102
27844
|
}
|
|
28103
|
-
|
|
28104
|
-
|
|
28105
|
-
setWorkerRef(currentWorker, false);
|
|
27845
|
+
finally {
|
|
27846
|
+
await parent.close().catch(() => undefined);
|
|
28106
27847
|
}
|
|
28107
|
-
|
|
28108
|
-
|
|
28109
|
-
|
|
27848
|
+
}
|
|
27849
|
+
function syncNativeFileBestEffort(fd) {
|
|
27850
|
+
try {
|
|
27851
|
+
external_node_fs_.fsyncSync(fd);
|
|
27852
|
+
}
|
|
27853
|
+
catch (error) {
|
|
27854
|
+
if (error.code !== "EPERM") {
|
|
27855
|
+
throw error;
|
|
27856
|
+
}
|
|
27857
|
+
}
|
|
27858
|
+
}
|
|
27859
|
+
function writeNativeFd(fd, data) {
|
|
27860
|
+
writeAll(fd, data);
|
|
27861
|
+
}
|
|
27862
|
+
|
|
27863
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/native-pinned-write.js
|
|
27864
|
+
|
|
27865
|
+
|
|
27866
|
+
|
|
27867
|
+
|
|
27868
|
+
|
|
27869
|
+
|
|
27870
|
+
function assertWithinMaxBytes(bytes, maxBytes) {
|
|
27871
|
+
if (maxBytes !== undefined && bytes > maxBytes) {
|
|
27872
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
28110
27873
|
}
|
|
28111
|
-
pending.reject(mapWorkerError(decoded));
|
|
28112
27874
|
}
|
|
28113
|
-
function
|
|
28114
|
-
|
|
28115
|
-
|
|
28116
|
-
|
|
27875
|
+
async function inputToBuffer(input, maxBytes) {
|
|
27876
|
+
if (input.kind === "buffer") {
|
|
27877
|
+
const data = typeof input.data === "string"
|
|
27878
|
+
? Buffer.from(input.data, input.encoding ?? "utf8")
|
|
27879
|
+
: Buffer.from(input.data);
|
|
27880
|
+
assertWithinMaxBytes(data.byteLength, maxBytes);
|
|
27881
|
+
return data;
|
|
28117
27882
|
}
|
|
28118
|
-
const
|
|
28119
|
-
|
|
28120
|
-
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
27883
|
+
const chunks = [];
|
|
27884
|
+
let bytes = 0;
|
|
27885
|
+
for await (const chunk of input.stream) {
|
|
27886
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
27887
|
+
bytes += buffer.byteLength;
|
|
27888
|
+
assertWithinMaxBytes(bytes, maxBytes);
|
|
27889
|
+
chunks.push(buffer);
|
|
27890
|
+
}
|
|
27891
|
+
return Buffer.concat(chunks, bytes);
|
|
27892
|
+
}
|
|
27893
|
+
function native_pinned_write_nativeOpenFlags(flags) {
|
|
27894
|
+
const closeOnExec = external_node_fs_.constants.O_CLOEXEC;
|
|
27895
|
+
return (flags |
|
|
27896
|
+
(closeOnExec ?? 0) |
|
|
27897
|
+
(typeof external_node_fs_.constants.O_NOFOLLOW === "number" ? external_node_fs_.constants.O_NOFOLLOW : 0));
|
|
27898
|
+
}
|
|
27899
|
+
function sameNativeIdentity(left, right) {
|
|
27900
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
27901
|
+
}
|
|
27902
|
+
async function runPinnedWriteNative(binding, params) {
|
|
27903
|
+
const data = await inputToBuffer(params.input, params.maxBytes);
|
|
27904
|
+
const root = await promises_.open(params.rootPath, external_node_fs_.constants.O_RDONLY |
|
|
27905
|
+
(typeof external_node_fs_.constants.O_DIRECTORY === "number" ? external_node_fs_.constants.O_DIRECTORY : 0));
|
|
27906
|
+
let parentFd;
|
|
27907
|
+
let tempFd;
|
|
27908
|
+
let targetFd;
|
|
27909
|
+
let tempIdentity;
|
|
27910
|
+
let parentPath = params.rootPath;
|
|
27911
|
+
const tempName = `.${params.basename}.${(0,external_node_crypto_.randomUUID)()}.native.tmp`;
|
|
27912
|
+
let renamed = false;
|
|
27913
|
+
try {
|
|
27914
|
+
const rootIdentity = binding.fstatIdentity(root.fd);
|
|
27915
|
+
if (params.rootIdentity && !sameNativeIdentity(params.rootIdentity, rootIdentity)) {
|
|
27916
|
+
throw new errors_FsSafeError("path-mismatch", "root path changed during native write");
|
|
27917
|
+
}
|
|
27918
|
+
if (params.mkdir) {
|
|
27919
|
+
binding.mkdirBeneath(root.fd, params.relativeParentPath, 0o777);
|
|
27920
|
+
}
|
|
27921
|
+
const parentFlags = external_node_fs_.constants.O_RDONLY |
|
|
27922
|
+
(typeof external_node_fs_.constants.O_DIRECTORY === "number" ? external_node_fs_.constants.O_DIRECTORY : 0);
|
|
27923
|
+
parentFd = binding.openBeneath(root.fd, params.relativeParentPath, parentFlags).fd;
|
|
27924
|
+
parentPath = await promises_.realpath(params.relativeParentPath
|
|
27925
|
+
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
27926
|
+
: params.rootPath);
|
|
27927
|
+
const parentPathStat = await promises_.lstat(parentPath);
|
|
27928
|
+
const parentIdentity = binding.fstatIdentity(parentFd);
|
|
27929
|
+
if (parentPathStat.isSymbolicLink() ||
|
|
27930
|
+
!sameNativeIdentity(parentPathStat, parentIdentity)) {
|
|
27931
|
+
throw new errors_FsSafeError("path-mismatch", "native write parent changed during resolution");
|
|
28128
27932
|
}
|
|
28129
|
-
|
|
28130
|
-
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
27933
|
+
try {
|
|
27934
|
+
await promises_.lstat(external_node_path_.join(parentPath, params.basename));
|
|
27935
|
+
throw Object.assign(new Error("destination already exists"), { code: "EEXIST" });
|
|
27936
|
+
}
|
|
27937
|
+
catch (error) {
|
|
27938
|
+
if (error.code !== "ENOENT") {
|
|
27939
|
+
throw error;
|
|
28134
27940
|
}
|
|
28135
|
-
const line = currentWorker.stdoutBuffer.slice(0, newline);
|
|
28136
|
-
currentWorker.stdoutBuffer = currentWorker.stdoutBuffer.slice(newline + 1);
|
|
28137
|
-
handleWorkerLine(currentWorker, line);
|
|
28138
27941
|
}
|
|
28139
|
-
|
|
28140
|
-
|
|
28141
|
-
|
|
28142
|
-
|
|
27942
|
+
tempFd = binding.openBeneath(parentFd, tempName, native_pinned_write_nativeOpenFlags(external_node_fs_.constants.O_WRONLY | external_node_fs_.constants.O_CREAT | external_node_fs_.constants.O_EXCL)).fd;
|
|
27943
|
+
external_node_fs_.fchmodSync(tempFd, params.mode || 0o600);
|
|
27944
|
+
writeNativeFd(tempFd, data);
|
|
27945
|
+
syncNativeFileBestEffort(tempFd);
|
|
27946
|
+
tempIdentity = external_node_fs_.fstatSync(tempFd);
|
|
27947
|
+
binding.renameNoReplace(parentFd, tempName, parentFd, params.basename);
|
|
27948
|
+
renamed = true;
|
|
27949
|
+
targetFd = binding.openBeneath(parentFd, params.basename, native_pinned_write_nativeOpenFlags(external_node_fs_.constants.O_RDONLY)).fd;
|
|
27950
|
+
const targetIdentity = binding.fstatIdentity(targetFd);
|
|
27951
|
+
if (!targetIdentity.isFile || !sameNativeIdentity(tempIdentity, targetIdentity)) {
|
|
27952
|
+
throw new errors_FsSafeError("path-mismatch", "native write target changed after rename");
|
|
28143
27953
|
}
|
|
28144
|
-
|
|
28145
|
-
|
|
28146
|
-
const mapped = isSpawnUnavailable(error)
|
|
28147
|
-
? new errors_FsSafeError("helper-unavailable", "Python helper is unavailable", { cause: error })
|
|
28148
|
-
: error instanceof Error
|
|
28149
|
-
? error
|
|
28150
|
-
: new Error(String(error));
|
|
28151
|
-
rejectPending(mapped, currentWorker);
|
|
28152
|
-
});
|
|
28153
|
-
child.once("close", (code, signal) => {
|
|
28154
|
-
const stderr = currentWorker.stderr.trim();
|
|
28155
|
-
rejectPending(new errors_FsSafeError("helper-failed", stderr || `pinned helper exited with code ${code ?? "null"} (${signal ?? "?"})`), currentWorker);
|
|
28156
|
-
});
|
|
28157
|
-
process.once("exit", () => {
|
|
28158
|
-
child.kill("SIGTERM");
|
|
28159
|
-
});
|
|
28160
|
-
setWorkerRef(currentWorker, false);
|
|
28161
|
-
return currentWorker;
|
|
28162
|
-
}
|
|
28163
|
-
function setRefable(value, ref) {
|
|
28164
|
-
if (!value) {
|
|
28165
|
-
return;
|
|
27954
|
+
syncNativeFileBestEffort(parentFd);
|
|
27955
|
+
return { dev: targetIdentity.dev, ino: targetIdentity.ino };
|
|
28166
27956
|
}
|
|
28167
|
-
|
|
28168
|
-
|
|
28169
|
-
|
|
28170
|
-
}
|
|
28171
|
-
|
|
28172
|
-
|
|
28173
|
-
|
|
28174
|
-
|
|
28175
|
-
|
|
28176
|
-
|
|
28177
|
-
|
|
28178
|
-
|
|
28179
|
-
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
|
|
28183
|
-
|
|
28184
|
-
|
|
28185
|
-
|
|
28186
|
-
|
|
28187
|
-
resolve: (value) => resolve(value),
|
|
28188
|
-
});
|
|
28189
|
-
const request = JSON.stringify({
|
|
28190
|
-
id: requestId,
|
|
28191
|
-
operation: params.operation,
|
|
28192
|
-
rootPath: params.rootPath,
|
|
28193
|
-
payload: params.payload,
|
|
28194
|
-
});
|
|
28195
|
-
currentWorker.child.stdin.write(`${request}\n`, (error) => {
|
|
28196
|
-
if (error) {
|
|
28197
|
-
currentWorker.pending.delete(requestId);
|
|
28198
|
-
if (currentWorker.pending.size === 0) {
|
|
28199
|
-
setWorkerRef(currentWorker, false);
|
|
28200
|
-
}
|
|
28201
|
-
reject(error);
|
|
28202
|
-
}
|
|
28203
|
-
});
|
|
28204
|
-
});
|
|
28205
|
-
}
|
|
28206
|
-
function assertPinnedPythonOperationAvailable() {
|
|
28207
|
-
const currentWorker = getWorker();
|
|
28208
|
-
if (typeof currentWorker.child.stdin?.write !== "function") {
|
|
28209
|
-
throw new errors_FsSafeError("helper-unavailable", "Python helper stdin is unavailable");
|
|
27957
|
+
finally {
|
|
27958
|
+
if (targetFd !== undefined) {
|
|
27959
|
+
external_node_fs_.closeSync(targetFd);
|
|
27960
|
+
}
|
|
27961
|
+
if (tempFd !== undefined) {
|
|
27962
|
+
external_node_fs_.closeSync(tempFd);
|
|
27963
|
+
}
|
|
27964
|
+
if (!renamed && parentFd !== undefined) {
|
|
27965
|
+
removeNativeCreatedFileIfStillPinned({
|
|
27966
|
+
binding,
|
|
27967
|
+
parentPath,
|
|
27968
|
+
parentFd,
|
|
27969
|
+
basename: tempName,
|
|
27970
|
+
created: tempIdentity,
|
|
27971
|
+
});
|
|
27972
|
+
}
|
|
27973
|
+
if (parentFd !== undefined) {
|
|
27974
|
+
external_node_fs_.closeSync(parentFd);
|
|
27975
|
+
}
|
|
27976
|
+
await root.close().catch(() => undefined);
|
|
28210
27977
|
}
|
|
28211
27978
|
}
|
|
27979
|
+
|
|
27980
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-operation.js
|
|
27981
|
+
|
|
28212
27982
|
function validatePinnedOperationPayload(payload) {
|
|
28213
27983
|
if (typeof payload.relativePath === "string") {
|
|
28214
27984
|
validatePinnedRelativePath(payload.relativePath);
|
|
@@ -28223,9 +27993,6 @@ function validatePinnedOperationPayload(payload) {
|
|
|
28223
27993
|
validatePinnedRelativePath(payload.to);
|
|
28224
27994
|
}
|
|
28225
27995
|
}
|
|
28226
|
-
function isPinnedHelperUnavailable(error) {
|
|
28227
|
-
return error instanceof Error && "code" in error && error.code === "helper-unavailable";
|
|
28228
|
-
}
|
|
28229
27996
|
function validatePinnedRelativePath(relativePath) {
|
|
28230
27997
|
if (relativePath.length === 0 || relativePath === ".") {
|
|
28231
27998
|
return;
|
|
@@ -28247,47 +28014,91 @@ function validatePinnedRelativePath(relativePath) {
|
|
|
28247
28014
|
}
|
|
28248
28015
|
}
|
|
28249
28016
|
|
|
28250
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/
|
|
28017
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
28251
28018
|
|
|
28252
28019
|
|
|
28253
|
-
|
|
28254
|
-
|
|
28255
|
-
|
|
28256
|
-
|
|
28257
|
-
|
|
28258
|
-
|
|
28259
|
-
|
|
28260
|
-
}
|
|
28261
|
-
async function helperStat(rootDir, relativePath) {
|
|
28262
|
-
return await runPinnedHelper("stat", rootDir, { relativePath });
|
|
28020
|
+
const READ_CHUNK_BYTES = 64 * 1024;
|
|
28021
|
+
function assertMaxBytes(maxBytes) {
|
|
28022
|
+
if (maxBytes === Number.POSITIVE_INFINITY) {
|
|
28023
|
+
return;
|
|
28024
|
+
}
|
|
28025
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
28026
|
+
throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
|
|
28027
|
+
}
|
|
28263
28028
|
}
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28268
|
-
|
|
28029
|
+
function createScratchBuffer(maxBytes) {
|
|
28030
|
+
const initialReadBytes = Number.isFinite(maxBytes)
|
|
28031
|
+
? Math.min(READ_CHUNK_BYTES, maxBytes + 1)
|
|
28032
|
+
: READ_CHUNK_BYTES;
|
|
28033
|
+
return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
|
|
28269
28034
|
}
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
function isPinnedPathHelperSpawnError(error) {
|
|
28276
|
-
return canFallbackFromPythonError(error);
|
|
28035
|
+
function nextReadLength(total, maxBytes, capacity) {
|
|
28036
|
+
return Number.isFinite(maxBytes)
|
|
28037
|
+
? Math.min(capacity, maxBytes - total + 1)
|
|
28038
|
+
: capacity;
|
|
28277
28039
|
}
|
|
28278
|
-
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
});
|
|
28040
|
+
function appendChunk(params) {
|
|
28041
|
+
const total = params.total + params.bytesRead;
|
|
28042
|
+
if (total > params.maxBytes) {
|
|
28043
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got at least ${total})`);
|
|
28283
28044
|
}
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28045
|
+
params.chunks.push(Buffer.from(params.scratch.subarray(0, params.bytesRead)));
|
|
28046
|
+
return total;
|
|
28047
|
+
}
|
|
28048
|
+
async function readBoundedAsync(maxBytes, readChunk) {
|
|
28049
|
+
assertMaxBytes(maxBytes);
|
|
28050
|
+
const chunks = [];
|
|
28051
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
28052
|
+
let total = 0;
|
|
28053
|
+
while (true) {
|
|
28054
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
28055
|
+
const bytesRead = await readChunk(scratch, length);
|
|
28056
|
+
if (bytesRead === 0) {
|
|
28057
|
+
return Buffer.concat(chunks, total);
|
|
28287
28058
|
}
|
|
28288
|
-
|
|
28289
|
-
|
|
28059
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
28060
|
+
}
|
|
28061
|
+
}
|
|
28062
|
+
/**
|
|
28063
|
+
* Reads from the handle's current offset without closing it. A bounded read
|
|
28064
|
+
* consumes at most maxBytes + 1 bytes so growth after an earlier stat cannot
|
|
28065
|
+
* force an unbounded allocation.
|
|
28066
|
+
*/
|
|
28067
|
+
async function readFileHandleBounded(handle, maxBytes) {
|
|
28068
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
28069
|
+
return (await handle.read(scratch, 0, length, null)).bytesRead;
|
|
28070
|
+
});
|
|
28071
|
+
}
|
|
28072
|
+
function readDescriptorChunk(fd, scratch, length) {
|
|
28073
|
+
return new Promise((resolve, reject) => {
|
|
28074
|
+
fs.read(fd, scratch, 0, length, null, (error, bytesRead) => {
|
|
28075
|
+
if (error) {
|
|
28076
|
+
reject(error);
|
|
28077
|
+
return;
|
|
28078
|
+
}
|
|
28079
|
+
resolve(bytesRead);
|
|
28290
28080
|
});
|
|
28081
|
+
});
|
|
28082
|
+
}
|
|
28083
|
+
/** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
28084
|
+
async function readFileDescriptorBounded(fd, maxBytes) {
|
|
28085
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
28086
|
+
return await readDescriptorChunk(fd, scratch, length);
|
|
28087
|
+
});
|
|
28088
|
+
}
|
|
28089
|
+
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
28090
|
+
function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
28091
|
+
assertMaxBytes(maxBytes);
|
|
28092
|
+
const chunks = [];
|
|
28093
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
28094
|
+
let total = 0;
|
|
28095
|
+
while (true) {
|
|
28096
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
28097
|
+
const bytesRead = fs.readSync(fd, scratch, 0, length, null);
|
|
28098
|
+
if (bytesRead === 0) {
|
|
28099
|
+
return Buffer.concat(chunks, total);
|
|
28100
|
+
}
|
|
28101
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
28291
28102
|
}
|
|
28292
28103
|
}
|
|
28293
28104
|
|
|
@@ -28295,6 +28106,11 @@ async function runPinnedPathHelper(params) {
|
|
|
28295
28106
|
|
|
28296
28107
|
|
|
28297
28108
|
|
|
28109
|
+
|
|
28110
|
+
|
|
28111
|
+
|
|
28112
|
+
|
|
28113
|
+
const MAX_LOCK_PAYLOAD_BYTES = 1024 * 1024;
|
|
28298
28114
|
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
|
|
28299
28115
|
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS = SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES * 8;
|
|
28300
28116
|
const SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX = "\t".repeat(8);
|
|
@@ -28318,28 +28134,98 @@ function serializeSidecarLockPayload(payload) {
|
|
|
28318
28134
|
ownershipToken,
|
|
28319
28135
|
};
|
|
28320
28136
|
}
|
|
28321
|
-
|
|
28137
|
+
function relativeSidecarLockPath(lockRoot, lockPath) {
|
|
28138
|
+
const resolved = external_node_path_.resolve(lockPath);
|
|
28139
|
+
const lexicalRelative = external_node_path_.relative(lockRoot.rootDir, resolved);
|
|
28140
|
+
const relative = lexicalRelative !== ".." &&
|
|
28141
|
+
!lexicalRelative.startsWith(`..${external_node_path_.sep}`) &&
|
|
28142
|
+
!external_node_path_.isAbsolute(lexicalRelative)
|
|
28143
|
+
? lexicalRelative
|
|
28144
|
+
: external_node_path_.relative(lockRoot.rootReal, resolved);
|
|
28145
|
+
if (!relative || relative === ".." || relative.startsWith(`..${external_node_path_.sep}`) || external_node_path_.isAbsolute(relative)) {
|
|
28146
|
+
throw new errors_FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
|
|
28147
|
+
}
|
|
28148
|
+
return relative.split(external_node_path_.sep).join(external_node_path_.posix.sep);
|
|
28149
|
+
}
|
|
28150
|
+
function parseSidecarLockPayload(raw, parser) {
|
|
28151
|
+
if (parser) {
|
|
28152
|
+
return parser(raw);
|
|
28153
|
+
}
|
|
28154
|
+
try {
|
|
28155
|
+
const parsed = JSON.parse(raw);
|
|
28156
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
28157
|
+
}
|
|
28158
|
+
catch {
|
|
28159
|
+
return null;
|
|
28160
|
+
}
|
|
28161
|
+
}
|
|
28162
|
+
async function readSidecarLockSnapshot(lockPath, options = {}) {
|
|
28322
28163
|
try {
|
|
28164
|
+
if (options.lockRoot) {
|
|
28165
|
+
const opened = await options.lockRoot.open(relativeSidecarLockPath(options.lockRoot, lockPath));
|
|
28166
|
+
try {
|
|
28167
|
+
const raw = (await readFileHandleBounded(opened.handle, MAX_LOCK_PAYLOAD_BYTES)).toString("utf8");
|
|
28168
|
+
return {
|
|
28169
|
+
raw,
|
|
28170
|
+
payload: parseSidecarLockPayload(raw, options.parsePayload),
|
|
28171
|
+
stat: opened.stat,
|
|
28172
|
+
};
|
|
28173
|
+
}
|
|
28174
|
+
finally {
|
|
28175
|
+
await opened.handle.close().catch(() => undefined);
|
|
28176
|
+
}
|
|
28177
|
+
}
|
|
28323
28178
|
const stat = await promises_.lstat(lockPath);
|
|
28324
28179
|
const raw = await promises_.readFile(lockPath, "utf8");
|
|
28325
|
-
|
|
28326
|
-
const parsed = JSON.parse(raw);
|
|
28327
|
-
const payload = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
28328
|
-
? parsed
|
|
28329
|
-
: null;
|
|
28330
|
-
return { raw, payload, stat };
|
|
28331
|
-
}
|
|
28332
|
-
catch {
|
|
28333
|
-
return { raw, payload: null, stat };
|
|
28334
|
-
}
|
|
28180
|
+
return { raw, payload: parseSidecarLockPayload(raw, options.parsePayload), stat };
|
|
28335
28181
|
}
|
|
28336
28182
|
catch (err) {
|
|
28337
|
-
if (err.code === "ENOENT"
|
|
28183
|
+
if (err.code === "ENOENT" ||
|
|
28184
|
+
(err instanceof errors_FsSafeError && err.code === "not-found")) {
|
|
28338
28185
|
return null;
|
|
28339
28186
|
}
|
|
28340
28187
|
throw err;
|
|
28341
28188
|
}
|
|
28342
28189
|
}
|
|
28190
|
+
function readSidecarLockSnapshotSync(lockPath, parsePayload) {
|
|
28191
|
+
let fd;
|
|
28192
|
+
try {
|
|
28193
|
+
const before = fsSync.lstatSync(lockPath);
|
|
28194
|
+
if (!before.isFile() || before.isSymbolicLink())
|
|
28195
|
+
return null;
|
|
28196
|
+
const noFollow = process.platform !== "win32" && typeof fsSync.constants.O_NOFOLLOW === "number"
|
|
28197
|
+
? fsSync.constants.O_NOFOLLOW
|
|
28198
|
+
: 0;
|
|
28199
|
+
fd = fsSync.openSync(lockPath, fsSync.constants.O_RDONLY | noFollow);
|
|
28200
|
+
const opened = fsSync.fstatSync(fd);
|
|
28201
|
+
const raw = fsSync.readFileSync(fd, "utf8");
|
|
28202
|
+
const after = fsSync.lstatSync(lockPath);
|
|
28203
|
+
if (!sameFileIdentity(before, opened) || !sameFileIdentity(opened, after))
|
|
28204
|
+
return null;
|
|
28205
|
+
return {
|
|
28206
|
+
raw,
|
|
28207
|
+
payload: parseSidecarLockPayload(raw, parsePayload),
|
|
28208
|
+
stat: after,
|
|
28209
|
+
ownershipToken: readSidecarLockOwnershipToken(raw),
|
|
28210
|
+
};
|
|
28211
|
+
}
|
|
28212
|
+
catch (error) {
|
|
28213
|
+
if (error.code === "ENOENT")
|
|
28214
|
+
return null;
|
|
28215
|
+
throw error;
|
|
28216
|
+
}
|
|
28217
|
+
finally {
|
|
28218
|
+
if (fd !== undefined)
|
|
28219
|
+
fsSync.closeSync(fd);
|
|
28220
|
+
}
|
|
28221
|
+
}
|
|
28222
|
+
function removeSidecarLockIfUnchangedSync(lockPath, observed) {
|
|
28223
|
+
const current = readSidecarLockSnapshotSync(lockPath);
|
|
28224
|
+
if (!current || !sidecarLockSnapshotMatches(current, observed))
|
|
28225
|
+
return false;
|
|
28226
|
+
fsSync.rmSync(lockPath);
|
|
28227
|
+
return true;
|
|
28228
|
+
}
|
|
28343
28229
|
function sidecarLockSnapshotMatches(current, observed) {
|
|
28344
28230
|
if (observed.ownershipToken !== undefined) {
|
|
28345
28231
|
return (current.stat?.isFile() === true &&
|
|
@@ -28357,16 +28243,21 @@ function sidecarLockSnapshotMatches(current, observed) {
|
|
|
28357
28243
|
}
|
|
28358
28244
|
return observed.stat !== undefined && current.stat !== undefined;
|
|
28359
28245
|
}
|
|
28360
|
-
async function removeSidecarLockIfUnchanged(lockPath, observed) {
|
|
28361
|
-
const current = await readSidecarLockSnapshot(lockPath);
|
|
28246
|
+
async function removeSidecarLockIfUnchanged(lockPath, observed, options = {}) {
|
|
28247
|
+
const current = await readSidecarLockSnapshot(lockPath, options);
|
|
28362
28248
|
if (!current || !observed || !sidecarLockSnapshotMatches(current, observed)) {
|
|
28363
28249
|
return false;
|
|
28364
28250
|
}
|
|
28365
|
-
|
|
28251
|
+
if (options.lockRoot) {
|
|
28252
|
+
await options.lockRoot.remove(relativeSidecarLockPath(options.lockRoot, lockPath)).catch(() => undefined);
|
|
28253
|
+
}
|
|
28254
|
+
else {
|
|
28255
|
+
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28256
|
+
}
|
|
28366
28257
|
return true;
|
|
28367
28258
|
}
|
|
28368
|
-
async function sidecarLockSnapshotStillPresent(lockPath, observed) {
|
|
28369
|
-
const current = await readSidecarLockSnapshot(lockPath);
|
|
28259
|
+
async function sidecarLockSnapshotStillPresent(lockPath, observed, options = {}) {
|
|
28260
|
+
const current = await readSidecarLockSnapshot(lockPath, options);
|
|
28370
28261
|
return !!current && !!observed && sidecarLockSnapshotMatches(current, observed);
|
|
28371
28262
|
}
|
|
28372
28263
|
async function sidecarReclaimGuardExists(pathname) {
|
|
@@ -28402,7 +28293,8 @@ async function removeStaleSidecarLockIfAllowed(params) {
|
|
|
28402
28293
|
if (!params.shouldRemoveStaleLock || params.snapshot.raw === undefined) {
|
|
28403
28294
|
return "not-approved";
|
|
28404
28295
|
}
|
|
28405
|
-
|
|
28296
|
+
const ioOptions = { lockRoot: params.lockRoot, parsePayload: params.parsePayload };
|
|
28297
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions))) {
|
|
28406
28298
|
return "changed";
|
|
28407
28299
|
}
|
|
28408
28300
|
if (!(await params.shouldRemoveStaleLock({
|
|
@@ -28413,11 +28305,16 @@ async function removeStaleSidecarLockIfAllowed(params) {
|
|
|
28413
28305
|
}))) {
|
|
28414
28306
|
return "not-approved";
|
|
28415
28307
|
}
|
|
28416
|
-
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot))) {
|
|
28308
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions))) {
|
|
28417
28309
|
return "changed";
|
|
28418
28310
|
}
|
|
28419
28311
|
try {
|
|
28420
|
-
|
|
28312
|
+
if (params.lockRoot) {
|
|
28313
|
+
await params.lockRoot.remove(relativeSidecarLockPath(params.lockRoot, params.lockPath));
|
|
28314
|
+
}
|
|
28315
|
+
else {
|
|
28316
|
+
await promises_.rm(params.lockPath);
|
|
28317
|
+
}
|
|
28421
28318
|
return "removed";
|
|
28422
28319
|
}
|
|
28423
28320
|
catch (err) {
|
|
@@ -28428,12 +28325,77 @@ async function removeStaleSidecarLockIfAllowed(params) {
|
|
|
28428
28325
|
}
|
|
28429
28326
|
}
|
|
28430
28327
|
|
|
28328
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.js
|
|
28329
|
+
|
|
28330
|
+
function createSidecarLockHandle(params) {
|
|
28331
|
+
let released = false;
|
|
28332
|
+
const release = async () => {
|
|
28333
|
+
if (released)
|
|
28334
|
+
return;
|
|
28335
|
+
released = true;
|
|
28336
|
+
await params.release();
|
|
28337
|
+
};
|
|
28338
|
+
return {
|
|
28339
|
+
lockPath: params.lockPath,
|
|
28340
|
+
normalizedTargetPath: params.normalizedTargetPath,
|
|
28341
|
+
verifyStillHeld: params.verifyStillHeld,
|
|
28342
|
+
release,
|
|
28343
|
+
[Symbol.asyncDispose]: release,
|
|
28344
|
+
};
|
|
28345
|
+
}
|
|
28346
|
+
function createHeldSidecarLockHandle(params) {
|
|
28347
|
+
return createSidecarLockHandle({
|
|
28348
|
+
lockPath: params.held.lockPath,
|
|
28349
|
+
normalizedTargetPath: params.normalizedTargetPath,
|
|
28350
|
+
verifyStillHeld: async () => await sidecarLockSnapshotStillPresent(params.held.lockPath, params.held.snapshot, {
|
|
28351
|
+
lockRoot: params.held.lockRoot,
|
|
28352
|
+
parsePayload: params.held.parsePayload,
|
|
28353
|
+
}),
|
|
28354
|
+
release: params.release,
|
|
28355
|
+
});
|
|
28356
|
+
}
|
|
28357
|
+
|
|
28358
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.js
|
|
28359
|
+
|
|
28360
|
+
function computeSidecarLockDelayMs(retry, attempt) {
|
|
28361
|
+
const minTimeout = retry.minTimeout ?? 50;
|
|
28362
|
+
const maxTimeout = retry.maxTimeout ?? 1000;
|
|
28363
|
+
const factor = retry.factor ?? 1;
|
|
28364
|
+
const base = Math.min(maxTimeout, Math.max(minTimeout, minTimeout * factor ** attempt));
|
|
28365
|
+
const jitter = retry.randomize ? 1 + Math.random() : 1;
|
|
28366
|
+
return Math.min(maxTimeout, Math.round(base * jitter));
|
|
28367
|
+
}
|
|
28368
|
+
function sidecarLockPayloadIsStale(payload, staleMs, nowMs) {
|
|
28369
|
+
const createdAt = payload &&
|
|
28370
|
+
typeof payload === "object" &&
|
|
28371
|
+
"createdAt" in payload &&
|
|
28372
|
+
typeof payload.createdAt === "string"
|
|
28373
|
+
? payload.createdAt
|
|
28374
|
+
: "";
|
|
28375
|
+
const createdAtMs = Date.parse(createdAt);
|
|
28376
|
+
return Number.isFinite(createdAtMs) && nowMs - createdAtMs > staleMs;
|
|
28377
|
+
}
|
|
28378
|
+
async function defaultSidecarLockShouldReclaim(params) {
|
|
28379
|
+
if (sidecarLockPayloadIsStale(params.payload, params.staleMs, params.nowMs))
|
|
28380
|
+
return true;
|
|
28381
|
+
try {
|
|
28382
|
+
return params.nowMs - (await promises_.stat(params.lockPath)).mtimeMs > params.staleMs;
|
|
28383
|
+
}
|
|
28384
|
+
catch {
|
|
28385
|
+
return true;
|
|
28386
|
+
}
|
|
28387
|
+
}
|
|
28388
|
+
|
|
28431
28389
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
28432
28390
|
|
|
28433
28391
|
|
|
28434
28392
|
|
|
28435
28393
|
|
|
28436
28394
|
|
|
28395
|
+
|
|
28396
|
+
|
|
28397
|
+
|
|
28398
|
+
|
|
28437
28399
|
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
28438
28400
|
function getGlobalManagers() {
|
|
28439
28401
|
const globalWithState = globalThis;
|
|
@@ -28459,6 +28421,9 @@ function resolveManagerState(key) {
|
|
|
28459
28421
|
// Backfill state created by fs-safe versions that predate reclaim guards.
|
|
28460
28422
|
state.reclaimCleanupRegistered ??= false;
|
|
28461
28423
|
state.reclaimGuards ??= new Set();
|
|
28424
|
+
for (const held of state.held.values()) {
|
|
28425
|
+
held.refCount ??= 1;
|
|
28426
|
+
}
|
|
28462
28427
|
}
|
|
28463
28428
|
return state;
|
|
28464
28429
|
}
|
|
@@ -28514,28 +28479,6 @@ async function resolveNormalizedTargetPath(targetPath) {
|
|
|
28514
28479
|
return resolved;
|
|
28515
28480
|
}
|
|
28516
28481
|
}
|
|
28517
|
-
function computeDelayMs(retry, attempt) {
|
|
28518
|
-
const minTimeout = retry.minTimeout ?? 50;
|
|
28519
|
-
const maxTimeout = retry.maxTimeout ?? 1000;
|
|
28520
|
-
const factor = retry.factor ?? 1;
|
|
28521
|
-
const base = Math.min(maxTimeout, Math.max(minTimeout, minTimeout * factor ** attempt));
|
|
28522
|
-
const jitter = retry.randomize ? 1 + Math.random() : 1;
|
|
28523
|
-
return Math.min(maxTimeout, Math.round(base * jitter));
|
|
28524
|
-
}
|
|
28525
|
-
async function defaultShouldReclaim(params) {
|
|
28526
|
-
const createdAt = typeof params.payload?.createdAt === "string" ? params.payload.createdAt : "";
|
|
28527
|
-
const createdAtMs = Date.parse(createdAt);
|
|
28528
|
-
if (Number.isFinite(createdAtMs) && params.nowMs - createdAtMs > params.staleMs) {
|
|
28529
|
-
return true;
|
|
28530
|
-
}
|
|
28531
|
-
try {
|
|
28532
|
-
const stat = await promises_.stat(params.lockPath);
|
|
28533
|
-
return params.nowMs - stat.mtimeMs > params.staleMs;
|
|
28534
|
-
}
|
|
28535
|
-
catch {
|
|
28536
|
-
return true;
|
|
28537
|
-
}
|
|
28538
|
-
}
|
|
28539
28482
|
function releaseAllReclaimGuardsSync(state) {
|
|
28540
28483
|
for (const reclaimGuardPath of state.reclaimGuards) {
|
|
28541
28484
|
try {
|
|
@@ -28551,7 +28494,7 @@ function releaseAllLocksSync(state) {
|
|
|
28551
28494
|
for (const [normalizedTargetPath, held] of state.held) {
|
|
28552
28495
|
void held.handle.close().catch(() => undefined);
|
|
28553
28496
|
try {
|
|
28554
|
-
if (snapshotMatchesSync(held.lockPath, held.snapshot)) {
|
|
28497
|
+
if (!held.lockRoot && snapshotMatchesSync(held.lockPath, held.snapshot)) {
|
|
28555
28498
|
external_node_fs_.rmSync(held.lockPath, { force: true });
|
|
28556
28499
|
}
|
|
28557
28500
|
}
|
|
@@ -28562,17 +28505,17 @@ function releaseAllLocksSync(state) {
|
|
|
28562
28505
|
}
|
|
28563
28506
|
releaseAllReclaimGuardsSync(state);
|
|
28564
28507
|
}
|
|
28565
|
-
async function releaseHeldLock(state, normalizedTargetPath, held,
|
|
28508
|
+
async function releaseHeldLock(state, normalizedTargetPath, held, options = {}) {
|
|
28566
28509
|
const current = state.held.get(normalizedTargetPath);
|
|
28567
28510
|
if (current !== held) {
|
|
28568
28511
|
return false;
|
|
28569
28512
|
}
|
|
28570
|
-
if (
|
|
28571
|
-
held.
|
|
28513
|
+
if (options.force) {
|
|
28514
|
+
held.refCount = 0;
|
|
28572
28515
|
}
|
|
28573
28516
|
else {
|
|
28574
|
-
held.
|
|
28575
|
-
if (held.
|
|
28517
|
+
held.refCount -= 1;
|
|
28518
|
+
if (held.refCount > 0) {
|
|
28576
28519
|
return false;
|
|
28577
28520
|
}
|
|
28578
28521
|
}
|
|
@@ -28581,9 +28524,16 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
28581
28524
|
return true;
|
|
28582
28525
|
}
|
|
28583
28526
|
state.held.delete(normalizedTargetPath);
|
|
28527
|
+
if (held.compromiseTimer) {
|
|
28528
|
+
clearInterval(held.compromiseTimer);
|
|
28529
|
+
held.compromiseTimer = undefined;
|
|
28530
|
+
}
|
|
28584
28531
|
held.releasePromise = (async () => {
|
|
28585
28532
|
await held.handle.close().catch(() => undefined);
|
|
28586
|
-
await removeSidecarLockIfUnchanged(held.lockPath, held.snapshot
|
|
28533
|
+
await removeSidecarLockIfUnchanged(held.lockPath, held.snapshot, {
|
|
28534
|
+
lockRoot: held.lockRoot,
|
|
28535
|
+
parsePayload: held.parsePayload,
|
|
28536
|
+
});
|
|
28587
28537
|
})();
|
|
28588
28538
|
try {
|
|
28589
28539
|
await held.releasePromise;
|
|
@@ -28593,6 +28543,13 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
28593
28543
|
held.releasePromise = undefined;
|
|
28594
28544
|
}
|
|
28595
28545
|
}
|
|
28546
|
+
function handleForHeldLock(state, normalizedTargetPath, held) {
|
|
28547
|
+
return createHeldSidecarLockHandle({
|
|
28548
|
+
normalizedTargetPath,
|
|
28549
|
+
held,
|
|
28550
|
+
release: async () => await releaseHeldLock(state, normalizedTargetPath, held),
|
|
28551
|
+
});
|
|
28552
|
+
}
|
|
28596
28553
|
function createSidecarLockManager(key) {
|
|
28597
28554
|
const state = resolveManagerState(key);
|
|
28598
28555
|
function ensureExitCleanupRegistered() {
|
|
@@ -28612,15 +28569,12 @@ function createSidecarLockManager(key) {
|
|
|
28612
28569
|
const normalizedTargetPath = await resolveNormalizedTargetPath(options.targetPath);
|
|
28613
28570
|
const lockPath = options.lockPath ?? `${normalizedTargetPath}.lock`;
|
|
28614
28571
|
const held = state.held.get(normalizedTargetPath);
|
|
28615
|
-
if (held &&
|
|
28616
|
-
|
|
28617
|
-
|
|
28618
|
-
|
|
28619
|
-
|
|
28620
|
-
|
|
28621
|
-
release,
|
|
28622
|
-
[Symbol.asyncDispose]: release,
|
|
28623
|
-
};
|
|
28572
|
+
if (held &&
|
|
28573
|
+
options.reentrantOwner !== undefined &&
|
|
28574
|
+
held.reentrantOwner !== undefined &&
|
|
28575
|
+
options.reentrantOwner === held.reentrantOwner) {
|
|
28576
|
+
held.refCount += 1;
|
|
28577
|
+
return handleForHeldLock(state, normalizedTargetPath, held);
|
|
28624
28578
|
}
|
|
28625
28579
|
const startedAt = Date.now();
|
|
28626
28580
|
const retry = options.retry ?? {};
|
|
@@ -28643,7 +28597,7 @@ function createSidecarLockManager(key) {
|
|
|
28643
28597
|
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
28644
28598
|
? Number.POSITIVE_INFINITY
|
|
28645
28599
|
: Math.max(0, options.timeoutMs - elapsed);
|
|
28646
|
-
const delay = Math.min(
|
|
28600
|
+
const delay = Math.min(computeSidecarLockDelayMs(retry, attempt), remaining);
|
|
28647
28601
|
attempt += 1;
|
|
28648
28602
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28649
28603
|
};
|
|
@@ -28655,18 +28609,36 @@ function createSidecarLockManager(key) {
|
|
|
28655
28609
|
}
|
|
28656
28610
|
let handle = null;
|
|
28657
28611
|
try {
|
|
28658
|
-
handle = await promises_.open(lockPath, "wx");
|
|
28659
28612
|
const payload = await options.payload();
|
|
28660
28613
|
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
28661
|
-
|
|
28614
|
+
if (options.lockRoot) {
|
|
28615
|
+
const relativeLockPath = relativeSidecarLockPath(options.lockRoot, lockPath);
|
|
28616
|
+
try {
|
|
28617
|
+
await options.lockRoot.create(relativeLockPath, raw, { mkdir: true, mode: 0o600 });
|
|
28618
|
+
}
|
|
28619
|
+
catch (error) {
|
|
28620
|
+
if (error instanceof errors_FsSafeError && error.code === "already-exists") {
|
|
28621
|
+
throw Object.assign(new Error("sidecar lock exists"), { code: "EEXIST" });
|
|
28622
|
+
}
|
|
28623
|
+
throw error;
|
|
28624
|
+
}
|
|
28625
|
+
handle = (await options.lockRoot.open(relativeLockPath)).handle;
|
|
28626
|
+
}
|
|
28627
|
+
else {
|
|
28628
|
+
handle = (await createNativeExclusiveFile(lockPath, 0o600)) ?? await promises_.open(lockPath, "wx");
|
|
28629
|
+
await handle.writeFile(raw, "utf8");
|
|
28630
|
+
}
|
|
28662
28631
|
const snapshot = { raw, payload, stat: await handle.stat(), ownershipToken };
|
|
28663
28632
|
const createdHeld = {
|
|
28664
|
-
|
|
28633
|
+
refCount: 1,
|
|
28634
|
+
reentrantOwner: options.reentrantOwner,
|
|
28665
28635
|
handle,
|
|
28666
28636
|
lockPath,
|
|
28667
28637
|
snapshot,
|
|
28668
28638
|
acquiredAt: Date.now(),
|
|
28669
28639
|
metadata: options.metadata ?? {},
|
|
28640
|
+
lockRoot: options.lockRoot,
|
|
28641
|
+
parsePayload: options.parsePayload,
|
|
28670
28642
|
};
|
|
28671
28643
|
state.held.set(normalizedTargetPath, createdHeld);
|
|
28672
28644
|
if (ownsReclaimGuard) {
|
|
@@ -28679,13 +28651,21 @@ function createSidecarLockManager(key) {
|
|
|
28679
28651
|
throw err;
|
|
28680
28652
|
}
|
|
28681
28653
|
}
|
|
28682
|
-
const
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
|
|
28688
|
-
|
|
28654
|
+
const returnedHandle = handleForHeldLock(state, normalizedTargetPath, createdHeld);
|
|
28655
|
+
const interval = options.compromiseCheckIntervalMs;
|
|
28656
|
+
if (options.onCompromised && interval !== undefined && interval > 0) {
|
|
28657
|
+
createdHeld.compromiseTimer = setInterval(() => {
|
|
28658
|
+
void returnedHandle.verifyStillHeld().then((stillHeld) => {
|
|
28659
|
+
if (!stillHeld && createdHeld.compromiseTimer) {
|
|
28660
|
+
clearInterval(createdHeld.compromiseTimer);
|
|
28661
|
+
createdHeld.compromiseTimer = undefined;
|
|
28662
|
+
options.onCompromised?.({ lockPath, normalizedTargetPath });
|
|
28663
|
+
}
|
|
28664
|
+
});
|
|
28665
|
+
}, interval);
|
|
28666
|
+
createdHeld.compromiseTimer.unref();
|
|
28667
|
+
}
|
|
28668
|
+
return returnedHandle;
|
|
28689
28669
|
}
|
|
28690
28670
|
catch (err) {
|
|
28691
28671
|
if (handle) {
|
|
@@ -28702,11 +28682,16 @@ function createSidecarLockManager(key) {
|
|
|
28702
28682
|
}
|
|
28703
28683
|
// If payload serialization/write fails, the file may be empty or
|
|
28704
28684
|
// partial JSON, so remove while our exclusive handle is still open.
|
|
28705
|
-
|
|
28685
|
+
if (!options.lockRoot) {
|
|
28686
|
+
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28687
|
+
}
|
|
28706
28688
|
await handle.close().catch(() => undefined);
|
|
28707
28689
|
// Windows can refuse removing an open file; retry after close but
|
|
28708
28690
|
// only if the path still points at the file identity we created.
|
|
28709
|
-
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot
|
|
28691
|
+
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot, {
|
|
28692
|
+
lockRoot: options.lockRoot,
|
|
28693
|
+
parsePayload: options.parsePayload,
|
|
28694
|
+
});
|
|
28710
28695
|
}
|
|
28711
28696
|
if (err.code !== "EEXIST") {
|
|
28712
28697
|
throw err;
|
|
@@ -28717,11 +28702,18 @@ function createSidecarLockManager(key) {
|
|
|
28717
28702
|
continue;
|
|
28718
28703
|
}
|
|
28719
28704
|
const nowMs = Date.now();
|
|
28720
|
-
const snapshot = await readSidecarLockSnapshot(lockPath
|
|
28705
|
+
const snapshot = await readSidecarLockSnapshot(lockPath, {
|
|
28706
|
+
lockRoot: options.lockRoot,
|
|
28707
|
+
parsePayload: options.parsePayload,
|
|
28708
|
+
});
|
|
28721
28709
|
if (!snapshot) {
|
|
28722
28710
|
continue;
|
|
28723
28711
|
}
|
|
28724
|
-
|
|
28712
|
+
if (state.held.has(normalizedTargetPath)) {
|
|
28713
|
+
await waitForRetry();
|
|
28714
|
+
continue;
|
|
28715
|
+
}
|
|
28716
|
+
const shouldReclaim = options.shouldReclaim ?? defaultSidecarLockShouldReclaim;
|
|
28725
28717
|
if (await shouldReclaim({
|
|
28726
28718
|
lockPath,
|
|
28727
28719
|
normalizedTargetPath,
|
|
@@ -28730,7 +28722,10 @@ function createSidecarLockManager(key) {
|
|
|
28730
28722
|
nowMs,
|
|
28731
28723
|
heldByThisProcess: state.held.has(normalizedTargetPath),
|
|
28732
28724
|
})) {
|
|
28733
|
-
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot
|
|
28725
|
+
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot, {
|
|
28726
|
+
lockRoot: options.lockRoot,
|
|
28727
|
+
parsePayload: options.parsePayload,
|
|
28728
|
+
}))) {
|
|
28734
28729
|
continue;
|
|
28735
28730
|
}
|
|
28736
28731
|
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
@@ -28745,6 +28740,8 @@ function createSidecarLockManager(key) {
|
|
|
28745
28740
|
normalizedTargetPath,
|
|
28746
28741
|
snapshot,
|
|
28747
28742
|
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
28743
|
+
lockRoot: options.lockRoot,
|
|
28744
|
+
parsePayload: options.parsePayload,
|
|
28748
28745
|
});
|
|
28749
28746
|
if (removal === "removed" || removal === "changed") {
|
|
28750
28747
|
continue;
|
|
@@ -28832,6 +28829,7 @@ function __setFsSafeTestHooksForTest(hooks) {
|
|
|
28832
28829
|
|
|
28833
28830
|
|
|
28834
28831
|
|
|
28832
|
+
|
|
28835
28833
|
function pinned_write_byteLength(input, encoding) {
|
|
28836
28834
|
return typeof input === "string"
|
|
28837
28835
|
? Buffer.byteLength(input, encoding ?? "utf8")
|
|
@@ -28846,7 +28844,7 @@ function assertSafeBasename(basename) {
|
|
|
28846
28844
|
throw new errors_FsSafeError("invalid-path", "invalid target path");
|
|
28847
28845
|
}
|
|
28848
28846
|
}
|
|
28849
|
-
function
|
|
28847
|
+
function pinned_write_assertWithinMaxBytes(bytes, maxBytes) {
|
|
28850
28848
|
if (maxBytes !== undefined && bytes > maxBytes) {
|
|
28851
28849
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
28852
28850
|
}
|
|
@@ -28866,7 +28864,7 @@ async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
|
28866
28864
|
for await (const chunk of stream) {
|
|
28867
28865
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
28868
28866
|
bytes += buffer.byteLength;
|
|
28869
|
-
|
|
28867
|
+
pinned_write_assertWithinMaxBytes(bytes, maxBytes);
|
|
28870
28868
|
let offset = 0;
|
|
28871
28869
|
while (offset < buffer.byteLength) {
|
|
28872
28870
|
const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
|
|
@@ -28877,74 +28875,21 @@ async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
|
28877
28875
|
}
|
|
28878
28876
|
}
|
|
28879
28877
|
}
|
|
28880
|
-
async function inputToBase64(input, maxBytes) {
|
|
28881
|
-
if (input.kind === "buffer") {
|
|
28882
|
-
assertWithinMaxBytes(pinned_write_byteLength(input.data, input.encoding), maxBytes);
|
|
28883
|
-
return (typeof input.data === "string"
|
|
28884
|
-
? Buffer.from(input.data, input.encoding ?? "utf8")
|
|
28885
|
-
: input.data).toString("base64");
|
|
28886
|
-
}
|
|
28887
|
-
const chunks = [];
|
|
28888
|
-
let bytes = 0;
|
|
28889
|
-
for await (const chunk of input.stream) {
|
|
28890
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
28891
|
-
bytes += buffer.byteLength;
|
|
28892
|
-
assertWithinMaxBytes(bytes, maxBytes);
|
|
28893
|
-
chunks.push(buffer);
|
|
28894
|
-
}
|
|
28895
|
-
return Buffer.concat(chunks, bytes).toString("base64");
|
|
28896
|
-
}
|
|
28897
28878
|
async function runPinnedWriteHelper(params) {
|
|
28898
28879
|
assertSafeBasename(params.basename);
|
|
28899
28880
|
validatePinnedOperationPayload({
|
|
28900
28881
|
relativeParentPath: params.relativeParentPath,
|
|
28901
28882
|
});
|
|
28902
|
-
// The
|
|
28903
|
-
//
|
|
28904
|
-
// Node fallback, where content verification can replace that one check.
|
|
28883
|
+
// The explicit compatibility policy uses the guarded Node fallback, where
|
|
28884
|
+
// content verification can replace the strict post-rename inode check.
|
|
28905
28885
|
if (params.onRenameIdentityMismatch === "verify-content") {
|
|
28906
28886
|
return await runPinnedWriteFallback(params);
|
|
28907
28887
|
}
|
|
28908
|
-
|
|
28909
|
-
|
|
28910
|
-
|
|
28911
|
-
if (params.input.kind === "stream") {
|
|
28912
|
-
try {
|
|
28913
|
-
assertPinnedPythonOperationAvailable();
|
|
28914
|
-
}
|
|
28915
|
-
catch (error) {
|
|
28916
|
-
if (canFallbackFromPythonError(error)) {
|
|
28917
|
-
return await runPinnedWriteFallback(params);
|
|
28918
|
-
}
|
|
28919
|
-
throw error;
|
|
28920
|
-
}
|
|
28921
|
-
}
|
|
28922
|
-
const input = params.input.kind === "stream"
|
|
28923
|
-
? { kind: "buffer", data: Buffer.from(await inputToBase64(params.input, params.maxBytes), "base64") }
|
|
28924
|
-
: params.input;
|
|
28925
|
-
const payload = {
|
|
28926
|
-
base64: await inputToBase64(input, params.maxBytes),
|
|
28927
|
-
basename: params.basename,
|
|
28928
|
-
maxBytes: params.maxBytes ?? -1,
|
|
28929
|
-
mkdir: params.mkdir,
|
|
28930
|
-
mode: params.mode || 0o600,
|
|
28931
|
-
overwrite: params.overwrite !== false,
|
|
28932
|
-
relativeParentPath: params.relativeParentPath,
|
|
28933
|
-
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
28934
|
-
};
|
|
28935
|
-
try {
|
|
28936
|
-
return await runPinnedPythonOperation({
|
|
28937
|
-
operation: "write",
|
|
28938
|
-
rootPath: params.rootPath,
|
|
28939
|
-
payload,
|
|
28940
|
-
});
|
|
28941
|
-
}
|
|
28942
|
-
catch (error) {
|
|
28943
|
-
if (canFallbackFromPythonError(error)) {
|
|
28944
|
-
return await runPinnedWriteFallback({ ...params, input });
|
|
28945
|
-
}
|
|
28946
|
-
throw error;
|
|
28888
|
+
const native = getNativeBinding();
|
|
28889
|
+
if (native && params.overwrite === false) {
|
|
28890
|
+
return await runPinnedWriteNative(native, params);
|
|
28947
28891
|
}
|
|
28892
|
+
return await runPinnedWriteFallback(params);
|
|
28948
28893
|
}
|
|
28949
28894
|
async function runPinnedWriteWithRenamePolicy(params) {
|
|
28950
28895
|
const { targetPath, renameIdentity, ...writeParams } = params;
|
|
@@ -28967,34 +28912,19 @@ async function runPinnedWriteWithRenamePolicy(params) {
|
|
|
28967
28912
|
onRenameIdentityMismatch: "verify-content",
|
|
28968
28913
|
}));
|
|
28969
28914
|
}
|
|
28970
|
-
async function runPinnedCopyHelper(params) {
|
|
28971
|
-
assertSafeBasename(params.basename);
|
|
28972
|
-
validatePinnedOperationPayload({
|
|
28973
|
-
relativeParentPath: params.relativeParentPath,
|
|
28974
|
-
});
|
|
28975
|
-
return await runPinnedPythonOperation({
|
|
28976
|
-
operation: "copy",
|
|
28977
|
-
rootPath: params.rootPath,
|
|
28978
|
-
payload: {
|
|
28979
|
-
basename: params.basename,
|
|
28980
|
-
maxBytes: params.maxBytes ?? -1,
|
|
28981
|
-
mkdir: params.mkdir,
|
|
28982
|
-
mode: params.mode || 0o600,
|
|
28983
|
-
overwrite: params.overwrite !== false,
|
|
28984
|
-
relativeParentPath: params.relativeParentPath,
|
|
28985
|
-
...(params.rootIdentity ? { rootDev: params.rootIdentity.dev, rootIno: params.rootIdentity.ino } : {}),
|
|
28986
|
-
sourceDev: params.sourceIdentity.dev,
|
|
28987
|
-
sourceIno: params.sourceIdentity.ino,
|
|
28988
|
-
sourcePath: params.sourcePath,
|
|
28989
|
-
},
|
|
28990
|
-
});
|
|
28991
|
-
}
|
|
28992
28915
|
async function runPinnedWriteFallback(params) {
|
|
28993
|
-
|
|
28916
|
+
let parentPath = params.relativeParentPath
|
|
28994
28917
|
? external_node_path_.join(params.rootPath, ...params.relativeParentPath.split("/"))
|
|
28995
28918
|
: params.rootPath;
|
|
28996
28919
|
if (params.mkdir) {
|
|
28997
|
-
|
|
28920
|
+
// mkdirPathComponentsWithGuards may resolve the final component through
|
|
28921
|
+
// an in-root symlink (e.g. a skill-bank layout). Use its returned real
|
|
28922
|
+
// path for the subsequent guard and target path so we don't re-check the
|
|
28923
|
+
// original, possibly-symlinked, lexical path and reject it outright.
|
|
28924
|
+
parentPath = await mkdirPathComponentsWithGuards({
|
|
28925
|
+
rootReal: params.rootPath,
|
|
28926
|
+
targetPath: parentPath,
|
|
28927
|
+
});
|
|
28998
28928
|
}
|
|
28999
28929
|
const parentGuard = params.mkdir
|
|
29000
28930
|
? await directory_guard_createAsyncDirectoryGuard(parentPath)
|
|
@@ -29010,8 +28940,9 @@ async function runPinnedWriteFallback(params) {
|
|
|
29010
28940
|
});
|
|
29011
28941
|
let created = true;
|
|
29012
28942
|
try {
|
|
28943
|
+
await handle.chmod(params.mode);
|
|
29013
28944
|
if (params.input.kind === "buffer") {
|
|
29014
|
-
|
|
28945
|
+
pinned_write_assertWithinMaxBytes(pinned_write_byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
29015
28946
|
if (typeof params.input.data === "string") {
|
|
29016
28947
|
await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
|
|
29017
28948
|
}
|
|
@@ -29049,8 +28980,9 @@ async function runPinnedWriteFallback(params) {
|
|
|
29049
28980
|
let renamed = false;
|
|
29050
28981
|
try {
|
|
29051
28982
|
handle = await promises_.open(tempPath, tempFlags, params.mode);
|
|
28983
|
+
await handle.chmod(params.mode);
|
|
29052
28984
|
if (params.input.kind === "buffer") {
|
|
29053
|
-
|
|
28985
|
+
pinned_write_assertWithinMaxBytes(pinned_write_byteLength(params.input.data, params.input.encoding), params.maxBytes);
|
|
29054
28986
|
if (typeof params.input.data === "string") {
|
|
29055
28987
|
await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
|
|
29056
28988
|
}
|
|
@@ -29133,12 +29065,85 @@ async function runPinnedWriteFallback(params) {
|
|
|
29133
29065
|
|
|
29134
29066
|
// EXTERNAL MODULE: external "node:os"
|
|
29135
29067
|
var external_node_os_ = __webpack_require__(8161);
|
|
29068
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path-existing.js
|
|
29069
|
+
|
|
29070
|
+
|
|
29071
|
+
|
|
29072
|
+
|
|
29073
|
+
function isFilesystemRoot(candidate) {
|
|
29074
|
+
return external_node_path_.parse(candidate).root === candidate;
|
|
29075
|
+
}
|
|
29076
|
+
async function root_path_existing_pathExists(targetPath) {
|
|
29077
|
+
try {
|
|
29078
|
+
await promises_.lstat(targetPath);
|
|
29079
|
+
return true;
|
|
29080
|
+
}
|
|
29081
|
+
catch (error) {
|
|
29082
|
+
if (path_isNotFoundPathError(error)) {
|
|
29083
|
+
return false;
|
|
29084
|
+
}
|
|
29085
|
+
throw error;
|
|
29086
|
+
}
|
|
29087
|
+
}
|
|
29088
|
+
async function root_path_existing_resolvePathViaExistingAncestor(targetPath) {
|
|
29089
|
+
const normalized = external_node_path_.resolve(targetPath);
|
|
29090
|
+
let cursor = normalized;
|
|
29091
|
+
const missingSuffix = [];
|
|
29092
|
+
while (!isFilesystemRoot(cursor) && !(await root_path_existing_pathExists(cursor))) {
|
|
29093
|
+
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
29094
|
+
const parent = external_node_path_.dirname(cursor);
|
|
29095
|
+
if (parent === cursor) {
|
|
29096
|
+
break;
|
|
29097
|
+
}
|
|
29098
|
+
cursor = parent;
|
|
29099
|
+
}
|
|
29100
|
+
if (!(await root_path_existing_pathExists(cursor))) {
|
|
29101
|
+
return normalized;
|
|
29102
|
+
}
|
|
29103
|
+
try {
|
|
29104
|
+
const resolvedAncestor = external_node_path_.resolve(await promises_.realpath(cursor));
|
|
29105
|
+
return missingSuffix.length === 0
|
|
29106
|
+
? resolvedAncestor
|
|
29107
|
+
: external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
|
|
29108
|
+
}
|
|
29109
|
+
catch {
|
|
29110
|
+
return normalized;
|
|
29111
|
+
}
|
|
29112
|
+
}
|
|
29113
|
+
function root_path_existing_resolvePathViaExistingAncestorSync(targetPath) {
|
|
29114
|
+
const normalized = path.resolve(targetPath);
|
|
29115
|
+
let cursor = normalized;
|
|
29116
|
+
const missingSuffix = [];
|
|
29117
|
+
while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
|
|
29118
|
+
missingSuffix.unshift(path.basename(cursor));
|
|
29119
|
+
const parent = path.dirname(cursor);
|
|
29120
|
+
if (parent === cursor) {
|
|
29121
|
+
break;
|
|
29122
|
+
}
|
|
29123
|
+
cursor = parent;
|
|
29124
|
+
}
|
|
29125
|
+
if (!fs.existsSync(cursor)) {
|
|
29126
|
+
return normalized;
|
|
29127
|
+
}
|
|
29128
|
+
try {
|
|
29129
|
+
const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
|
|
29130
|
+
return missingSuffix.length === 0
|
|
29131
|
+
? resolvedAncestor
|
|
29132
|
+
: path.resolve(resolvedAncestor, ...missingSuffix);
|
|
29133
|
+
}
|
|
29134
|
+
catch {
|
|
29135
|
+
return normalized;
|
|
29136
|
+
}
|
|
29137
|
+
}
|
|
29138
|
+
|
|
29136
29139
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path.js
|
|
29137
29140
|
|
|
29138
29141
|
|
|
29139
29142
|
|
|
29140
29143
|
|
|
29141
29144
|
|
|
29145
|
+
|
|
29146
|
+
|
|
29142
29147
|
const ROOT_PATH_ALIAS_POLICIES = {
|
|
29143
29148
|
strict: Object.freeze({
|
|
29144
29149
|
allowFinalSymlinkForUnlink: false,
|
|
@@ -29154,7 +29159,7 @@ async function resolveRootPath(params) {
|
|
|
29154
29159
|
const absolutePath = external_node_path_.resolve(params.absolutePath);
|
|
29155
29160
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
29156
29161
|
? external_node_path_.resolve(params.rootCanonicalPath)
|
|
29157
|
-
: await
|
|
29162
|
+
: await root_path_existing_resolvePathViaExistingAncestor(rootPath);
|
|
29158
29163
|
const context = createBoundaryResolutionContext({
|
|
29159
29164
|
resolveParams: params,
|
|
29160
29165
|
rootPath,
|
|
@@ -29216,15 +29221,40 @@ function isPromiseLike(value) {
|
|
|
29216
29221
|
typeof value.then === "function");
|
|
29217
29222
|
}
|
|
29218
29223
|
function createLexicalTraversalState(params) {
|
|
29219
|
-
const
|
|
29224
|
+
const rawAbsolutePath = params.params.absolutePath;
|
|
29225
|
+
const rawRelativePath = rawPathRelativeToRoot(params.rootPath, rawAbsolutePath);
|
|
29226
|
+
const relative = rawRelativePath ?? external_node_path_.relative(params.rootPath, params.absolutePath);
|
|
29220
29227
|
return {
|
|
29221
|
-
segments: relative
|
|
29228
|
+
segments: splitTraversalSegments(relative),
|
|
29222
29229
|
allowFinalSymlink: params.params.policy?.allowFinalSymlinkForUnlink === true,
|
|
29223
29230
|
canonicalCursor: params.rootCanonicalPath,
|
|
29224
29231
|
lexicalCursor: params.rootPath,
|
|
29225
29232
|
preserveFinalSymlink: false,
|
|
29226
29233
|
};
|
|
29227
29234
|
}
|
|
29235
|
+
function splitTraversalSegments(value) {
|
|
29236
|
+
return value
|
|
29237
|
+
.split(process.platform === "win32" ? /[\\/]+/ : /\/+/)
|
|
29238
|
+
.filter((segment) => Boolean(segment) && segment !== ".");
|
|
29239
|
+
}
|
|
29240
|
+
function rawPathRelativeToRoot(rootPath, candidatePath) {
|
|
29241
|
+
if (!external_node_path_.isAbsolute(candidatePath)) {
|
|
29242
|
+
return undefined;
|
|
29243
|
+
}
|
|
29244
|
+
const root = external_node_path_.resolve(rootPath);
|
|
29245
|
+
const candidate = process.platform === "win32"
|
|
29246
|
+
? candidatePath.replaceAll("/", external_node_path_.sep)
|
|
29247
|
+
: candidatePath;
|
|
29248
|
+
if (candidate === root) {
|
|
29249
|
+
return "";
|
|
29250
|
+
}
|
|
29251
|
+
const rootWithSep = root.endsWith(external_node_path_.sep) ? root : `${root}${external_node_path_.sep}`;
|
|
29252
|
+
const candidatePrefix = candidate.slice(0, rootWithSep.length);
|
|
29253
|
+
const prefixMatches = process.platform === "win32"
|
|
29254
|
+
? candidatePrefix.toLowerCase() === rootWithSep.toLowerCase()
|
|
29255
|
+
: candidatePrefix === rootWithSep;
|
|
29256
|
+
return prefixMatches ? candidate.slice(rootWithSep.length) : undefined;
|
|
29257
|
+
}
|
|
29228
29258
|
function assertLexicalCursorInsideBoundary(params) {
|
|
29229
29259
|
assertInsideBoundary({
|
|
29230
29260
|
boundaryLabel: params.params.boundaryLabel,
|
|
@@ -29235,13 +29265,15 @@ function assertLexicalCursorInsideBoundary(params) {
|
|
|
29235
29265
|
}
|
|
29236
29266
|
function applyMissingSuffixToCanonicalCursor(params) {
|
|
29237
29267
|
const missingSuffix = params.state.segments.slice(params.missingFromIndex);
|
|
29238
|
-
|
|
29239
|
-
|
|
29240
|
-
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
|
|
29244
|
-
|
|
29268
|
+
for (const segment of missingSuffix) {
|
|
29269
|
+
advanceCanonicalCursorForSegment({
|
|
29270
|
+
state: params.state,
|
|
29271
|
+
segment,
|
|
29272
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
29273
|
+
params: params.params,
|
|
29274
|
+
absolutePath: params.absolutePath,
|
|
29275
|
+
});
|
|
29276
|
+
}
|
|
29245
29277
|
}
|
|
29246
29278
|
function advanceCanonicalCursorForSegment(params) {
|
|
29247
29279
|
params.state.canonicalCursor = external_node_path_.resolve(params.state.canonicalCursor, params.segment);
|
|
@@ -29357,11 +29389,20 @@ function resolveAndApplySymlinkHop(params) {
|
|
|
29357
29389
|
boundaryLabel: params.boundaryLabel,
|
|
29358
29390
|
});
|
|
29359
29391
|
}
|
|
29392
|
+
function applyParentTraversalStep(params) {
|
|
29393
|
+
params.state.lexicalCursor = external_node_path_.resolve(params.state.lexicalCursor, "..");
|
|
29394
|
+
advanceCanonicalCursorForSegment({
|
|
29395
|
+
state: params.state,
|
|
29396
|
+
segment: "..",
|
|
29397
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
29398
|
+
params: params.resolveParams,
|
|
29399
|
+
absolutePath: params.absolutePath,
|
|
29400
|
+
});
|
|
29401
|
+
}
|
|
29360
29402
|
function* iterateLexicalTraversal(state) {
|
|
29361
29403
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
29362
29404
|
const segment = state.segments[idx] ?? "";
|
|
29363
29405
|
const isLast = idx === state.segments.length - 1;
|
|
29364
|
-
state.lexicalCursor = external_node_path_.join(state.lexicalCursor, segment);
|
|
29365
29406
|
yield { idx, segment, isLast };
|
|
29366
29407
|
}
|
|
29367
29408
|
}
|
|
@@ -29374,6 +29415,14 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
29374
29415
|
absolutePath: params.absolutePath,
|
|
29375
29416
|
};
|
|
29376
29417
|
for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
|
|
29418
|
+
if (segment === "..") {
|
|
29419
|
+
applyParentTraversalStep({
|
|
29420
|
+
...sharedStepParams,
|
|
29421
|
+
resolveParams: params.params,
|
|
29422
|
+
});
|
|
29423
|
+
continue;
|
|
29424
|
+
}
|
|
29425
|
+
state.lexicalCursor = external_node_path_.join(state.lexicalCursor, segment);
|
|
29377
29426
|
const stat = await readLexicalStat({
|
|
29378
29427
|
...sharedStepParams,
|
|
29379
29428
|
missingFromIndex: idx,
|
|
@@ -29401,7 +29450,7 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
29401
29450
|
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor),
|
|
29402
29451
|
});
|
|
29403
29452
|
}
|
|
29404
|
-
const kind = await getPathKind(
|
|
29453
|
+
const kind = await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
|
|
29405
29454
|
return finalizeLexicalResolution({
|
|
29406
29455
|
...params,
|
|
29407
29456
|
state,
|
|
@@ -29413,6 +29462,15 @@ function resolveRootPathLexicalSync(params) {
|
|
|
29413
29462
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
29414
29463
|
const segment = state.segments[idx] ?? "";
|
|
29415
29464
|
const isLast = idx === state.segments.length - 1;
|
|
29465
|
+
if (segment === "..") {
|
|
29466
|
+
applyParentTraversalStep({
|
|
29467
|
+
state,
|
|
29468
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
29469
|
+
resolveParams: params.params,
|
|
29470
|
+
absolutePath: params.absolutePath,
|
|
29471
|
+
});
|
|
29472
|
+
continue;
|
|
29473
|
+
}
|
|
29416
29474
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
29417
29475
|
const maybeStat = readLexicalStat({
|
|
29418
29476
|
state,
|
|
@@ -29454,7 +29512,7 @@ function resolveRootPathLexicalSync(params) {
|
|
|
29454
29512
|
throw new Error("Unexpected async symlink resolution");
|
|
29455
29513
|
}
|
|
29456
29514
|
}
|
|
29457
|
-
const kind = getPathKindSync(
|
|
29515
|
+
const kind = getPathKindSync(state.canonicalCursor, state.preserveFinalSymlink);
|
|
29458
29516
|
return finalizeLexicalResolution({
|
|
29459
29517
|
...params,
|
|
29460
29518
|
state,
|
|
@@ -29523,7 +29581,7 @@ async function resolveOutsideLexicalCanonicalPathAsync(params) {
|
|
|
29523
29581
|
if (path_isPathInside(params.rootPath, params.absolutePath)) {
|
|
29524
29582
|
return undefined;
|
|
29525
29583
|
}
|
|
29526
|
-
return await
|
|
29584
|
+
return await root_path_existing_resolvePathViaExistingAncestor(params.absolutePath);
|
|
29527
29585
|
}
|
|
29528
29586
|
function resolveOutsideLexicalCanonicalPathSync(params) {
|
|
29529
29587
|
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
@@ -29570,60 +29628,6 @@ function buildResolvedRootPath(params) {
|
|
|
29570
29628
|
kind: params.kind.kind,
|
|
29571
29629
|
};
|
|
29572
29630
|
}
|
|
29573
|
-
async function root_path_resolvePathViaExistingAncestor(targetPath) {
|
|
29574
|
-
const normalized = external_node_path_.resolve(targetPath);
|
|
29575
|
-
let cursor = normalized;
|
|
29576
|
-
const missingSuffix = [];
|
|
29577
|
-
while (!isFilesystemRoot(cursor) && !(await root_path_pathExists(cursor))) {
|
|
29578
|
-
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
29579
|
-
const parent = external_node_path_.dirname(cursor);
|
|
29580
|
-
if (parent === cursor) {
|
|
29581
|
-
break;
|
|
29582
|
-
}
|
|
29583
|
-
cursor = parent;
|
|
29584
|
-
}
|
|
29585
|
-
if (!(await root_path_pathExists(cursor))) {
|
|
29586
|
-
return normalized;
|
|
29587
|
-
}
|
|
29588
|
-
try {
|
|
29589
|
-
const resolvedAncestor = external_node_path_.resolve(await promises_.realpath(cursor));
|
|
29590
|
-
if (missingSuffix.length === 0) {
|
|
29591
|
-
return resolvedAncestor;
|
|
29592
|
-
}
|
|
29593
|
-
return external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
|
|
29594
|
-
}
|
|
29595
|
-
catch {
|
|
29596
|
-
return normalized;
|
|
29597
|
-
}
|
|
29598
|
-
}
|
|
29599
|
-
function resolvePathViaExistingAncestorSync(targetPath) {
|
|
29600
|
-
const normalized = path.resolve(targetPath);
|
|
29601
|
-
let cursor = normalized;
|
|
29602
|
-
const missingSuffix = [];
|
|
29603
|
-
while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
|
|
29604
|
-
missingSuffix.unshift(path.basename(cursor));
|
|
29605
|
-
const parent = path.dirname(cursor);
|
|
29606
|
-
if (parent === cursor) {
|
|
29607
|
-
break;
|
|
29608
|
-
}
|
|
29609
|
-
cursor = parent;
|
|
29610
|
-
}
|
|
29611
|
-
if (!fs.existsSync(cursor)) {
|
|
29612
|
-
return normalized;
|
|
29613
|
-
}
|
|
29614
|
-
try {
|
|
29615
|
-
// Keep sync behavior aligned with async (`fsp.realpath`) to avoid
|
|
29616
|
-
// platform-specific canonical alias drift (notably on Windows).
|
|
29617
|
-
const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
|
|
29618
|
-
if (missingSuffix.length === 0) {
|
|
29619
|
-
return resolvedAncestor;
|
|
29620
|
-
}
|
|
29621
|
-
return path.resolve(resolvedAncestor, ...missingSuffix);
|
|
29622
|
-
}
|
|
29623
|
-
catch {
|
|
29624
|
-
return normalized;
|
|
29625
|
-
}
|
|
29626
|
-
}
|
|
29627
29631
|
async function getPathKind(absolutePath, preserveFinalSymlink) {
|
|
29628
29632
|
try {
|
|
29629
29633
|
const stat = preserveFinalSymlink
|
|
@@ -29691,21 +29695,6 @@ function shortPath(value) {
|
|
|
29691
29695
|
}
|
|
29692
29696
|
return value;
|
|
29693
29697
|
}
|
|
29694
|
-
function isFilesystemRoot(candidate) {
|
|
29695
|
-
return external_node_path_.parse(candidate).root === candidate;
|
|
29696
|
-
}
|
|
29697
|
-
async function root_path_pathExists(targetPath) {
|
|
29698
|
-
try {
|
|
29699
|
-
await promises_.lstat(targetPath);
|
|
29700
|
-
return true;
|
|
29701
|
-
}
|
|
29702
|
-
catch (error) {
|
|
29703
|
-
if (path_isNotFoundPathError(error)) {
|
|
29704
|
-
return false;
|
|
29705
|
-
}
|
|
29706
|
-
throw error;
|
|
29707
|
-
}
|
|
29708
|
-
}
|
|
29709
29698
|
async function resolveSymlinkHopPath(symlinkPath) {
|
|
29710
29699
|
try {
|
|
29711
29700
|
return external_node_path_.resolve(await promises_.realpath(symlinkPath));
|
|
@@ -29716,7 +29705,7 @@ async function resolveSymlinkHopPath(symlinkPath) {
|
|
|
29716
29705
|
}
|
|
29717
29706
|
const linkTarget = await promises_.readlink(symlinkPath);
|
|
29718
29707
|
const linkAbsolute = external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget);
|
|
29719
|
-
return
|
|
29708
|
+
return root_path_existing_resolvePathViaExistingAncestor(linkAbsolute);
|
|
29720
29709
|
}
|
|
29721
29710
|
}
|
|
29722
29711
|
function resolveSymlinkHopPathSync(symlinkPath) {
|
|
@@ -29751,7 +29740,7 @@ async function assertNoPathAliasEscape(params) {
|
|
|
29751
29740
|
return;
|
|
29752
29741
|
}
|
|
29753
29742
|
await assertNoHardlinkedFinalPath({
|
|
29754
|
-
filePath: resolved.
|
|
29743
|
+
filePath: resolved.canonicalPath,
|
|
29755
29744
|
root: resolved.rootPath,
|
|
29756
29745
|
boundaryLabel: params.boundaryLabel,
|
|
29757
29746
|
allowFinalHardlinkForUnlink: params.policy?.allowFinalHardlinkForUnlink,
|
|
@@ -29785,8 +29774,80 @@ function path_policy_shortPath(value) {
|
|
|
29785
29774
|
return value;
|
|
29786
29775
|
}
|
|
29787
29776
|
|
|
29788
|
-
|
|
29789
|
-
|
|
29777
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/string-coerce.js
|
|
29778
|
+
function readStringValue(value) {
|
|
29779
|
+
return typeof value === "string" ? value : undefined;
|
|
29780
|
+
}
|
|
29781
|
+
function normalizeNullableString(value) {
|
|
29782
|
+
if (typeof value !== "string") {
|
|
29783
|
+
return null;
|
|
29784
|
+
}
|
|
29785
|
+
const trimmed = value.trim();
|
|
29786
|
+
return trimmed ? trimmed : null;
|
|
29787
|
+
}
|
|
29788
|
+
function normalizeOptionalString(value) {
|
|
29789
|
+
return normalizeNullableString(value) ?? undefined;
|
|
29790
|
+
}
|
|
29791
|
+
function normalizeStringifiedOptionalString(value) {
|
|
29792
|
+
if (typeof value === "string") {
|
|
29793
|
+
return normalizeOptionalString(value);
|
|
29794
|
+
}
|
|
29795
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
29796
|
+
return normalizeOptionalString(String(value));
|
|
29797
|
+
}
|
|
29798
|
+
return undefined;
|
|
29799
|
+
}
|
|
29800
|
+
function normalizeOptionalLowercaseString(value) {
|
|
29801
|
+
return normalizeOptionalString(value)?.toLowerCase();
|
|
29802
|
+
}
|
|
29803
|
+
function normalizeLowercaseStringOrEmpty(value) {
|
|
29804
|
+
return normalizeOptionalLowercaseString(value) ?? "";
|
|
29805
|
+
}
|
|
29806
|
+
function normalizeFastMode(raw) {
|
|
29807
|
+
if (typeof raw === "boolean") {
|
|
29808
|
+
return raw;
|
|
29809
|
+
}
|
|
29810
|
+
if (!raw) {
|
|
29811
|
+
return undefined;
|
|
29812
|
+
}
|
|
29813
|
+
const key = normalizeLowercaseStringOrEmpty(raw);
|
|
29814
|
+
if (["off", "false", "no", "0", "disable", "disabled", "normal"].includes(key)) {
|
|
29815
|
+
return false;
|
|
29816
|
+
}
|
|
29817
|
+
if (["on", "true", "yes", "1", "enable", "enabled", "fast"].includes(key)) {
|
|
29818
|
+
return true;
|
|
29819
|
+
}
|
|
29820
|
+
return undefined;
|
|
29821
|
+
}
|
|
29822
|
+
function lowercasePreservingWhitespace(value) {
|
|
29823
|
+
return value.toLowerCase();
|
|
29824
|
+
}
|
|
29825
|
+
function localeLowercasePreservingWhitespace(value) {
|
|
29826
|
+
return value.toLocaleLowerCase();
|
|
29827
|
+
}
|
|
29828
|
+
function resolvePrimaryStringValue(value) {
|
|
29829
|
+
if (typeof value === "string") {
|
|
29830
|
+
return normalizeOptionalString(value);
|
|
29831
|
+
}
|
|
29832
|
+
if (!value || typeof value !== "object") {
|
|
29833
|
+
return undefined;
|
|
29834
|
+
}
|
|
29835
|
+
return normalizeOptionalString(value.primary);
|
|
29836
|
+
}
|
|
29837
|
+
function normalizeOptionalThreadValue(value) {
|
|
29838
|
+
if (typeof value === "number") {
|
|
29839
|
+
return Number.isFinite(value) ? Math.trunc(value) : undefined;
|
|
29840
|
+
}
|
|
29841
|
+
return normalizeOptionalString(value);
|
|
29842
|
+
}
|
|
29843
|
+
function normalizeOptionalStringifiedId(value) {
|
|
29844
|
+
const normalized = normalizeOptionalThreadValue(value);
|
|
29845
|
+
return normalized == null ? undefined : String(normalized);
|
|
29846
|
+
}
|
|
29847
|
+
function hasNonEmptyString(value) {
|
|
29848
|
+
return normalizeOptionalString(value) !== undefined;
|
|
29849
|
+
}
|
|
29850
|
+
|
|
29790
29851
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/local-file-access.js
|
|
29791
29852
|
|
|
29792
29853
|
|
|
@@ -29918,6 +29979,28 @@ const WINDOWS_RESERVED_DEVICE_NAMES = new Set([
|
|
|
29918
29979
|
"LPT²",
|
|
29919
29980
|
"LPT³",
|
|
29920
29981
|
]);
|
|
29982
|
+
const WINDOWS_SEPARATOR_CHAR_CODE = 0x5c;
|
|
29983
|
+
const WINDOWS_IGNORED_SPACE_CHAR_CODE = 0x20;
|
|
29984
|
+
const WINDOWS_IGNORED_DOT_CHAR_CODE = 0x2e;
|
|
29985
|
+
function trimTrailingWindowsSeparators(value) {
|
|
29986
|
+
let end = value.length;
|
|
29987
|
+
while (end > 0 && value.charCodeAt(end - 1) === WINDOWS_SEPARATOR_CHAR_CODE) {
|
|
29988
|
+
end -= 1;
|
|
29989
|
+
}
|
|
29990
|
+
return end === value.length ? value : value.slice(0, end);
|
|
29991
|
+
}
|
|
29992
|
+
function trimTrailingWindowsIgnoredChars(value) {
|
|
29993
|
+
let end = value.length;
|
|
29994
|
+
while (end > 0) {
|
|
29995
|
+
const charCode = value.charCodeAt(end - 1);
|
|
29996
|
+
if (charCode !== WINDOWS_IGNORED_SPACE_CHAR_CODE &&
|
|
29997
|
+
charCode !== WINDOWS_IGNORED_DOT_CHAR_CODE) {
|
|
29998
|
+
break;
|
|
29999
|
+
}
|
|
30000
|
+
end -= 1;
|
|
30001
|
+
}
|
|
30002
|
+
return end === value.length ? value : value.slice(0, end);
|
|
30003
|
+
}
|
|
29921
30004
|
function candidateReadPaths(filePath) {
|
|
29922
30005
|
if (!filePath.startsWith("file://")) {
|
|
29923
30006
|
return [filePath];
|
|
@@ -29946,10 +30029,10 @@ function matchPosixDeviceReadPath(filePath, cwd) {
|
|
|
29946
30029
|
return undefined;
|
|
29947
30030
|
}
|
|
29948
30031
|
function normalizeWindowsDeviceBaseName(filePath) {
|
|
29949
|
-
const normalized = filePath.replace(/\//g, "\\")
|
|
30032
|
+
const normalized = trimTrailingWindowsSeparators(filePath.replace(/\//g, "\\"));
|
|
29950
30033
|
const lastSegment = normalized.split("\\").filter(Boolean).at(-1) ?? normalized;
|
|
29951
30034
|
const withoutStream = lastSegment.split(":")[0] ?? lastSegment;
|
|
29952
|
-
const withoutTrailingIgnoredChars = withoutStream
|
|
30035
|
+
const withoutTrailingIgnoredChars = trimTrailingWindowsIgnoredChars(withoutStream);
|
|
29953
30036
|
return (withoutTrailingIgnoredChars.split(".")[0] ?? withoutTrailingIgnoredChars).toUpperCase();
|
|
29954
30037
|
}
|
|
29955
30038
|
function matchWindowsDeviceReadPath(filePath) {
|
|
@@ -29984,94 +30067,6 @@ function assertNoUnsafeDeviceReadPath(filePath, options) {
|
|
|
29984
30067
|
}
|
|
29985
30068
|
}
|
|
29986
30069
|
|
|
29987
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
29988
|
-
|
|
29989
|
-
|
|
29990
|
-
const READ_CHUNK_BYTES = 64 * 1024;
|
|
29991
|
-
function assertMaxBytes(maxBytes) {
|
|
29992
|
-
if (maxBytes === Number.POSITIVE_INFINITY) {
|
|
29993
|
-
return;
|
|
29994
|
-
}
|
|
29995
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
29996
|
-
throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
|
|
29997
|
-
}
|
|
29998
|
-
}
|
|
29999
|
-
function createScratchBuffer(maxBytes) {
|
|
30000
|
-
const initialReadBytes = Number.isFinite(maxBytes)
|
|
30001
|
-
? Math.min(READ_CHUNK_BYTES, maxBytes + 1)
|
|
30002
|
-
: READ_CHUNK_BYTES;
|
|
30003
|
-
return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
|
|
30004
|
-
}
|
|
30005
|
-
function nextReadLength(total, maxBytes, capacity) {
|
|
30006
|
-
return Number.isFinite(maxBytes)
|
|
30007
|
-
? Math.min(capacity, maxBytes - total + 1)
|
|
30008
|
-
: capacity;
|
|
30009
|
-
}
|
|
30010
|
-
function appendChunk(params) {
|
|
30011
|
-
const total = params.total + params.bytesRead;
|
|
30012
|
-
if (total > params.maxBytes) {
|
|
30013
|
-
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got at least ${total})`);
|
|
30014
|
-
}
|
|
30015
|
-
params.chunks.push(Buffer.from(params.scratch.subarray(0, params.bytesRead)));
|
|
30016
|
-
return total;
|
|
30017
|
-
}
|
|
30018
|
-
async function readBoundedAsync(maxBytes, readChunk) {
|
|
30019
|
-
assertMaxBytes(maxBytes);
|
|
30020
|
-
const chunks = [];
|
|
30021
|
-
const scratch = createScratchBuffer(maxBytes);
|
|
30022
|
-
let total = 0;
|
|
30023
|
-
while (true) {
|
|
30024
|
-
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
30025
|
-
const bytesRead = await readChunk(scratch, length);
|
|
30026
|
-
if (bytesRead === 0) {
|
|
30027
|
-
return Buffer.concat(chunks, total);
|
|
30028
|
-
}
|
|
30029
|
-
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
30030
|
-
}
|
|
30031
|
-
}
|
|
30032
|
-
/**
|
|
30033
|
-
* Reads from the handle's current offset without closing it. A bounded read
|
|
30034
|
-
* consumes at most maxBytes + 1 bytes so growth after an earlier stat cannot
|
|
30035
|
-
* force an unbounded allocation.
|
|
30036
|
-
*/
|
|
30037
|
-
async function readFileHandleBounded(handle, maxBytes) {
|
|
30038
|
-
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
30039
|
-
return (await handle.read(scratch, 0, length, null)).bytesRead;
|
|
30040
|
-
});
|
|
30041
|
-
}
|
|
30042
|
-
function readDescriptorChunk(fd, scratch, length) {
|
|
30043
|
-
return new Promise((resolve, reject) => {
|
|
30044
|
-
fs.read(fd, scratch, 0, length, null, (error, bytesRead) => {
|
|
30045
|
-
if (error) {
|
|
30046
|
-
reject(error);
|
|
30047
|
-
return;
|
|
30048
|
-
}
|
|
30049
|
-
resolve(bytesRead);
|
|
30050
|
-
});
|
|
30051
|
-
});
|
|
30052
|
-
}
|
|
30053
|
-
/** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
30054
|
-
async function readFileDescriptorBounded(fd, maxBytes) {
|
|
30055
|
-
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
30056
|
-
return await readDescriptorChunk(fd, scratch, length);
|
|
30057
|
-
});
|
|
30058
|
-
}
|
|
30059
|
-
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
30060
|
-
function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
30061
|
-
assertMaxBytes(maxBytes);
|
|
30062
|
-
const chunks = [];
|
|
30063
|
-
const scratch = createScratchBuffer(maxBytes);
|
|
30064
|
-
let total = 0;
|
|
30065
|
-
while (true) {
|
|
30066
|
-
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
30067
|
-
const bytesRead = fs.readSync(fd, scratch, 0, length, null);
|
|
30068
|
-
if (bytesRead === 0) {
|
|
30069
|
-
return Buffer.concat(chunks, total);
|
|
30070
|
-
}
|
|
30071
|
-
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
30072
|
-
}
|
|
30073
|
-
}
|
|
30074
|
-
|
|
30075
30070
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/read-opened-file.js
|
|
30076
30071
|
|
|
30077
30072
|
|
|
@@ -30084,6 +30079,7 @@ async function read_opened_file_readOpenedFileSafely(params) {
|
|
|
30084
30079
|
: await readFileHandleBounded(params.opened.handle, params.maxBytes);
|
|
30085
30080
|
return {
|
|
30086
30081
|
buffer,
|
|
30082
|
+
containment: params.opened.containment,
|
|
30087
30083
|
realPath: params.opened.realPath,
|
|
30088
30084
|
stat: params.opened.stat,
|
|
30089
30085
|
};
|
|
@@ -30227,6 +30223,7 @@ function resolveOsHomeRelativePath(input, opts) {
|
|
|
30227
30223
|
|
|
30228
30224
|
|
|
30229
30225
|
|
|
30226
|
+
|
|
30230
30227
|
const ensureTrailingSep = (value) => value.endsWith(external_node_path_.sep) ? value : value + external_node_path_.sep;
|
|
30231
30228
|
function assertValidRootRelativePath(relativePath) {
|
|
30232
30229
|
path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
@@ -30271,13 +30268,31 @@ async function resolveRootContext(rootDir) {
|
|
|
30271
30268
|
rootWithSep: ensureTrailingSep(rootReal),
|
|
30272
30269
|
};
|
|
30273
30270
|
}
|
|
30274
|
-
async function resolvePathInRoot(root, relativePath) {
|
|
30271
|
+
async function resolvePathInRoot(root, relativePath, options) {
|
|
30275
30272
|
assertValidRootRelativePath(relativePath);
|
|
30276
30273
|
const expanded = await expandRelativePathWithHome(relativePath);
|
|
30277
30274
|
const resolved = external_node_path_.resolve(root.rootWithSep, expanded);
|
|
30278
30275
|
if (!path_isPathInside(root.rootWithSep, resolved)) {
|
|
30279
30276
|
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
30280
30277
|
}
|
|
30278
|
+
const rawAbsolutePath = external_node_path_.isAbsolute(expanded)
|
|
30279
|
+
? expanded
|
|
30280
|
+
: `${root.rootWithSep}${expanded}`;
|
|
30281
|
+
try {
|
|
30282
|
+
await resolveRootPath({
|
|
30283
|
+
absolutePath: rawAbsolutePath,
|
|
30284
|
+
rootPath: root.rootReal,
|
|
30285
|
+
rootCanonicalPath: root.rootReal,
|
|
30286
|
+
boundaryLabel: "root",
|
|
30287
|
+
policy: options?.allowFinalSymlink ? ROOT_PATH_ALIAS_POLICIES.unlinkTarget : undefined,
|
|
30288
|
+
});
|
|
30289
|
+
}
|
|
30290
|
+
catch (error) {
|
|
30291
|
+
const code = options?.aliasErrorCode ?? "outside-workspace";
|
|
30292
|
+
throw new errors_FsSafeError(code, code === "path-alias" ? "path alias escape blocked" : "file is outside workspace root", {
|
|
30293
|
+
cause: error instanceof Error ? error : undefined,
|
|
30294
|
+
});
|
|
30295
|
+
}
|
|
30281
30296
|
return { rootReal: root.rootReal, rootWithSep: root.rootWithSep, resolved };
|
|
30282
30297
|
}
|
|
30283
30298
|
async function resolvePathWithinRoot(params) {
|
|
@@ -30316,14 +30331,143 @@ function stringifyJsonDocument(value, replacer, space) {
|
|
|
30316
30331
|
return text;
|
|
30317
30332
|
}
|
|
30318
30333
|
|
|
30334
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-walk.js
|
|
30335
|
+
|
|
30336
|
+
|
|
30337
|
+
|
|
30338
|
+
function validateBudget(name, value) {
|
|
30339
|
+
if (value === undefined)
|
|
30340
|
+
return Number.POSITIVE_INFINITY;
|
|
30341
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
30342
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
30343
|
+
}
|
|
30344
|
+
return value;
|
|
30345
|
+
}
|
|
30346
|
+
function entryKind(entry) {
|
|
30347
|
+
if (entry.isSymbolicLink)
|
|
30348
|
+
return "symlink";
|
|
30349
|
+
if (entry.isDirectory)
|
|
30350
|
+
return "directory";
|
|
30351
|
+
if (entry.isFile)
|
|
30352
|
+
return "file";
|
|
30353
|
+
return "other";
|
|
30354
|
+
}
|
|
30355
|
+
function limitEntry(relativePath) {
|
|
30356
|
+
return { relativePath, kind: "truncated", size: 0 };
|
|
30357
|
+
}
|
|
30358
|
+
async function* walkRoot(root, relativePath, options) {
|
|
30359
|
+
const maxDepth = validateBudget("maxDepth", options.maxDepth);
|
|
30360
|
+
const maxEntries = validateBudget("maxEntries", options.maxEntries);
|
|
30361
|
+
const visitedDirectories = new Set();
|
|
30362
|
+
let examined = 0;
|
|
30363
|
+
const onLimit = (atPath) => {
|
|
30364
|
+
if ((options.limitBehavior ?? "truncate") === "throw") {
|
|
30365
|
+
throw new errors_FsSafeError("too-large", `root walk budget exceeded at ${atPath || "."}`);
|
|
30366
|
+
}
|
|
30367
|
+
return limitEntry(atPath);
|
|
30368
|
+
};
|
|
30369
|
+
async function* visit(directory, depth) {
|
|
30370
|
+
options.signal?.throwIfAborted();
|
|
30371
|
+
let entries;
|
|
30372
|
+
try {
|
|
30373
|
+
const resolvedDirectory = await resolveRootPath({
|
|
30374
|
+
absolutePath: external_node_path_.resolve(root.rootReal, directory),
|
|
30375
|
+
rootPath: root.rootReal,
|
|
30376
|
+
rootCanonicalPath: root.rootReal,
|
|
30377
|
+
boundaryLabel: "root walk",
|
|
30378
|
+
});
|
|
30379
|
+
if (!resolvedDirectory.exists || resolvedDirectory.kind !== "directory") {
|
|
30380
|
+
throw new errors_FsSafeError("not-file", `root walk path is not a directory: ${directory || "."}`);
|
|
30381
|
+
}
|
|
30382
|
+
if (visitedDirectories.has(resolvedDirectory.canonicalPath)) {
|
|
30383
|
+
return;
|
|
30384
|
+
}
|
|
30385
|
+
visitedDirectories.add(resolvedDirectory.canonicalPath);
|
|
30386
|
+
const listingDirectory = external_node_path_.relative(root.rootReal, resolvedDirectory.canonicalPath)
|
|
30387
|
+
.split(external_node_path_.sep)
|
|
30388
|
+
.join(external_node_path_.posix.sep);
|
|
30389
|
+
entries = await root.list(listingDirectory, { withFileTypes: true });
|
|
30390
|
+
}
|
|
30391
|
+
catch (error) {
|
|
30392
|
+
if ((options.onDirectoryError ?? "throw") === "throw")
|
|
30393
|
+
throw error;
|
|
30394
|
+
yield { relativePath: directory, kind: "directory-error", size: 0, error };
|
|
30395
|
+
return;
|
|
30396
|
+
}
|
|
30397
|
+
for (const entry of entries) {
|
|
30398
|
+
options.signal?.throwIfAborted();
|
|
30399
|
+
const child = directory
|
|
30400
|
+
? external_node_path_.posix.join(directory.split(external_node_path_.sep).join(external_node_path_.posix.sep), entry.name)
|
|
30401
|
+
: entry.name;
|
|
30402
|
+
if (examined >= maxEntries) {
|
|
30403
|
+
yield onLimit(child);
|
|
30404
|
+
return;
|
|
30405
|
+
}
|
|
30406
|
+
examined += 1;
|
|
30407
|
+
let kind = entryKind(entry);
|
|
30408
|
+
let size = entry.size;
|
|
30409
|
+
if (kind === "symlink") {
|
|
30410
|
+
if (options.symlinkPolicy === "skip") {
|
|
30411
|
+
continue;
|
|
30412
|
+
}
|
|
30413
|
+
const resolved = await resolveRootPath({
|
|
30414
|
+
absolutePath: external_node_path_.resolve(root.rootReal, child),
|
|
30415
|
+
rootPath: root.rootReal,
|
|
30416
|
+
rootCanonicalPath: root.rootReal,
|
|
30417
|
+
boundaryLabel: "root walk",
|
|
30418
|
+
});
|
|
30419
|
+
if (!resolved.exists) {
|
|
30420
|
+
continue;
|
|
30421
|
+
}
|
|
30422
|
+
kind = resolved.kind === "directory" ? "directory" : resolved.kind === "file" ? "file" : "other";
|
|
30423
|
+
size = entry.size;
|
|
30424
|
+
}
|
|
30425
|
+
const walkEntry = { relativePath: child, kind, size };
|
|
30426
|
+
const filterResult = options.entryFilter?.(walkEntry) ?? "include";
|
|
30427
|
+
if (!["include", "skip", "skip-subtree"].includes(filterResult)) {
|
|
30428
|
+
throw new TypeError(`invalid root walk entryFilter result: ${String(filterResult)}`);
|
|
30429
|
+
}
|
|
30430
|
+
if (filterResult === "include") {
|
|
30431
|
+
yield walkEntry;
|
|
30432
|
+
}
|
|
30433
|
+
if (kind !== "directory") {
|
|
30434
|
+
continue;
|
|
30435
|
+
}
|
|
30436
|
+
if (filterResult === "skip-subtree") {
|
|
30437
|
+
continue;
|
|
30438
|
+
}
|
|
30439
|
+
if (depth >= maxDepth) {
|
|
30440
|
+
yield onLimit(child);
|
|
30441
|
+
return;
|
|
30442
|
+
}
|
|
30443
|
+
yield* visit(child, depth + 1);
|
|
30444
|
+
}
|
|
30445
|
+
}
|
|
30446
|
+
yield* visit(relativePath, 0);
|
|
30447
|
+
}
|
|
30448
|
+
|
|
30319
30449
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/temp-cleanup.js
|
|
30320
30450
|
|
|
30451
|
+
|
|
30321
30452
|
const tempCleanupEntries = new Map();
|
|
30322
30453
|
let cleanupRegistered = false;
|
|
30454
|
+
function pathStillMatchesReceipt(entry) {
|
|
30455
|
+
if (!entry.identity) {
|
|
30456
|
+
return false;
|
|
30457
|
+
}
|
|
30458
|
+
try {
|
|
30459
|
+
return file_identity_sameFileIdentity(external_node_fs_.lstatSync(entry.path), entry.identity);
|
|
30460
|
+
}
|
|
30461
|
+
catch (error) {
|
|
30462
|
+
return error.code === "ENOENT";
|
|
30463
|
+
}
|
|
30464
|
+
}
|
|
30323
30465
|
function cleanupRegisteredTempPathsSync() {
|
|
30324
30466
|
for (const entry of tempCleanupEntries.values()) {
|
|
30325
30467
|
try {
|
|
30326
|
-
|
|
30468
|
+
if (pathStillMatchesReceipt(entry)) {
|
|
30469
|
+
external_node_fs_.rmSync(entry.path, { force: true, recursive: entry.recursive });
|
|
30470
|
+
}
|
|
30327
30471
|
}
|
|
30328
30472
|
catch {
|
|
30329
30473
|
// Process-exit cleanup is best-effort.
|
|
@@ -30336,13 +30480,27 @@ function registerTempPathForExit(tempPath, options) {
|
|
|
30336
30480
|
cleanupRegistered = true;
|
|
30337
30481
|
process.once("exit", cleanupRegisteredTempPathsSync);
|
|
30338
30482
|
}
|
|
30339
|
-
|
|
30483
|
+
const entry = {
|
|
30340
30484
|
path: tempPath,
|
|
30341
30485
|
recursive: options?.recursive === true,
|
|
30342
|
-
|
|
30343
|
-
|
|
30486
|
+
identity: options?.identity,
|
|
30487
|
+
};
|
|
30488
|
+
if (!entry.identity) {
|
|
30489
|
+
try {
|
|
30490
|
+
entry.identity = external_node_fs_.lstatSync(tempPath);
|
|
30491
|
+
}
|
|
30492
|
+
catch {
|
|
30493
|
+
// Callers that register before creation set the identity after opening.
|
|
30494
|
+
}
|
|
30495
|
+
}
|
|
30496
|
+
tempCleanupEntries.set(tempPath, entry);
|
|
30497
|
+
const unregister = (() => {
|
|
30344
30498
|
tempCleanupEntries.delete(tempPath);
|
|
30499
|
+
});
|
|
30500
|
+
unregister.setIdentity = (identity) => {
|
|
30501
|
+
entry.identity = identity;
|
|
30345
30502
|
};
|
|
30503
|
+
return unregister;
|
|
30346
30504
|
}
|
|
30347
30505
|
function __cleanupRegisteredTempPathsForTest() {
|
|
30348
30506
|
cleanupRegisteredTempPathsSync();
|
|
@@ -30353,7 +30511,9 @@ function __cleanupRegisteredTempPathForTest(tempPath) {
|
|
|
30353
30511
|
return;
|
|
30354
30512
|
}
|
|
30355
30513
|
try {
|
|
30356
|
-
|
|
30514
|
+
if (pathStillMatchesReceipt(entry)) {
|
|
30515
|
+
fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
|
|
30516
|
+
}
|
|
30357
30517
|
}
|
|
30358
30518
|
finally {
|
|
30359
30519
|
tempCleanupEntries.delete(tempPath);
|
|
@@ -30404,9 +30564,6 @@ async function serializePathWrite(key, run) {
|
|
|
30404
30564
|
|
|
30405
30565
|
|
|
30406
30566
|
|
|
30407
|
-
|
|
30408
|
-
|
|
30409
|
-
|
|
30410
30567
|
|
|
30411
30568
|
|
|
30412
30569
|
|
|
@@ -30437,6 +30594,7 @@ const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
|
30437
30594
|
function openResult(params) {
|
|
30438
30595
|
return {
|
|
30439
30596
|
handle: params.handle,
|
|
30597
|
+
containment: "best-effort",
|
|
30440
30598
|
realPath: params.realPath,
|
|
30441
30599
|
stat: params.stat,
|
|
30442
30600
|
[Symbol.asyncDispose]: () => params.handle.close().catch(() => undefined),
|
|
@@ -30555,7 +30713,7 @@ class RootHandle {
|
|
|
30555
30713
|
};
|
|
30556
30714
|
}
|
|
30557
30715
|
async resolve(relativePath) {
|
|
30558
|
-
return (await resolvePathInRoot(this.context, relativePath)).resolved;
|
|
30716
|
+
return (await resolvePathInRoot(this.context, relativePath, { allowFinalSymlink: true })).resolved;
|
|
30559
30717
|
}
|
|
30560
30718
|
async open(relativePath, options = {}) {
|
|
30561
30719
|
return await openFileInRoot(this.context, {
|
|
@@ -30694,58 +30852,34 @@ class RootHandle {
|
|
|
30694
30852
|
}
|
|
30695
30853
|
async stat(relativePath) {
|
|
30696
30854
|
assertValidRootRelativePath(relativePath);
|
|
30697
|
-
|
|
30698
|
-
|
|
30699
|
-
}
|
|
30700
|
-
catch (error) {
|
|
30701
|
-
if (canFallbackFromPythonError(error)) {
|
|
30702
|
-
return await statPathFallback(this.context, relativePath);
|
|
30703
|
-
}
|
|
30704
|
-
throw error;
|
|
30705
|
-
}
|
|
30855
|
+
validatePinnedOperationPayload({ relativePath });
|
|
30856
|
+
return await statPathFallback(this.context, relativePath);
|
|
30706
30857
|
}
|
|
30707
30858
|
async list(relativePath, options = {}) {
|
|
30708
30859
|
assertValidRootRelativePath(relativePath);
|
|
30709
|
-
|
|
30710
|
-
|
|
30711
|
-
? await helperReaddir(this.rootReal, relativePath, true)
|
|
30712
|
-
: await helperReaddir(this.rootReal, relativePath, false);
|
|
30713
|
-
}
|
|
30714
|
-
catch (error) {
|
|
30715
|
-
if (canFallbackFromPythonError(error)) {
|
|
30716
|
-
return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
|
|
30717
|
-
}
|
|
30718
|
-
throw error;
|
|
30719
|
-
}
|
|
30860
|
+
validatePinnedOperationPayload({ relativePath });
|
|
30861
|
+
return await listPathFallback(this.context, relativePath, options.withFileTypes === true);
|
|
30720
30862
|
}
|
|
30721
30863
|
async move(fromRelative, toRelative, options = {}) {
|
|
30722
30864
|
assertValidRootRelativePath(fromRelative);
|
|
30723
30865
|
assertValidRootRelativePath(toRelative);
|
|
30866
|
+
validatePinnedOperationPayload({ from: fromRelative, to: toRelative });
|
|
30724
30867
|
const denyMutations = mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations);
|
|
30725
30868
|
await assertMoveMutationAllowed(this.context, {
|
|
30726
30869
|
fromRelative,
|
|
30727
30870
|
toRelative,
|
|
30728
30871
|
denyMutations,
|
|
30729
30872
|
});
|
|
30730
|
-
|
|
30731
|
-
|
|
30732
|
-
|
|
30733
|
-
|
|
30734
|
-
|
|
30735
|
-
|
|
30736
|
-
|
|
30737
|
-
|
|
30738
|
-
|
|
30739
|
-
|
|
30740
|
-
fromRelative,
|
|
30741
|
-
denyMutations,
|
|
30742
|
-
overwrite: options.overwrite ?? false,
|
|
30743
|
-
toRelative,
|
|
30744
|
-
});
|
|
30745
|
-
return;
|
|
30746
|
-
}
|
|
30747
|
-
throw error;
|
|
30748
|
-
}
|
|
30873
|
+
await movePathFallback(this.context, {
|
|
30874
|
+
fromRelative,
|
|
30875
|
+
denyMutations,
|
|
30876
|
+
overwrite: options.overwrite ?? false,
|
|
30877
|
+
toRelative,
|
|
30878
|
+
});
|
|
30879
|
+
}
|
|
30880
|
+
walk(relativePath, options) {
|
|
30881
|
+
assertValidRootRelativePath(relativePath);
|
|
30882
|
+
return walkRoot(this, relativePath, options);
|
|
30749
30883
|
}
|
|
30750
30884
|
}
|
|
30751
30885
|
function readDefaults(defaults) {
|
|
@@ -30760,7 +30894,9 @@ async function root_impl_root(rootDir, defaults = {}) {
|
|
|
30760
30894
|
return new RootHandle(await resolveRootContext(rootDir), defaults);
|
|
30761
30895
|
}
|
|
30762
30896
|
async function openFileInRoot(root, params) {
|
|
30763
|
-
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath
|
|
30897
|
+
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, {
|
|
30898
|
+
allowFinalSymlink: true,
|
|
30899
|
+
});
|
|
30764
30900
|
let opened;
|
|
30765
30901
|
try {
|
|
30766
30902
|
opened = await openVerifiedLocalFile(resolved, {
|
|
@@ -30831,6 +30967,15 @@ function buildAtomicWriteTempPath(targetPath) {
|
|
|
30831
30967
|
function rootWriteQueueKey(root, relativePath) {
|
|
30832
30968
|
return `${root.rootReal}\0${relativePath}`;
|
|
30833
30969
|
}
|
|
30970
|
+
async function prepareRootWriteTarget(rootReal, targetPath) {
|
|
30971
|
+
const parentPath = await mkdirPathComponentsWithGuards({
|
|
30972
|
+
rootReal,
|
|
30973
|
+
targetPath: external_node_path_.dirname(targetPath),
|
|
30974
|
+
});
|
|
30975
|
+
// Continue through the guarded walk's real parent instead of re-entering
|
|
30976
|
+
// the original path through a symlinked component.
|
|
30977
|
+
return external_node_path_.join(parentPath, external_node_path_.basename(targetPath));
|
|
30978
|
+
}
|
|
30834
30979
|
async function writeTempFileForAtomicReplace(params) {
|
|
30835
30980
|
const tempHandle = await promises_.open(params.tempPath, OPEN_WRITE_CREATE_FLAGS, params.mode);
|
|
30836
30981
|
try {
|
|
@@ -30861,7 +31006,7 @@ async function verifyAtomicWriteResult(params) {
|
|
|
30861
31006
|
}
|
|
30862
31007
|
}
|
|
30863
31008
|
async function openWritableFileInRoot(root, params) {
|
|
30864
|
-
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath);
|
|
31009
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, { aliasErrorCode: "path-alias" });
|
|
30865
31010
|
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
30866
31011
|
try {
|
|
30867
31012
|
await assertNoPathAliasEscape({
|
|
@@ -30873,15 +31018,11 @@ async function openWritableFileInRoot(root, params) {
|
|
|
30873
31018
|
catch (err) {
|
|
30874
31019
|
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
30875
31020
|
}
|
|
30876
|
-
|
|
30877
|
-
|
|
30878
|
-
await
|
|
30879
|
-
await promises_.mkdir(external_node_path_.dirname(resolved), { recursive: true });
|
|
30880
|
-
});
|
|
30881
|
-
}
|
|
30882
|
-
let ioPath = resolved;
|
|
31021
|
+
let ioPath = params.mkdir === false
|
|
31022
|
+
? resolved
|
|
31023
|
+
: await prepareRootWriteTarget(rootReal, resolved);
|
|
30883
31024
|
try {
|
|
30884
|
-
const resolvedRealPath = await promises_.realpath(
|
|
31025
|
+
const resolvedRealPath = await promises_.realpath(ioPath);
|
|
30885
31026
|
if (!path_isPathInside(rootWithSep, resolvedRealPath)) {
|
|
30886
31027
|
throw new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
30887
31028
|
}
|
|
@@ -30966,6 +31107,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
30966
31107
|
}
|
|
30967
31108
|
return {
|
|
30968
31109
|
handle,
|
|
31110
|
+
containment: "best-effort",
|
|
30969
31111
|
createdForWrite,
|
|
30970
31112
|
realPath,
|
|
30971
31113
|
stat,
|
|
@@ -31021,44 +31163,22 @@ async function appendFileInRoot(root, params) {
|
|
|
31021
31163
|
}
|
|
31022
31164
|
}
|
|
31023
31165
|
async function removePathInRoot(root, params) {
|
|
31166
|
+
validatePinnedOperationPayload({ relativePath: params.relativePath });
|
|
31024
31167
|
const resolved = await resolvePinnedRemovePathInRoot(root, params.relativePath, params.denyMutations);
|
|
31025
|
-
if (process.platform === "win32") {
|
|
31026
|
-
await removePathFallback(resolved);
|
|
31027
|
-
return;
|
|
31028
|
-
}
|
|
31029
31168
|
try {
|
|
31030
|
-
await
|
|
31031
|
-
operation: "remove",
|
|
31032
|
-
rootPath: resolved.rootReal,
|
|
31033
|
-
relativePath: resolved.relativePosix,
|
|
31034
|
-
});
|
|
31169
|
+
await removePathFallback(resolved);
|
|
31035
31170
|
}
|
|
31036
31171
|
catch (error) {
|
|
31037
|
-
if (isPinnedPathHelperSpawnError(error)) {
|
|
31038
|
-
await removePathFallback(resolved);
|
|
31039
|
-
return;
|
|
31040
|
-
}
|
|
31041
31172
|
throw normalizePinnedPathError(error);
|
|
31042
31173
|
}
|
|
31043
31174
|
}
|
|
31044
31175
|
async function mkdirPathInRoot(root, params) {
|
|
31176
|
+
validatePinnedOperationPayload({ relativePath: params.relativePath });
|
|
31045
31177
|
const resolved = await resolvePinnedPathInRoot(root, params);
|
|
31046
|
-
if (process.platform === "win32") {
|
|
31047
|
-
await mkdirPathFallback(resolved);
|
|
31048
|
-
return;
|
|
31049
|
-
}
|
|
31050
31178
|
try {
|
|
31051
|
-
await
|
|
31052
|
-
operation: "mkdirp",
|
|
31053
|
-
rootPath: resolved.rootReal,
|
|
31054
|
-
relativePath: resolved.relativePosix,
|
|
31055
|
-
});
|
|
31179
|
+
await mkdirPathFallback(resolved);
|
|
31056
31180
|
}
|
|
31057
31181
|
catch (error) {
|
|
31058
|
-
if (isPinnedPathHelperSpawnError(error)) {
|
|
31059
|
-
await mkdirPathFallback(resolved);
|
|
31060
|
-
return;
|
|
31061
|
-
}
|
|
31062
31182
|
throw normalizePinnedPathError(error);
|
|
31063
31183
|
}
|
|
31064
31184
|
}
|
|
@@ -31124,49 +31244,25 @@ async function copyFileInRoot(root, params) {
|
|
|
31124
31244
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
|
|
31125
31245
|
}
|
|
31126
31246
|
try {
|
|
31127
|
-
if (process.platform === "win32") {
|
|
31128
|
-
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
31129
|
-
await copyFileFallback(root, params, source);
|
|
31130
|
-
});
|
|
31131
|
-
return;
|
|
31132
|
-
}
|
|
31133
31247
|
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
31134
31248
|
await serializePathWrite(pinned.targetPath, async () => {
|
|
31135
|
-
|
|
31249
|
+
await assertCopySourceCurrent(source);
|
|
31250
|
+
const identity = await runPinnedWriteHelper({
|
|
31251
|
+
rootPath: pinned.rootReal,
|
|
31252
|
+
relativeParentPath: pinned.relativeParentPath,
|
|
31253
|
+
basename: pinned.basename,
|
|
31254
|
+
mkdir: params.mkdir !== false,
|
|
31255
|
+
mode: pinned.mode,
|
|
31256
|
+
overwrite: true,
|
|
31257
|
+
maxBytes: params.maxBytes,
|
|
31258
|
+
input: { kind: "stream", stream: source.handle.createReadStream() },
|
|
31259
|
+
});
|
|
31136
31260
|
try {
|
|
31137
|
-
|
|
31138
|
-
await copyFileFallback(root, params, source);
|
|
31139
|
-
return;
|
|
31140
|
-
}
|
|
31141
|
-
identity = await runPinnedCopyHelper({
|
|
31142
|
-
rootPath: pinned.rootReal,
|
|
31143
|
-
relativeParentPath: pinned.relativeParentPath,
|
|
31144
|
-
basename: pinned.basename,
|
|
31145
|
-
mkdir: params.mkdir !== false,
|
|
31146
|
-
mode: pinned.mode,
|
|
31147
|
-
overwrite: true,
|
|
31148
|
-
maxBytes: params.maxBytes,
|
|
31149
|
-
sourcePath: source.realPath,
|
|
31150
|
-
sourceIdentity: { dev: source.stat.dev, ino: source.stat.ino },
|
|
31151
|
-
});
|
|
31261
|
+
await assertCopySourcePathCurrent(source);
|
|
31152
31262
|
}
|
|
31153
31263
|
catch (error) {
|
|
31154
|
-
|
|
31155
|
-
|
|
31156
|
-
return;
|
|
31157
|
-
}
|
|
31158
|
-
throw normalizePinnedWriteError(error);
|
|
31159
|
-
}
|
|
31160
|
-
try {
|
|
31161
|
-
await verifyAtomicWriteResult({
|
|
31162
|
-
root,
|
|
31163
|
-
targetPath: pinned.targetPath,
|
|
31164
|
-
expectedIdentity: identity,
|
|
31165
|
-
});
|
|
31166
|
-
}
|
|
31167
|
-
catch (err) {
|
|
31168
|
-
emitWriteBoundaryWarning(`post-copy verification failed: ${String(err)}`);
|
|
31169
|
-
throw err;
|
|
31264
|
+
await removeCopyTargetIfUnchanged(pinned.targetPath, identity).catch(() => undefined);
|
|
31265
|
+
throw error;
|
|
31170
31266
|
}
|
|
31171
31267
|
});
|
|
31172
31268
|
}
|
|
@@ -31174,8 +31270,33 @@ async function copyFileInRoot(root, params) {
|
|
|
31174
31270
|
await source.handle.close().catch(() => { });
|
|
31175
31271
|
}
|
|
31176
31272
|
}
|
|
31273
|
+
async function assertCopySourceCurrent(source) {
|
|
31274
|
+
const opened = await source.handle.stat();
|
|
31275
|
+
if (!file_identity_sameFileIdentity(opened, source.stat)) {
|
|
31276
|
+
throw new errors_FsSafeError("path-mismatch", "copy source descriptor changed");
|
|
31277
|
+
}
|
|
31278
|
+
await assertCopySourcePathCurrent(source);
|
|
31279
|
+
}
|
|
31280
|
+
async function assertCopySourcePathCurrent(source) {
|
|
31281
|
+
const current = await promises_.lstat(source.realPath);
|
|
31282
|
+
if (current.isSymbolicLink() || !current.isFile() || !file_identity_sameFileIdentity(current, source.stat)) {
|
|
31283
|
+
throw new errors_FsSafeError("path-mismatch", "copy source path changed");
|
|
31284
|
+
}
|
|
31285
|
+
}
|
|
31286
|
+
async function removeCopyTargetIfUnchanged(targetPath, identity) {
|
|
31287
|
+
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(targetPath));
|
|
31288
|
+
const current = await promises_.lstat(targetPath);
|
|
31289
|
+
if (current.isSymbolicLink() || !file_identity_sameFileIdentity(current, identity)) {
|
|
31290
|
+
return;
|
|
31291
|
+
}
|
|
31292
|
+
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
31293
|
+
await promises_.rm(targetPath);
|
|
31294
|
+
});
|
|
31295
|
+
}
|
|
31177
31296
|
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode, denyMutations) {
|
|
31178
|
-
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath
|
|
31297
|
+
const { rootReal, rootWithSep, resolved } = await resolvePathInRoot(root, relativePath, {
|
|
31298
|
+
aliasErrorCode: "path-alias",
|
|
31299
|
+
});
|
|
31179
31300
|
await assertMutationNotDenied(resolved, denyMutations);
|
|
31180
31301
|
try {
|
|
31181
31302
|
await assertNoPathAliasEscape({
|
|
@@ -31277,8 +31398,11 @@ async function resolvePinnedRootPathInRoot(root, params) {
|
|
|
31277
31398
|
const rootReal = root.rootReal;
|
|
31278
31399
|
let resolved;
|
|
31279
31400
|
try {
|
|
31401
|
+
const expandedPath = await expandRelativePathWithHome(params.relativePath);
|
|
31280
31402
|
resolved = await resolveRootPath({
|
|
31281
|
-
absolutePath: external_node_path_.
|
|
31403
|
+
absolutePath: external_node_path_.isAbsolute(expandedPath)
|
|
31404
|
+
? expandedPath
|
|
31405
|
+
: `${ensureTrailingSep(rootReal)}${expandedPath}`,
|
|
31282
31406
|
rootPath: rootReal,
|
|
31283
31407
|
rootCanonicalPath: rootReal,
|
|
31284
31408
|
boundaryLabel: "root",
|
|
@@ -31349,35 +31473,51 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
31349
31473
|
}
|
|
31350
31474
|
}
|
|
31351
31475
|
async function assertMoveMutationAllowed(root, params) {
|
|
31352
|
-
const source = await resolvePathInRoot(root, params.fromRelative
|
|
31476
|
+
const source = await resolvePathInRoot(root, params.fromRelative, {
|
|
31477
|
+
aliasErrorCode: "path-alias",
|
|
31478
|
+
allowFinalSymlink: true,
|
|
31479
|
+
});
|
|
31353
31480
|
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
31354
|
-
const target = await resolvePathInRoot(root, params.toRelative
|
|
31481
|
+
const target = await resolvePathInRoot(root, params.toRelative, {
|
|
31482
|
+
aliasErrorCode: "path-alias",
|
|
31483
|
+
allowFinalSymlink: true,
|
|
31484
|
+
});
|
|
31355
31485
|
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
31356
31486
|
}
|
|
31357
31487
|
async function movePathFallback(root, params) {
|
|
31358
|
-
const source = await resolvePathInRoot(root, params.fromRelative
|
|
31488
|
+
const source = await resolvePathInRoot(root, params.fromRelative, {
|
|
31489
|
+
aliasErrorCode: "path-alias",
|
|
31490
|
+
allowFinalSymlink: true,
|
|
31491
|
+
});
|
|
31359
31492
|
await assertMutationNotDenied(source.resolved, params.denyMutations, { protectAncestors: true });
|
|
31360
31493
|
await resolvePinnedRootPathInRoot(root, {
|
|
31361
31494
|
relativePath: params.fromRelative,
|
|
31362
31495
|
policy: PATH_ALIAS_POLICIES.strict,
|
|
31363
31496
|
});
|
|
31364
|
-
const target = await resolvePathInRoot(root, params.toRelative
|
|
31497
|
+
const target = await resolvePathInRoot(root, params.toRelative, {
|
|
31498
|
+
aliasErrorCode: "path-alias",
|
|
31499
|
+
allowFinalSymlink: true,
|
|
31500
|
+
});
|
|
31365
31501
|
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
31366
31502
|
await resolvePinnedRootPathInRoot(root, {
|
|
31367
31503
|
relativePath: params.toRelative,
|
|
31368
31504
|
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
31369
31505
|
});
|
|
31370
|
-
|
|
31371
|
-
|
|
31372
|
-
|
|
31373
|
-
|
|
31374
|
-
|
|
31375
|
-
|
|
31376
|
-
|
|
31377
|
-
|
|
31378
|
-
|
|
31379
|
-
|
|
31380
|
-
|
|
31506
|
+
const targetStat = await promises_.lstat(target.resolved).catch(() => undefined);
|
|
31507
|
+
const replacesFinalSymlink = process.platform !== "win32" && params.overwrite && targetStat?.isSymbolicLink() === true;
|
|
31508
|
+
if (!replacesFinalSymlink) {
|
|
31509
|
+
try {
|
|
31510
|
+
await assertNoPathAliasEscape({
|
|
31511
|
+
absolutePath: target.resolved,
|
|
31512
|
+
rootPath: target.rootReal,
|
|
31513
|
+
boundaryLabel: "root",
|
|
31514
|
+
});
|
|
31515
|
+
}
|
|
31516
|
+
catch (error) {
|
|
31517
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", {
|
|
31518
|
+
cause: error instanceof Error ? error : undefined,
|
|
31519
|
+
});
|
|
31520
|
+
}
|
|
31381
31521
|
}
|
|
31382
31522
|
let sourceStat;
|
|
31383
31523
|
try {
|
|
@@ -31464,6 +31604,7 @@ async function writeFileFallback(root, params) {
|
|
|
31464
31604
|
encoding: params.encoding,
|
|
31465
31605
|
mode: mode || 0o600,
|
|
31466
31606
|
});
|
|
31607
|
+
unregisterTempPath.setIdentity(writtenStat);
|
|
31467
31608
|
const commitTempPath = tempPath;
|
|
31468
31609
|
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
31469
31610
|
await promises_.rename(commitTempPath, destinationPath);
|
|
@@ -31491,7 +31632,9 @@ async function writeFileFallback(root, params) {
|
|
|
31491
31632
|
}
|
|
31492
31633
|
}
|
|
31493
31634
|
async function writeMissingFileFallback(root, params) {
|
|
31494
|
-
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath
|
|
31635
|
+
const { rootReal, resolved } = await resolvePathInRoot(root, params.relativePath, {
|
|
31636
|
+
aliasErrorCode: "path-alias",
|
|
31637
|
+
});
|
|
31495
31638
|
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
31496
31639
|
try {
|
|
31497
31640
|
await assertNoPathAliasEscape({
|
|
@@ -31503,14 +31646,14 @@ async function writeMissingFileFallback(root, params) {
|
|
|
31503
31646
|
catch (err) {
|
|
31504
31647
|
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
31505
31648
|
}
|
|
31506
|
-
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(
|
|
31649
|
+
const targetPath = params.mkdir === false
|
|
31650
|
+
? resolved
|
|
31651
|
+
: await prepareRootWriteTarget(rootReal, resolved);
|
|
31652
|
+
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(targetPath));
|
|
31510
31653
|
let created = false;
|
|
31511
31654
|
try {
|
|
31512
31655
|
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
31513
|
-
const handle = await promises_.open(
|
|
31656
|
+
const handle = await promises_.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
31514
31657
|
created = true;
|
|
31515
31658
|
try {
|
|
31516
31659
|
if (typeof params.data === "string") {
|
|
@@ -31534,7 +31677,7 @@ async function writeMissingFileFallback(root, params) {
|
|
|
31534
31677
|
await handle.close();
|
|
31535
31678
|
await verifyAtomicWriteResult({
|
|
31536
31679
|
root,
|
|
31537
|
-
targetPath
|
|
31680
|
+
targetPath,
|
|
31538
31681
|
expectedIdentity: writtenStat,
|
|
31539
31682
|
});
|
|
31540
31683
|
created = false;
|
|
@@ -31549,87 +31692,102 @@ async function writeMissingFileFallback(root, params) {
|
|
|
31549
31692
|
}
|
|
31550
31693
|
finally {
|
|
31551
31694
|
if (created) {
|
|
31552
|
-
await promises_.rm(
|
|
31695
|
+
await promises_.rm(targetPath, { force: true }).catch(() => undefined);
|
|
31553
31696
|
}
|
|
31554
31697
|
}
|
|
31555
31698
|
}
|
|
31556
|
-
|
|
31557
|
-
|
|
31558
|
-
|
|
31559
|
-
|
|
31560
|
-
|
|
31561
|
-
|
|
31562
|
-
|
|
31563
|
-
|
|
31564
|
-
|
|
31565
|
-
|
|
31566
|
-
|
|
31567
|
-
|
|
31568
|
-
|
|
31569
|
-
|
|
31570
|
-
|
|
31571
|
-
});
|
|
31572
|
-
const destinationPath = target.realPath;
|
|
31573
|
-
const mode = params.mode ?? (target.stat.mode & 0o777);
|
|
31574
|
-
await target.handle.close().catch(() => { });
|
|
31575
|
-
targetClosedByUs = true;
|
|
31576
|
-
const destinationGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(destinationPath));
|
|
31577
|
-
tempPath = buildAtomicWriteTempPath(destinationPath);
|
|
31578
|
-
unregisterTempPath = registerTempPathForExit(tempPath);
|
|
31579
|
-
tempHandle = await promises_.open(tempPath, OPEN_WRITE_CREATE_FLAGS, mode || 0o600);
|
|
31580
|
-
const sourceStream = createBoundedReadStream(source, params.maxBytes);
|
|
31581
|
-
const targetStream = tempHandle.createWriteStream();
|
|
31582
|
-
sourceStream.once("close", () => {
|
|
31583
|
-
sourceClosedByStream = true;
|
|
31584
|
-
});
|
|
31585
|
-
targetStream.once("close", () => {
|
|
31586
|
-
tempClosedByStream = true;
|
|
31587
|
-
});
|
|
31588
|
-
await (0,external_node_stream_promises_.pipeline)(sourceStream, targetStream);
|
|
31589
|
-
const writtenStat = await promises_.stat(tempPath);
|
|
31590
|
-
if (!tempClosedByStream) {
|
|
31591
|
-
await tempHandle.close().catch(() => { });
|
|
31592
|
-
tempClosedByStream = true;
|
|
31593
|
-
}
|
|
31594
|
-
tempHandle = null;
|
|
31595
|
-
const commitTempPath = tempPath;
|
|
31596
|
-
await withAsyncDirectoryGuards([destinationGuard], async () => {
|
|
31597
|
-
await promises_.rename(commitTempPath, destinationPath);
|
|
31598
|
-
});
|
|
31599
|
-
tempPath = null;
|
|
31600
|
-
unregisterTempPath();
|
|
31601
|
-
unregisterTempPath = null;
|
|
31602
|
-
try {
|
|
31603
|
-
await verifyAtomicWriteResult({
|
|
31604
|
-
root,
|
|
31605
|
-
targetPath: destinationPath,
|
|
31606
|
-
expectedIdentity: writtenStat,
|
|
31607
|
-
});
|
|
31699
|
+
|
|
31700
|
+
;// CONCATENATED MODULE: ../core/src/gateways/read-file-no-follow.ts
|
|
31701
|
+
/**
|
|
31702
|
+
* Atomic file read with symlink and size protection.
|
|
31703
|
+
*/
|
|
31704
|
+
|
|
31705
|
+
function isEloopError(error) {
|
|
31706
|
+
return error instanceof Error && "code" in error && error.code === "ELOOP";
|
|
31707
|
+
}
|
|
31708
|
+
async function openWithoutFollowingSymlinks(filePath, safePath) {
|
|
31709
|
+
const hasNoFollow = typeof external_node_fs_.constants.O_NOFOLLOW === "number" && external_node_fs_.constants.O_NOFOLLOW !== 0;
|
|
31710
|
+
if (!hasNoFollow) {
|
|
31711
|
+
const stats = await (0,promises_.lstat)(filePath);
|
|
31712
|
+
if (stats.isSymbolicLink()) {
|
|
31713
|
+
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
31608
31714
|
}
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
31715
|
+
return (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY);
|
|
31716
|
+
}
|
|
31717
|
+
try {
|
|
31718
|
+
return await (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY | external_node_fs_.constants.O_NOFOLLOW);
|
|
31719
|
+
} catch (error) {
|
|
31720
|
+
if (isEloopError(error)) {
|
|
31721
|
+
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
31612
31722
|
}
|
|
31723
|
+
throw error;
|
|
31613
31724
|
}
|
|
31614
|
-
|
|
31615
|
-
|
|
31616
|
-
|
|
31725
|
+
}
|
|
31726
|
+
/**
|
|
31727
|
+
* Reads a file atomically with symlink and size protection.
|
|
31728
|
+
*
|
|
31729
|
+
* Uses `O_NOFOLLOW` (where available) to atomically reject symlinks at
|
|
31730
|
+
* the kernel level, eliminating the TOCTOU window between `lstat()` and
|
|
31731
|
+
* `readFile()`. Falls back to `lstat()` on platforms without `O_NOFOLLOW`.
|
|
31732
|
+
* Validates file size via `fstat()` on the opened file descriptor so the
|
|
31733
|
+
* size check and the read operate on the same inode.
|
|
31734
|
+
*/ async function read_file_no_follow_readFileNoFollow(filePath, maxBytes) {
|
|
31735
|
+
const safePath = JSON.stringify(filePath);
|
|
31736
|
+
const fh = await openWithoutFollowingSymlinks(filePath, safePath);
|
|
31737
|
+
try {
|
|
31738
|
+
const fdStats = await fh.stat();
|
|
31739
|
+
if (fdStats.size > maxBytes) {
|
|
31740
|
+
throw new Error(`File ${safePath} exceeds size limit (${fdStats.size} bytes > ${maxBytes} bytes)`);
|
|
31617
31741
|
}
|
|
31618
|
-
|
|
31742
|
+
return await fh.readFile("utf-8");
|
|
31743
|
+
} finally{
|
|
31744
|
+
await fh.close();
|
|
31619
31745
|
}
|
|
31620
|
-
|
|
31621
|
-
|
|
31622
|
-
|
|
31746
|
+
}
|
|
31747
|
+
|
|
31748
|
+
;// CONCATENATED MODULE: ../core/src/gateways/symlink-path-guard.ts
|
|
31749
|
+
/**
|
|
31750
|
+
* Path-segment symlink rejection helpers for root-bounded FS ops.
|
|
31751
|
+
*/
|
|
31752
|
+
|
|
31753
|
+
|
|
31754
|
+
function isEnoentError(error) {
|
|
31755
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
31756
|
+
}
|
|
31757
|
+
function symlinkNotAllowedError(relativePath) {
|
|
31758
|
+
return new Error(`Symlinks are not allowed: path contains symbolic link: ${JSON.stringify(relativePath)}`, {
|
|
31759
|
+
cause: new errors_FsSafeError("symlink", "symlink not allowed")
|
|
31760
|
+
});
|
|
31761
|
+
}
|
|
31762
|
+
async function lstatIfExists(path) {
|
|
31763
|
+
try {
|
|
31764
|
+
return await (0,promises_.lstat)(path);
|
|
31765
|
+
} catch (error) {
|
|
31766
|
+
if (isEnoentError(error)) {
|
|
31767
|
+
return undefined;
|
|
31623
31768
|
}
|
|
31624
|
-
|
|
31625
|
-
|
|
31769
|
+
throw error;
|
|
31770
|
+
}
|
|
31771
|
+
}
|
|
31772
|
+
/**
|
|
31773
|
+
* Walk path segments under rootPath and reject any symbolic-link component.
|
|
31774
|
+
* Stops at the first missing segment (ENOENT).
|
|
31775
|
+
*/ async function rejectSymlinkSegments(rootPath, relativeFromRoot, onSymlink) {
|
|
31776
|
+
if (!relativeFromRoot) {
|
|
31777
|
+
return;
|
|
31778
|
+
}
|
|
31779
|
+
let currentPath = rootPath;
|
|
31780
|
+
for (const segment of relativeFromRoot.split(external_node_path_.sep)){
|
|
31781
|
+
if (!segment || segment === ".") {
|
|
31782
|
+
continue;
|
|
31626
31783
|
}
|
|
31627
|
-
|
|
31628
|
-
|
|
31784
|
+
currentPath = (0,external_node_path_.join)(currentPath, segment);
|
|
31785
|
+
const stats = await lstatIfExists(currentPath);
|
|
31786
|
+
if (stats === undefined) {
|
|
31787
|
+
return;
|
|
31629
31788
|
}
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
await target.handle.close().catch(() => { });
|
|
31789
|
+
if (stats.isSymbolicLink()) {
|
|
31790
|
+
throw onSymlink(currentPath);
|
|
31633
31791
|
}
|
|
31634
31792
|
}
|
|
31635
31793
|
}
|
|
@@ -31641,6 +31799,8 @@ async function copyFileFallback(root, params, source) {
|
|
|
31641
31799
|
|
|
31642
31800
|
|
|
31643
31801
|
|
|
31802
|
+
|
|
31803
|
+
|
|
31644
31804
|
/**
|
|
31645
31805
|
* Checks if a file or directory exists.
|
|
31646
31806
|
*/ async function file_system_utils_fileExists(path) {
|
|
@@ -31652,28 +31812,34 @@ async function copyFileFallback(root, params, source) {
|
|
|
31652
31812
|
}
|
|
31653
31813
|
}
|
|
31654
31814
|
function isPathWithinRoot(rootPath, candidatePath) {
|
|
31655
|
-
|
|
31815
|
+
if (candidatePath === rootPath) {
|
|
31816
|
+
return true;
|
|
31817
|
+
}
|
|
31818
|
+
// When root is `/` (or `C:\`), appending sep would yield `//` / `C:\\` and
|
|
31819
|
+
// incorrectly reject every in-root path. Use root as the prefix when it
|
|
31820
|
+
// already ends with the separator.
|
|
31821
|
+
const prefix = rootPath.endsWith(external_node_path_.sep) ? rootPath : `${rootPath}${external_node_path_.sep}`;
|
|
31822
|
+
return candidatePath.startsWith(prefix);
|
|
31656
31823
|
}
|
|
31657
31824
|
function mapFsSafeError(error, relativePath) {
|
|
31658
|
-
if (error instanceof errors_FsSafeError) {
|
|
31659
|
-
|
|
31660
|
-
|
|
31661
|
-
|
|
31662
|
-
|
|
31663
|
-
|
|
31664
|
-
|
|
31665
|
-
|
|
31666
|
-
|
|
31667
|
-
|
|
31668
|
-
|
|
31669
|
-
|
|
31670
|
-
|
|
31671
|
-
|
|
31672
|
-
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
|
|
31676
|
-
}
|
|
31825
|
+
if (!(error instanceof errors_FsSafeError)) {
|
|
31826
|
+
throw error;
|
|
31827
|
+
}
|
|
31828
|
+
const safePath = JSON.stringify(relativePath);
|
|
31829
|
+
if (error.code === "outside-workspace" || error.code === "invalid-path") {
|
|
31830
|
+
throw new Error(`Resolved path is outside target directory: ${safePath}`, {
|
|
31831
|
+
cause: error
|
|
31832
|
+
});
|
|
31833
|
+
}
|
|
31834
|
+
if (error.code === "path-alias" || error.code === "symlink") {
|
|
31835
|
+
throw new Error(`Symlinks are not allowed: path contains symbolic link: ${safePath}`, {
|
|
31836
|
+
cause: error
|
|
31837
|
+
});
|
|
31838
|
+
}
|
|
31839
|
+
if (error.code === "too-large") {
|
|
31840
|
+
throw new Error(`File ${safePath} exceeds size limit: ${error.message}`, {
|
|
31841
|
+
cause: error
|
|
31842
|
+
});
|
|
31677
31843
|
}
|
|
31678
31844
|
throw error;
|
|
31679
31845
|
}
|
|
@@ -31684,8 +31850,20 @@ async function createSafeRoot(targetDir, maxBytes) {
|
|
|
31684
31850
|
symlinks: "reject"
|
|
31685
31851
|
});
|
|
31686
31852
|
}
|
|
31853
|
+
/**
|
|
31854
|
+
* fs-safe 0.5 exists/stat/list canonicalize through in-root symlinks; open/read
|
|
31855
|
+
* still honor symlinks:"reject". Enforce reject on every root-bounded op.
|
|
31856
|
+
*/ async function assertNoSymlinksWithinRoot(targetDir, relativePath) {
|
|
31857
|
+
if (!relativePath) {
|
|
31858
|
+
return;
|
|
31859
|
+
}
|
|
31860
|
+
const absolutePath = await file_system_utils_resolvePathWithinRoot(targetDir, relativePath);
|
|
31861
|
+
const rootPath = await (0,promises_.realpath)(targetDir);
|
|
31862
|
+
await rejectSymlinkSegments(rootPath, (0,external_node_path_.relative)(rootPath, absolutePath), ()=>symlinkNotAllowedError(relativePath));
|
|
31863
|
+
}
|
|
31687
31864
|
async function file_system_utils_readTextWithinRoot(targetDir, relativePath, maxBytes) {
|
|
31688
31865
|
try {
|
|
31866
|
+
await assertNoSymlinksWithinRoot(targetDir, relativePath);
|
|
31689
31867
|
const safeRoot = await createSafeRoot(targetDir, maxBytes);
|
|
31690
31868
|
return await safeRoot.readText(relativePath, {
|
|
31691
31869
|
maxBytes
|
|
@@ -31696,6 +31874,7 @@ async function file_system_utils_readTextWithinRoot(targetDir, relativePath, max
|
|
|
31696
31874
|
}
|
|
31697
31875
|
async function file_system_utils_listDirectoryWithinRoot(targetDir, relativePath) {
|
|
31698
31876
|
try {
|
|
31877
|
+
await assertNoSymlinksWithinRoot(targetDir, relativePath);
|
|
31699
31878
|
const safeRoot = await createSafeRoot(targetDir);
|
|
31700
31879
|
const entries = await safeRoot.list(relativePath, {
|
|
31701
31880
|
withFileTypes: true
|
|
@@ -31715,6 +31894,7 @@ function toSafeDirEntry(entry) {
|
|
|
31715
31894
|
}
|
|
31716
31895
|
async function file_system_utils_pathExistsWithinRoot(targetDir, relativePath) {
|
|
31717
31896
|
try {
|
|
31897
|
+
await assertNoSymlinksWithinRoot(targetDir, relativePath);
|
|
31718
31898
|
const safeRoot = await createSafeRoot(targetDir);
|
|
31719
31899
|
return await safeRoot.exists(relativePath);
|
|
31720
31900
|
} catch (error) {
|
|
@@ -31731,6 +31911,7 @@ async function file_system_utils_pathExistsWithinRoot(targetDir, relativePath) {
|
|
|
31731
31911
|
}
|
|
31732
31912
|
async function file_system_utils_statWithinRoot(targetDir, relativePath) {
|
|
31733
31913
|
try {
|
|
31914
|
+
await assertNoSymlinksWithinRoot(targetDir, relativePath);
|
|
31734
31915
|
const safeRoot = await createSafeRoot(targetDir);
|
|
31735
31916
|
return await safeRoot.stat(relativePath);
|
|
31736
31917
|
} catch (error) {
|
|
@@ -31757,26 +31938,7 @@ async function file_system_utils_statWithinRoot(targetDir, relativePath) {
|
|
|
31757
31938
|
if (!isPathWithinRoot(rootPath, absolutePath)) {
|
|
31758
31939
|
throw new Error(`Resolved path is outside target directory: ${absolutePath}`);
|
|
31759
31940
|
}
|
|
31760
|
-
|
|
31761
|
-
if (!relativePath || relativePath === ".") {
|
|
31762
|
-
return;
|
|
31763
|
-
}
|
|
31764
|
-
const segments = relativePath.split(external_node_path_.sep);
|
|
31765
|
-
let currentPath = rootPath;
|
|
31766
|
-
for (const segment of segments){
|
|
31767
|
-
currentPath = (0,external_node_path_.join)(currentPath, segment);
|
|
31768
|
-
try {
|
|
31769
|
-
const stats = await (0,promises_.lstat)(currentPath);
|
|
31770
|
-
if (stats.isSymbolicLink()) {
|
|
31771
|
-
throw new Error(`Path contains symbolic link: ${currentPath}`);
|
|
31772
|
-
}
|
|
31773
|
-
} catch (error) {
|
|
31774
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
31775
|
-
return;
|
|
31776
|
-
}
|
|
31777
|
-
throw error;
|
|
31778
|
-
}
|
|
31779
|
-
}
|
|
31941
|
+
await rejectSymlinkSegments(rootPath, (0,external_node_path_.relative)(rootPath, absolutePath), (segmentPath)=>new Error(`Path contains symbolic link: ${segmentPath}`));
|
|
31780
31942
|
}
|
|
31781
31943
|
/**
|
|
31782
31944
|
* Resolves a relative path under targetDir and validates it does not pass through symlinks.
|
|
@@ -31793,44 +31955,6 @@ async function file_system_utils_statWithinRoot(targetDir, relativePath) {
|
|
|
31793
31955
|
throw new Error(`${context} exceeds size limit (${fileStats.size} bytes > ${maxBytes} bytes)`);
|
|
31794
31956
|
}
|
|
31795
31957
|
}
|
|
31796
|
-
/**
|
|
31797
|
-
* Reads a file atomically with symlink and size protection.
|
|
31798
|
-
*
|
|
31799
|
-
* Uses `O_NOFOLLOW` (where available) to atomically reject symlinks at
|
|
31800
|
-
* the kernel level, eliminating the TOCTOU window between `lstat()` and
|
|
31801
|
-
* `readFile()`. Falls back to `lstat()` on platforms without `O_NOFOLLOW`.
|
|
31802
|
-
* Validates file size via `fstat()` on the opened file descriptor so the
|
|
31803
|
-
* size check and the read operate on the same inode.
|
|
31804
|
-
*/ async function file_system_utils_readFileNoFollow(filePath, maxBytes) {
|
|
31805
|
-
const hasNoFollow = typeof external_node_fs_.constants.O_NOFOLLOW === "number" && external_node_fs_.constants.O_NOFOLLOW !== 0;
|
|
31806
|
-
const safePath = JSON.stringify(filePath);
|
|
31807
|
-
let fh;
|
|
31808
|
-
if (hasNoFollow) {
|
|
31809
|
-
try {
|
|
31810
|
-
fh = await (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY | external_node_fs_.constants.O_NOFOLLOW);
|
|
31811
|
-
} catch (error) {
|
|
31812
|
-
if (error instanceof Error && "code" in error && error.code === "ELOOP") {
|
|
31813
|
-
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
31814
|
-
}
|
|
31815
|
-
throw error;
|
|
31816
|
-
}
|
|
31817
|
-
} else {
|
|
31818
|
-
const stats = await (0,promises_.lstat)(filePath);
|
|
31819
|
-
if (stats.isSymbolicLink()) {
|
|
31820
|
-
throw new Error(`Symlinks are not allowed: ${safePath}`);
|
|
31821
|
-
}
|
|
31822
|
-
fh = await (0,promises_.open)(filePath, external_node_fs_.constants.O_RDONLY);
|
|
31823
|
-
}
|
|
31824
|
-
try {
|
|
31825
|
-
const fdStats = await fh.stat();
|
|
31826
|
-
if (fdStats.size > maxBytes) {
|
|
31827
|
-
throw new Error(`File ${safePath} exceeds size limit (${fdStats.size} bytes > ${maxBytes} bytes)`);
|
|
31828
|
-
}
|
|
31829
|
-
return await fh.readFile("utf-8");
|
|
31830
|
-
} finally{
|
|
31831
|
-
await fh.close();
|
|
31832
|
-
}
|
|
31833
|
-
}
|
|
31834
31958
|
|
|
31835
31959
|
;// CONCATENATED MODULE: ../core/src/gateways/agent-file-gateway.ts
|
|
31836
31960
|
/**
|
|
@@ -33199,8 +33323,6 @@ function _parseInput(input) {
|
|
|
33199
33323
|
}
|
|
33200
33324
|
|
|
33201
33325
|
|
|
33202
|
-
// EXTERNAL MODULE: external "node:module"
|
|
33203
|
-
var external_node_module_ = __webpack_require__(8995);
|
|
33204
33326
|
// EXTERNAL MODULE: ../../node_modules/jiti/dist/jiti.cjs
|
|
33205
33327
|
var jiti = __webpack_require__(3273);
|
|
33206
33328
|
;// CONCATENATED MODULE: ../../node_modules/jiti/lib/jiti.mjs
|
|
@@ -34845,8 +34967,9 @@ async function readVersionFileContent(targetDir, relativePath) {
|
|
|
34845
34967
|
continue;
|
|
34846
34968
|
}
|
|
34847
34969
|
if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
|
|
34848
|
-
const
|
|
34849
|
-
const
|
|
34970
|
+
const { lockfile } = pmConfig;
|
|
34971
|
+
const lockfilePath = lockfile ? await file_system_utils_resolveSafePath(targetDir, lockfile) : undefined;
|
|
34972
|
+
const hasLockfile = lockfilePath && lockfile ? await file_system_utils_pathExistsWithinRoot(targetDir, lockfile) : false;
|
|
34850
34973
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
34851
34974
|
continue;
|
|
34852
34975
|
}
|
|
@@ -34863,8 +34986,9 @@ async function readVersionFileContent(targetDir, relativePath) {
|
|
|
34863
34986
|
const packageManagers = [];
|
|
34864
34987
|
for (const pmConfig of otherPackageManagers){
|
|
34865
34988
|
if (await file_system_utils_pathExistsWithinRoot(targetDir, pmConfig.manifestFile)) {
|
|
34866
|
-
const
|
|
34867
|
-
const
|
|
34989
|
+
const { lockfile } = pmConfig;
|
|
34990
|
+
const lockfilePath = lockfile ? await file_system_utils_resolveSafePath(targetDir, lockfile) : undefined;
|
|
34991
|
+
const hasLockfile = lockfilePath && lockfile ? await file_system_utils_pathExistsWithinRoot(targetDir, lockfile) : false;
|
|
34868
34992
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
34869
34993
|
continue;
|
|
34870
34994
|
}
|
|
@@ -34885,6 +35009,8 @@ async function readVersionFileContent(targetDir, relativePath) {
|
|
|
34885
35009
|
return new FileSystemEnvironmentGateway(cwd);
|
|
34886
35010
|
}
|
|
34887
35011
|
|
|
35012
|
+
// EXTERNAL MODULE: external "node:child_process"
|
|
35013
|
+
var external_node_child_process_ = __webpack_require__(1421);
|
|
34888
35014
|
;// CONCATENATED MODULE: ../core/src/gateways/github-ruleset-gateway.ts
|
|
34889
35015
|
/**
|
|
34890
35016
|
* Gateway for interacting with GitHub repository rulesets via Octokit.
|
|
@@ -35024,11 +35150,18 @@ function defaultExec(command, args, options) {
|
|
|
35024
35150
|
throw new Error("Not authenticated");
|
|
35025
35151
|
}
|
|
35026
35152
|
try {
|
|
35027
|
-
|
|
35153
|
+
const params = {
|
|
35028
35154
|
owner,
|
|
35029
35155
|
repo,
|
|
35030
|
-
|
|
35031
|
-
|
|
35156
|
+
name: payload.name,
|
|
35157
|
+
enforcement: payload.enforcement,
|
|
35158
|
+
target: payload.target,
|
|
35159
|
+
// biome-ignore lint/style/useNamingConvention: GitHub API schema requires snake_case
|
|
35160
|
+
bypass_actors: payload.bypass_actors,
|
|
35161
|
+
conditions: payload.conditions,
|
|
35162
|
+
rules: payload.rules
|
|
35163
|
+
};
|
|
35164
|
+
await this.octokit.rest.repos.createRepoRuleset(params);
|
|
35032
35165
|
} catch (error) {
|
|
35033
35166
|
const details = formatOctokitError(error);
|
|
35034
35167
|
throw new Error(`Failed to create ruleset for ${owner}/${repo}: ${details}`);
|
|
@@ -37364,7 +37497,7 @@ const PackageJsonSchema = schemas_object({
|
|
|
37364
37497
|
const packageJsonPath = (0,external_node_path_.join)(targetDir, "package.json");
|
|
37365
37498
|
let content;
|
|
37366
37499
|
try {
|
|
37367
|
-
content = await
|
|
37500
|
+
content = await read_file_no_follow_readFileNoFollow(packageJsonPath, MAX_PACKAGE_JSON_BYTES);
|
|
37368
37501
|
} catch (error) {
|
|
37369
37502
|
const code = error instanceof Error && "code" in error ? error.code : undefined;
|
|
37370
37503
|
if (code !== "ENOENT") {
|
|
@@ -57350,120 +57483,6 @@ function lib_isUint8Array(value) {
|
|
|
57350
57483
|
*/
|
|
57351
57484
|
const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
57352
57485
|
|
|
57353
|
-
;// CONCATENATED MODULE: ../core/src/gateways/markdown-ast-gateway.ts
|
|
57354
|
-
/**
|
|
57355
|
-
* Gateway for parsing Markdown files into AST using remark.
|
|
57356
|
-
* Extracts structural features like headings, code blocks, and paragraphs.
|
|
57357
|
-
*/
|
|
57358
|
-
|
|
57359
|
-
|
|
57360
|
-
/** Maximum instruction file size: 1 MB */ const markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
|
|
57361
|
-
/**
|
|
57362
|
-
* Remark-based implementation of the Markdown AST gateway.
|
|
57363
|
-
*/ class RemarkMarkdownAstGateway {
|
|
57364
|
-
async parseFile(filePath) {
|
|
57365
|
-
const content = await file_system_utils_readFileNoFollow(filePath, markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES);
|
|
57366
|
-
return this.parseContent(content);
|
|
57367
|
-
}
|
|
57368
|
-
parseContent(content) {
|
|
57369
|
-
const processor = remark();
|
|
57370
|
-
const ast = processor.parse(content);
|
|
57371
|
-
const headings = [];
|
|
57372
|
-
const codeBlocks = [];
|
|
57373
|
-
const inlineCodes = [];
|
|
57374
|
-
lib_visit(ast, "heading", (node)=>{
|
|
57375
|
-
const text = this.extractHeadingText(node);
|
|
57376
|
-
headings.push({
|
|
57377
|
-
text,
|
|
57378
|
-
depth: node.depth,
|
|
57379
|
-
position: {
|
|
57380
|
-
line: node.position?.start.line ?? 0
|
|
57381
|
-
}
|
|
57382
|
-
});
|
|
57383
|
-
});
|
|
57384
|
-
// Track code blocks with their node index in root children
|
|
57385
|
-
for(let i = 0; i < ast.children.length; i++){
|
|
57386
|
-
const child = ast.children[i];
|
|
57387
|
-
if (child.type === "code") {
|
|
57388
|
-
const codeNode = child;
|
|
57389
|
-
codeBlocks.push({
|
|
57390
|
-
value: codeNode.value,
|
|
57391
|
-
lang: codeNode.lang ?? undefined,
|
|
57392
|
-
position: {
|
|
57393
|
-
line: codeNode.position?.start.line ?? 0
|
|
57394
|
-
},
|
|
57395
|
-
nodeIndex: i
|
|
57396
|
-
});
|
|
57397
|
-
}
|
|
57398
|
-
}
|
|
57399
|
-
lib_visit(ast, "inlineCode", (node)=>{
|
|
57400
|
-
inlineCodes.push({
|
|
57401
|
-
value: node.value,
|
|
57402
|
-
position: {
|
|
57403
|
-
line: node.position?.start.line ?? 0
|
|
57404
|
-
}
|
|
57405
|
-
});
|
|
57406
|
-
});
|
|
57407
|
-
return {
|
|
57408
|
-
headings,
|
|
57409
|
-
codeBlocks,
|
|
57410
|
-
inlineCodes,
|
|
57411
|
-
ast
|
|
57412
|
-
};
|
|
57413
|
-
}
|
|
57414
|
-
findConditionalKeywordsInProximity(structure, codeBlockNodeIndex, proximityWindow, keywords) {
|
|
57415
|
-
const ast = structure.ast;
|
|
57416
|
-
const endIndex = Math.min(ast.children.length, codeBlockNodeIndex + 1 + proximityWindow);
|
|
57417
|
-
for(let i = codeBlockNodeIndex + 1; i < endIndex; i++){
|
|
57418
|
-
const sibling = ast.children[i];
|
|
57419
|
-
const text = extractTextFromNode(sibling);
|
|
57420
|
-
const lowerText = text.toLowerCase();
|
|
57421
|
-
for (const keyword of keywords){
|
|
57422
|
-
if (containsWordBoundary(lowerText, keyword.toLowerCase())) {
|
|
57423
|
-
return true;
|
|
57424
|
-
}
|
|
57425
|
-
}
|
|
57426
|
-
}
|
|
57427
|
-
return false;
|
|
57428
|
-
}
|
|
57429
|
-
extractHeadingText(node) {
|
|
57430
|
-
const parts = [];
|
|
57431
|
-
lib_visit(node, "text", (textNode)=>{
|
|
57432
|
-
parts.push(textNode.value);
|
|
57433
|
-
});
|
|
57434
|
-
return parts.join("");
|
|
57435
|
-
}
|
|
57436
|
-
}
|
|
57437
|
-
/**
|
|
57438
|
-
* Checks if text contains a keyword at a word boundary (safe string matching, no regex).
|
|
57439
|
-
*/ function containsWordBoundary(text, keyword) {
|
|
57440
|
-
const index = text.indexOf(keyword);
|
|
57441
|
-
if (index === -1) {
|
|
57442
|
-
return false;
|
|
57443
|
-
}
|
|
57444
|
-
const charBefore = index > 0 ? text[index - 1] : " ";
|
|
57445
|
-
const charAfter = index + keyword.length < text.length ? text[index + keyword.length] : " ";
|
|
57446
|
-
const isWordBoundaryBefore = !/[a-z0-9]/i.test(charBefore);
|
|
57447
|
-
const isWordBoundaryAfter = !/[a-z0-9]/i.test(charAfter);
|
|
57448
|
-
return isWordBoundaryBefore && isWordBoundaryAfter;
|
|
57449
|
-
}
|
|
57450
|
-
/**
|
|
57451
|
-
* Extracts plain text content from any AST node recursively.
|
|
57452
|
-
*/ function extractTextFromNode(node) {
|
|
57453
|
-
if (node.value && typeof node.value === "string") {
|
|
57454
|
-
return node.value;
|
|
57455
|
-
}
|
|
57456
|
-
if (Array.isArray(node.children)) {
|
|
57457
|
-
return node.children.map((child)=>extractTextFromNode(child)).join(" ");
|
|
57458
|
-
}
|
|
57459
|
-
return "";
|
|
57460
|
-
}
|
|
57461
|
-
/**
|
|
57462
|
-
* Creates and returns the default Markdown AST gateway.
|
|
57463
|
-
*/ function createMarkdownAstGateway() {
|
|
57464
|
-
return new RemarkMarkdownAstGateway();
|
|
57465
|
-
}
|
|
57466
|
-
|
|
57467
57486
|
;// CONCATENATED MODULE: ../core/src/entities/instruction-quality.ts
|
|
57468
57487
|
/**
|
|
57469
57488
|
* Core domain entities for instruction quality analysis.
|
|
@@ -57515,6 +57534,9 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
|
|
|
57515
57534
|
* Orchestrates discovery, AST parsing, and scoring of feedback loop documentation.
|
|
57516
57535
|
*/
|
|
57517
57536
|
|
|
57537
|
+
/** Type guard narrowing an `unknown` AST child to {@link AstNodeLike}. */ function isAstNodeLike(node) {
|
|
57538
|
+
return typeof node === "object" && node !== null && "type" in node && typeof node.type === "string";
|
|
57539
|
+
}
|
|
57518
57540
|
/** Maximum number of raw heading pattern entries accepted before deduplication. */ const MAX_RAW_HEADING_PATTERNS = 1000;
|
|
57519
57541
|
/** Maximum number of heading patterns accepted in a single execute() call. */ const MAX_HEADING_PATTERNS = 50;
|
|
57520
57542
|
/** Maximum character length for a single heading pattern. */ const analyze_instruction_quality_MAX_PATTERN_LENGTH = 200;
|
|
@@ -57870,6 +57892,9 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
|
|
|
57870
57892
|
return parts.join(" ");
|
|
57871
57893
|
}
|
|
57872
57894
|
collectText(node, parts) {
|
|
57895
|
+
if (!isAstNodeLike(node)) {
|
|
57896
|
+
return;
|
|
57897
|
+
}
|
|
57873
57898
|
if (node.type === "text" && typeof node.value === "string") {
|
|
57874
57899
|
parts.push(node.value);
|
|
57875
57900
|
}
|
|
@@ -58016,6 +58041,124 @@ const HEADING_PATTERN_DESCRIPTIONS = new Map(Object.entries(HEADING_PATTERN_DESC
|
|
|
58016
58041
|
}
|
|
58017
58042
|
}
|
|
58018
58043
|
|
|
58044
|
+
;// CONCATENATED MODULE: ../core/src/gateways/markdown-ast-gateway.ts
|
|
58045
|
+
/**
|
|
58046
|
+
* Gateway for parsing Markdown files into AST using remark.
|
|
58047
|
+
* Extracts structural features like headings, code blocks, and paragraphs.
|
|
58048
|
+
*/
|
|
58049
|
+
|
|
58050
|
+
|
|
58051
|
+
|
|
58052
|
+
/** Maximum instruction file size: 1 MB */ const markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES = 1_048_576;
|
|
58053
|
+
/**
|
|
58054
|
+
* Remark-based implementation of the Markdown AST gateway.
|
|
58055
|
+
*/ class RemarkMarkdownAstGateway {
|
|
58056
|
+
async parseFile(filePath) {
|
|
58057
|
+
const content = await read_file_no_follow_readFileNoFollow(filePath, markdown_ast_gateway_MAX_INSTRUCTION_FILE_BYTES);
|
|
58058
|
+
return this.parseContent(content);
|
|
58059
|
+
}
|
|
58060
|
+
parseContent(content) {
|
|
58061
|
+
const processor = remark();
|
|
58062
|
+
const ast = processor.parse(content);
|
|
58063
|
+
const headings = [];
|
|
58064
|
+
const codeBlocks = [];
|
|
58065
|
+
const inlineCodes = [];
|
|
58066
|
+
lib_visit(ast, "heading", (node)=>{
|
|
58067
|
+
const text = this.extractHeadingText(node);
|
|
58068
|
+
headings.push({
|
|
58069
|
+
text,
|
|
58070
|
+
depth: node.depth,
|
|
58071
|
+
position: {
|
|
58072
|
+
line: node.position?.start.line ?? 0
|
|
58073
|
+
}
|
|
58074
|
+
});
|
|
58075
|
+
});
|
|
58076
|
+
// Track code blocks with their node index in root children
|
|
58077
|
+
for(let i = 0; i < ast.children.length; i++){
|
|
58078
|
+
const child = ast.children[i];
|
|
58079
|
+
if (child.type === "code") {
|
|
58080
|
+
const codeNode = child;
|
|
58081
|
+
codeBlocks.push({
|
|
58082
|
+
value: codeNode.value,
|
|
58083
|
+
lang: codeNode.lang ?? undefined,
|
|
58084
|
+
position: {
|
|
58085
|
+
line: codeNode.position?.start.line ?? 0
|
|
58086
|
+
},
|
|
58087
|
+
nodeIndex: i
|
|
58088
|
+
});
|
|
58089
|
+
}
|
|
58090
|
+
}
|
|
58091
|
+
lib_visit(ast, "inlineCode", (node)=>{
|
|
58092
|
+
inlineCodes.push({
|
|
58093
|
+
value: node.value,
|
|
58094
|
+
position: {
|
|
58095
|
+
line: node.position?.start.line ?? 0
|
|
58096
|
+
}
|
|
58097
|
+
});
|
|
58098
|
+
});
|
|
58099
|
+
return {
|
|
58100
|
+
headings,
|
|
58101
|
+
codeBlocks,
|
|
58102
|
+
inlineCodes,
|
|
58103
|
+
ast
|
|
58104
|
+
};
|
|
58105
|
+
}
|
|
58106
|
+
findConditionalKeywordsInProximity(structure, codeBlockNodeIndex, proximityWindow, keywords) {
|
|
58107
|
+
const ast = structure.ast;
|
|
58108
|
+
const endIndex = Math.min(ast.children.length, codeBlockNodeIndex + 1 + proximityWindow);
|
|
58109
|
+
for(let i = codeBlockNodeIndex + 1; i < endIndex; i++){
|
|
58110
|
+
const sibling = ast.children[i];
|
|
58111
|
+
const text = extractTextFromNode(sibling);
|
|
58112
|
+
const lowerText = text.toLowerCase();
|
|
58113
|
+
for (const keyword of keywords){
|
|
58114
|
+
if (containsWordBoundary(lowerText, keyword.toLowerCase())) {
|
|
58115
|
+
return true;
|
|
58116
|
+
}
|
|
58117
|
+
}
|
|
58118
|
+
}
|
|
58119
|
+
return false;
|
|
58120
|
+
}
|
|
58121
|
+
extractHeadingText(node) {
|
|
58122
|
+
const parts = [];
|
|
58123
|
+
lib_visit(node, "text", (textNode)=>{
|
|
58124
|
+
parts.push(textNode.value);
|
|
58125
|
+
});
|
|
58126
|
+
return parts.join("");
|
|
58127
|
+
}
|
|
58128
|
+
}
|
|
58129
|
+
/**
|
|
58130
|
+
* Checks if text contains a keyword at a word boundary (safe string matching, no regex).
|
|
58131
|
+
*/ function containsWordBoundary(text, keyword) {
|
|
58132
|
+
const index = text.indexOf(keyword);
|
|
58133
|
+
if (index === -1) {
|
|
58134
|
+
return false;
|
|
58135
|
+
}
|
|
58136
|
+
const charBefore = index > 0 ? text[index - 1] : " ";
|
|
58137
|
+
const charAfter = index + keyword.length < text.length ? text[index + keyword.length] : " ";
|
|
58138
|
+
const isWordBoundaryBefore = !/[a-z0-9]/i.test(charBefore);
|
|
58139
|
+
const isWordBoundaryAfter = !/[a-z0-9]/i.test(charAfter);
|
|
58140
|
+
return isWordBoundaryBefore && isWordBoundaryAfter;
|
|
58141
|
+
}
|
|
58142
|
+
/**
|
|
58143
|
+
* Extracts plain text content from any AST node recursively.
|
|
58144
|
+
*/ function extractTextFromNode(node) {
|
|
58145
|
+
if (!isAstNodeLike(node)) {
|
|
58146
|
+
return "";
|
|
58147
|
+
}
|
|
58148
|
+
if (node.value && typeof node.value === "string") {
|
|
58149
|
+
return node.value;
|
|
58150
|
+
}
|
|
58151
|
+
if (Array.isArray(node.children)) {
|
|
58152
|
+
return node.children.map((child)=>extractTextFromNode(child)).join(" ");
|
|
58153
|
+
}
|
|
58154
|
+
return "";
|
|
58155
|
+
}
|
|
58156
|
+
/**
|
|
58157
|
+
* Creates and returns the default Markdown AST gateway.
|
|
58158
|
+
*/ function createMarkdownAstGateway() {
|
|
58159
|
+
return new RemarkMarkdownAstGateway();
|
|
58160
|
+
}
|
|
58161
|
+
|
|
58019
58162
|
;// CONCATENATED MODULE: ./src/tools/analyze-instruction-quality.ts
|
|
58020
58163
|
/**
|
|
58021
58164
|
* MCP tool handler for analyzing instruction quality.
|
|
@@ -69893,4 +70036,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
69893
70036
|
// module factories are used so entry inlining is disabled
|
|
69894
70037
|
// startup
|
|
69895
70038
|
// Load entry module and return exports
|
|
69896
|
-
var __webpack_exports__ = __webpack_require__(
|
|
70039
|
+
var __webpack_exports__ = __webpack_require__(7489);
|