@lousy-agents/mcp 5.19.0 → 5.19.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 +1141 -799
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -6559,7 +6559,7 @@ function escapeJsonPtr(str) {
|
|
|
6559
6559
|
|
|
6560
6560
|
|
|
6561
6561
|
},
|
|
6562
|
-
|
|
6562
|
+
4583(__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);
|
|
@@ -26553,6 +26553,9 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
26553
26553
|
const OPERATIONAL_CODES = new Set([
|
|
26554
26554
|
"helper-failed",
|
|
26555
26555
|
"helper-unavailable",
|
|
26556
|
+
"not-empty",
|
|
26557
|
+
"not-found",
|
|
26558
|
+
"not-removable",
|
|
26556
26559
|
"permission-unverified",
|
|
26557
26560
|
"timeout",
|
|
26558
26561
|
"unsupported-platform",
|
|
@@ -26606,6 +26609,7 @@ function file_identity_sameFileIdentity(left, right, platform = process.platform
|
|
|
26606
26609
|
|
|
26607
26610
|
|
|
26608
26611
|
|
|
26612
|
+
|
|
26609
26613
|
const NOT_FOUND_CODES = new Set(["ENOENT", "ENOTDIR"]);
|
|
26610
26614
|
const SYMLINK_OPEN_CODES = new Set(["ELOOP", "EINVAL", "ENOTSUP"]);
|
|
26611
26615
|
const POSIX_SEPARATOR_CHAR_CODE = 0x2f;
|
|
@@ -26723,6 +26727,9 @@ function splitSafeRelativePath(relativePath) {
|
|
|
26723
26727
|
if (segment === "..") {
|
|
26724
26728
|
throw new FsSafeError("invalid-path", "relative path must not contain '..'");
|
|
26725
26729
|
}
|
|
26730
|
+
if (isDriveRelativePath(segment)) {
|
|
26731
|
+
throw new FsSafeError("invalid-path", "relative path must not contain a drive letter");
|
|
26732
|
+
}
|
|
26726
26733
|
}
|
|
26727
26734
|
return segments;
|
|
26728
26735
|
}
|
|
@@ -26735,6 +26742,74 @@ function resolveSafeRelativePath(rootDir, relativePath) {
|
|
|
26735
26742
|
return target;
|
|
26736
26743
|
}
|
|
26737
26744
|
|
|
26745
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-errors.js
|
|
26746
|
+
|
|
26747
|
+
|
|
26748
|
+
const REMOVE_NOT_EMPTY_CODES = new Set(["ENOTEMPTY", "EEXIST"]);
|
|
26749
|
+
function fileNotFoundError(cause) {
|
|
26750
|
+
return cause === undefined
|
|
26751
|
+
? new errors_FsSafeError("not-found", "file not found")
|
|
26752
|
+
: new errors_FsSafeError("not-found", "file not found", { cause });
|
|
26753
|
+
}
|
|
26754
|
+
function outsideWorkspaceError() {
|
|
26755
|
+
return new errors_FsSafeError("outside-workspace", "file is outside workspace root");
|
|
26756
|
+
}
|
|
26757
|
+
function root_errors_directoryComponentNotDirectoryError(cause) {
|
|
26758
|
+
return cause === undefined
|
|
26759
|
+
? new errors_FsSafeError("not-file", "directory component must be a directory")
|
|
26760
|
+
: new errors_FsSafeError("not-file", "directory component must be a directory", { cause });
|
|
26761
|
+
}
|
|
26762
|
+
function hardlinkedPathNotAllowedError() {
|
|
26763
|
+
return new errors_FsSafeError("hardlink", "hardlinked path not allowed");
|
|
26764
|
+
}
|
|
26765
|
+
function isAlreadyExistsError(error) {
|
|
26766
|
+
return hasNodeErrorCode(error, "EEXIST") || /File exists|EEXIST/i.test(String(error));
|
|
26767
|
+
}
|
|
26768
|
+
function normalizePinnedWriteError(error) {
|
|
26769
|
+
if (error instanceof errors_FsSafeError) {
|
|
26770
|
+
return error;
|
|
26771
|
+
}
|
|
26772
|
+
if (path_isNotFoundPathError(error)) {
|
|
26773
|
+
return fileNotFoundError(error instanceof Error ? error : undefined);
|
|
26774
|
+
}
|
|
26775
|
+
return new errors_FsSafeError("invalid-path", "path is not a regular file under root", {
|
|
26776
|
+
cause: error instanceof Error ? error : undefined,
|
|
26777
|
+
});
|
|
26778
|
+
}
|
|
26779
|
+
function normalizePinnedPathError(error) {
|
|
26780
|
+
if (error instanceof errors_FsSafeError) {
|
|
26781
|
+
return error;
|
|
26782
|
+
}
|
|
26783
|
+
return new errors_FsSafeError("path-alias", "path is not under root", {
|
|
26784
|
+
cause: error instanceof Error ? error : undefined,
|
|
26785
|
+
});
|
|
26786
|
+
}
|
|
26787
|
+
function normalizeRemoveGuardError(error) {
|
|
26788
|
+
if (error instanceof errors_FsSafeError) {
|
|
26789
|
+
return error;
|
|
26790
|
+
}
|
|
26791
|
+
if (path_isNotFoundPathError(error)) {
|
|
26792
|
+
return fileNotFoundError(error instanceof Error ? error : undefined);
|
|
26793
|
+
}
|
|
26794
|
+
return normalizePinnedPathError(error);
|
|
26795
|
+
}
|
|
26796
|
+
function normalizeRemovePathError(error) {
|
|
26797
|
+
if (error instanceof errors_FsSafeError) {
|
|
26798
|
+
return error;
|
|
26799
|
+
}
|
|
26800
|
+
if (!isNodeError(error) || typeof error.code !== "string") {
|
|
26801
|
+
return normalizePinnedPathError(error);
|
|
26802
|
+
}
|
|
26803
|
+
const cause = error instanceof Error ? error : undefined;
|
|
26804
|
+
if (path_isNotFoundPathError(error)) {
|
|
26805
|
+
return fileNotFoundError(cause);
|
|
26806
|
+
}
|
|
26807
|
+
if (REMOVE_NOT_EMPTY_CODES.has(error.code)) {
|
|
26808
|
+
return new errors_FsSafeError("not-empty", "directory is not empty", { cause });
|
|
26809
|
+
}
|
|
26810
|
+
return new errors_FsSafeError("not-removable", "path could not be removed", { cause });
|
|
26811
|
+
}
|
|
26812
|
+
|
|
26738
26813
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/directory-guard.js
|
|
26739
26814
|
|
|
26740
26815
|
|
|
@@ -26742,17 +26817,18 @@ function resolveSafeRelativePath(rootDir, relativePath) {
|
|
|
26742
26817
|
|
|
26743
26818
|
|
|
26744
26819
|
|
|
26820
|
+
|
|
26745
26821
|
async function directory_guard_createAsyncDirectoryGuard(dir) {
|
|
26746
26822
|
const stat = await promises_.lstat(dir);
|
|
26747
26823
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26748
|
-
throw
|
|
26824
|
+
throw root_errors_directoryComponentNotDirectoryError();
|
|
26749
26825
|
}
|
|
26750
26826
|
return { dir, realPath: await promises_.realpath(dir), stat };
|
|
26751
26827
|
}
|
|
26752
26828
|
async function assertAsyncDirectoryGuard(guard) {
|
|
26753
26829
|
const stat = await promises_.lstat(guard.dir);
|
|
26754
26830
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26755
|
-
throw
|
|
26831
|
+
throw root_errors_directoryComponentNotDirectoryError();
|
|
26756
26832
|
}
|
|
26757
26833
|
if (!file_identity_sameFileIdentity(stat, guard.stat) || (await promises_.realpath(guard.dir)) !== guard.realPath) {
|
|
26758
26834
|
throw new errors_FsSafeError("path-mismatch", "directory changed during operation");
|
|
@@ -26761,14 +26837,14 @@ async function assertAsyncDirectoryGuard(guard) {
|
|
|
26761
26837
|
function directory_guard_createSyncDirectoryGuard(dir) {
|
|
26762
26838
|
const stat = fsSync.lstatSync(dir);
|
|
26763
26839
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26764
|
-
throw
|
|
26840
|
+
throw directoryComponentNotDirectoryError();
|
|
26765
26841
|
}
|
|
26766
26842
|
return { dir, realPath: fsSync.realpathSync(dir), stat };
|
|
26767
26843
|
}
|
|
26768
26844
|
function directory_guard_assertSyncDirectoryGuard(guard) {
|
|
26769
26845
|
const stat = fsSync.lstatSync(guard.dir);
|
|
26770
26846
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
26771
|
-
throw
|
|
26847
|
+
throw directoryComponentNotDirectoryError();
|
|
26772
26848
|
}
|
|
26773
26849
|
if (!sameFileIdentity(stat, guard.stat) || fsSync.realpathSync(guard.dir) !== guard.realPath) {
|
|
26774
26850
|
throw new FsSafeError("path-mismatch", "directory changed during operation");
|
|
@@ -27114,6 +27190,7 @@ async function ensureDurableDirectory(options) {
|
|
|
27114
27190
|
|
|
27115
27191
|
|
|
27116
27192
|
|
|
27193
|
+
|
|
27117
27194
|
function isSameOrChildPath(candidate, parent) {
|
|
27118
27195
|
const parentPrefix = parent.endsWith(external_node_path_.sep) ? parent : `${parent}${external_node_path_.sep}`;
|
|
27119
27196
|
return candidate === parent || candidate.startsWith(parentPrefix);
|
|
@@ -27126,9 +27203,7 @@ async function realpathOrThrowNotFile(target) {
|
|
|
27126
27203
|
if (path_isNotFoundPathError(error)) {
|
|
27127
27204
|
// A dangling symlink (or a component removed between lstat and
|
|
27128
27205
|
// realpath) is not a usable directory component.
|
|
27129
|
-
throw
|
|
27130
|
-
cause: error instanceof Error ? error : undefined,
|
|
27131
|
-
});
|
|
27206
|
+
throw root_errors_directoryComponentNotDirectoryError(error instanceof Error ? error : undefined);
|
|
27132
27207
|
}
|
|
27133
27208
|
throw error;
|
|
27134
27209
|
}
|
|
@@ -27151,8 +27226,8 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
27151
27226
|
for (const part of relative.split(external_node_path_.sep).filter(Boolean)) {
|
|
27152
27227
|
const next = external_node_path_.join(current, part);
|
|
27153
27228
|
const parentGuard = await directory_guard_createAsyncDirectoryGuard(current);
|
|
27154
|
-
await params.beforeComponent?.(next);
|
|
27155
27229
|
await assertAsyncDirectoryGuard(parentGuard);
|
|
27230
|
+
await params.beforeComponent?.(next);
|
|
27156
27231
|
try {
|
|
27157
27232
|
await promises_.mkdir(next);
|
|
27158
27233
|
}
|
|
@@ -27163,7 +27238,7 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
27163
27238
|
}
|
|
27164
27239
|
const stat = await promises_.lstat(next);
|
|
27165
27240
|
if (!stat.isSymbolicLink() && !stat.isDirectory()) {
|
|
27166
|
-
throw
|
|
27241
|
+
throw root_errors_directoryComponentNotDirectoryError();
|
|
27167
27242
|
}
|
|
27168
27243
|
// Node's recursive mkdir follows symlinks in missing components. Build one
|
|
27169
27244
|
// segment at a time and realpath-check each segment before descending.
|
|
@@ -27183,7 +27258,7 @@ async function mkdirPathComponentsWithGuards(params) {
|
|
|
27183
27258
|
// the returned resolved path, not their own lexical parent path.
|
|
27184
27259
|
const targetStat = await promises_.stat(nextReal);
|
|
27185
27260
|
if (!targetStat.isDirectory()) {
|
|
27186
|
-
throw
|
|
27261
|
+
throw root_errors_directoryComponentNotDirectoryError();
|
|
27187
27262
|
}
|
|
27188
27263
|
await directory_guard_createAsyncDirectoryGuard(nextReal);
|
|
27189
27264
|
await assertAsyncDirectoryGuard(parentGuard);
|
|
@@ -27275,30 +27350,37 @@ function guardedRmSync(params) {
|
|
|
27275
27350
|
}), { verifyAfter: params.verifyAfter });
|
|
27276
27351
|
}
|
|
27277
27352
|
|
|
27278
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/
|
|
27353
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path-existing.js
|
|
27279
27354
|
|
|
27280
27355
|
|
|
27281
27356
|
|
|
27282
27357
|
|
|
27283
|
-
|
|
27358
|
+
function isFilesystemRoot(candidate) {
|
|
27359
|
+
return external_node_path_.parse(candidate).root === candidate;
|
|
27360
|
+
}
|
|
27361
|
+
async function pathExists(targetPath) {
|
|
27284
27362
|
try {
|
|
27285
|
-
await promises_.lstat(
|
|
27363
|
+
await promises_.lstat(targetPath);
|
|
27286
27364
|
return true;
|
|
27287
27365
|
}
|
|
27288
|
-
catch (
|
|
27289
|
-
if (
|
|
27290
|
-
|
|
27366
|
+
catch (error) {
|
|
27367
|
+
if (path_isNotFoundPathError(error)) {
|
|
27368
|
+
return false;
|
|
27291
27369
|
}
|
|
27292
|
-
|
|
27370
|
+
throw error;
|
|
27293
27371
|
}
|
|
27294
27372
|
}
|
|
27295
27373
|
async function resolvePathViaExistingAncestor(targetPath) {
|
|
27296
27374
|
const normalized = external_node_path_.resolve(targetPath);
|
|
27297
27375
|
let cursor = normalized;
|
|
27298
27376
|
const missingSuffix = [];
|
|
27299
|
-
while (
|
|
27377
|
+
while (!isFilesystemRoot(cursor) && !(await pathExists(cursor))) {
|
|
27300
27378
|
missingSuffix.unshift(external_node_path_.basename(cursor));
|
|
27301
|
-
|
|
27379
|
+
const parent = external_node_path_.dirname(cursor);
|
|
27380
|
+
if (parent === cursor) {
|
|
27381
|
+
break;
|
|
27382
|
+
}
|
|
27383
|
+
cursor = parent;
|
|
27302
27384
|
}
|
|
27303
27385
|
if (!(await pathExists(cursor))) {
|
|
27304
27386
|
return normalized;
|
|
@@ -27313,7 +27395,38 @@ async function resolvePathViaExistingAncestor(targetPath) {
|
|
|
27313
27395
|
return normalized;
|
|
27314
27396
|
}
|
|
27315
27397
|
}
|
|
27316
|
-
|
|
27398
|
+
function root_path_existing_resolvePathViaExistingAncestorSync(targetPath) {
|
|
27399
|
+
const normalized = path.resolve(targetPath);
|
|
27400
|
+
let cursor = normalized;
|
|
27401
|
+
const missingSuffix = [];
|
|
27402
|
+
while (!isFilesystemRoot(cursor) && !fs.existsSync(cursor)) {
|
|
27403
|
+
missingSuffix.unshift(path.basename(cursor));
|
|
27404
|
+
const parent = path.dirname(cursor);
|
|
27405
|
+
if (parent === cursor) {
|
|
27406
|
+
break;
|
|
27407
|
+
}
|
|
27408
|
+
cursor = parent;
|
|
27409
|
+
}
|
|
27410
|
+
if (!fs.existsSync(cursor)) {
|
|
27411
|
+
return normalized;
|
|
27412
|
+
}
|
|
27413
|
+
try {
|
|
27414
|
+
const resolvedAncestor = path.resolve(fs.realpathSync(cursor));
|
|
27415
|
+
return missingSuffix.length === 0
|
|
27416
|
+
? resolvedAncestor
|
|
27417
|
+
: path.resolve(resolvedAncestor, ...missingSuffix);
|
|
27418
|
+
}
|
|
27419
|
+
catch {
|
|
27420
|
+
return normalized;
|
|
27421
|
+
}
|
|
27422
|
+
}
|
|
27423
|
+
|
|
27424
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/deny-mutations.js
|
|
27425
|
+
|
|
27426
|
+
|
|
27427
|
+
|
|
27428
|
+
|
|
27429
|
+
async function resolveMutationComparablePaths(rawPath) {
|
|
27317
27430
|
path_assertNoNulPathInput(rawPath, "path contains a NUL byte");
|
|
27318
27431
|
const resolved = external_node_path_.resolve(rawPath);
|
|
27319
27432
|
return new Set([resolved, await resolvePathViaExistingAncestor(resolved)]);
|
|
@@ -27342,9 +27455,9 @@ async function assertMutationNotDenied(filePath, policy, options = {}) {
|
|
|
27342
27455
|
if (!hasPolicyEntries(policy)) {
|
|
27343
27456
|
return;
|
|
27344
27457
|
}
|
|
27345
|
-
const targetPaths = await
|
|
27458
|
+
const targetPaths = await resolveMutationComparablePaths(filePath);
|
|
27346
27459
|
for (const deniedPath of policyPathEntries(policy.paths)) {
|
|
27347
|
-
const deniedPaths = await
|
|
27460
|
+
const deniedPaths = await resolveMutationComparablePaths(deniedPath);
|
|
27348
27461
|
for (const target of targetPaths) {
|
|
27349
27462
|
for (const denied of deniedPaths) {
|
|
27350
27463
|
if (isSamePath(denied, target) ||
|
|
@@ -27355,7 +27468,7 @@ async function assertMutationNotDenied(filePath, policy, options = {}) {
|
|
|
27355
27468
|
}
|
|
27356
27469
|
}
|
|
27357
27470
|
for (const deniedPrefix of policyPathEntries(policy.prefixes)) {
|
|
27358
|
-
const deniedPaths = await
|
|
27471
|
+
const deniedPaths = await resolveMutationComparablePaths(deniedPrefix);
|
|
27359
27472
|
for (const target of targetPaths) {
|
|
27360
27473
|
for (const denied of deniedPaths) {
|
|
27361
27474
|
if (path_isPathInside(denied, target) ||
|
|
@@ -27818,7 +27931,20 @@ async function createNativeExclusiveFile(targetPath, mode) {
|
|
|
27818
27931
|
let fd;
|
|
27819
27932
|
let created;
|
|
27820
27933
|
try {
|
|
27821
|
-
|
|
27934
|
+
let opened;
|
|
27935
|
+
try {
|
|
27936
|
+
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));
|
|
27937
|
+
}
|
|
27938
|
+
catch (error) {
|
|
27939
|
+
// The parent open above and every post-create operation remain untagged.
|
|
27940
|
+
// Only this exclusive-open failure has enough provenance for a caller to
|
|
27941
|
+
// classify a Windows lock-file denial without swallowing setup failures.
|
|
27942
|
+
const openError = error;
|
|
27943
|
+
if (process.platform === "win32" && openError.code === "EPERM") {
|
|
27944
|
+
openError.path = targetPath;
|
|
27945
|
+
}
|
|
27946
|
+
throw error;
|
|
27947
|
+
}
|
|
27822
27948
|
fd = opened.fd;
|
|
27823
27949
|
external_node_fs_.fchmodSync(fd, mode);
|
|
27824
27950
|
created = external_node_fs_.fstatSync(fd);
|
|
@@ -27872,23 +27998,22 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
27872
27998
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
27873
27999
|
}
|
|
27874
28000
|
}
|
|
27875
|
-
async function
|
|
28001
|
+
async function writeNativeInput(fd, input, maxBytes) {
|
|
27876
28002
|
if (input.kind === "buffer") {
|
|
27877
28003
|
const data = typeof input.data === "string"
|
|
27878
28004
|
? Buffer.from(input.data, input.encoding ?? "utf8")
|
|
27879
28005
|
: Buffer.from(input.data);
|
|
27880
28006
|
assertWithinMaxBytes(data.byteLength, maxBytes);
|
|
27881
|
-
|
|
28007
|
+
writeNativeFd(fd, data);
|
|
28008
|
+
return;
|
|
27882
28009
|
}
|
|
27883
|
-
const chunks = [];
|
|
27884
28010
|
let bytes = 0;
|
|
27885
28011
|
for await (const chunk of input.stream) {
|
|
27886
28012
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
27887
28013
|
bytes += buffer.byteLength;
|
|
27888
28014
|
assertWithinMaxBytes(bytes, maxBytes);
|
|
27889
|
-
|
|
28015
|
+
writeNativeFd(fd, buffer);
|
|
27890
28016
|
}
|
|
27891
|
-
return Buffer.concat(chunks, bytes);
|
|
27892
28017
|
}
|
|
27893
28018
|
function native_pinned_write_nativeOpenFlags(flags) {
|
|
27894
28019
|
const closeOnExec = external_node_fs_.constants.O_CLOEXEC;
|
|
@@ -27900,7 +28025,6 @@ function sameNativeIdentity(left, right) {
|
|
|
27900
28025
|
return left.dev === right.dev && left.ino === right.ino;
|
|
27901
28026
|
}
|
|
27902
28027
|
async function runPinnedWriteNative(binding, params) {
|
|
27903
|
-
const data = await inputToBuffer(params.input, params.maxBytes);
|
|
27904
28028
|
const root = await promises_.open(params.rootPath, external_node_fs_.constants.O_RDONLY |
|
|
27905
28029
|
(typeof external_node_fs_.constants.O_DIRECTORY === "number" ? external_node_fs_.constants.O_DIRECTORY : 0));
|
|
27906
28030
|
let parentFd;
|
|
@@ -27930,27 +28054,56 @@ async function runPinnedWriteNative(binding, params) {
|
|
|
27930
28054
|
!sameNativeIdentity(parentPathStat, parentIdentity)) {
|
|
27931
28055
|
throw new errors_FsSafeError("path-mismatch", "native write parent changed during resolution");
|
|
27932
28056
|
}
|
|
27933
|
-
|
|
27934
|
-
|
|
27935
|
-
|
|
27936
|
-
|
|
27937
|
-
|
|
27938
|
-
|
|
27939
|
-
|
|
28057
|
+
if (params.overwrite === false) {
|
|
28058
|
+
try {
|
|
28059
|
+
await promises_.lstat(external_node_path_.join(parentPath, params.basename));
|
|
28060
|
+
throw Object.assign(new Error("destination already exists"), { code: "EEXIST" });
|
|
28061
|
+
}
|
|
28062
|
+
catch (error) {
|
|
28063
|
+
if (error.code !== "ENOENT") {
|
|
28064
|
+
throw error;
|
|
28065
|
+
}
|
|
27940
28066
|
}
|
|
27941
28067
|
}
|
|
27942
28068
|
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
28069
|
tempIdentity = external_node_fs_.fstatSync(tempFd);
|
|
27947
|
-
binding
|
|
28070
|
+
// Creation is requested at 0600 in the binding, but a restrictive umask
|
|
28071
|
+
// can remove owner access. Keep the unpublished inode private and
|
|
28072
|
+
// reopenable until the published name has been identity-fenced.
|
|
28073
|
+
external_node_fs_.fchmodSync(tempFd, 0o600);
|
|
28074
|
+
await writeNativeInput(tempFd, params.input, params.maxBytes);
|
|
28075
|
+
syncNativeFileBestEffort(tempFd);
|
|
28076
|
+
if (params.overwrite === false) {
|
|
28077
|
+
binding.renameNoReplace(parentFd, tempName, parentFd, params.basename);
|
|
28078
|
+
}
|
|
28079
|
+
else {
|
|
28080
|
+
binding.renameReplace(parentFd, tempName, parentFd, params.basename);
|
|
28081
|
+
}
|
|
27948
28082
|
renamed = true;
|
|
27949
28083
|
targetFd = binding.openBeneath(parentFd, params.basename, native_pinned_write_nativeOpenFlags(external_node_fs_.constants.O_RDONLY)).fd;
|
|
27950
28084
|
const targetIdentity = binding.fstatIdentity(targetFd);
|
|
27951
28085
|
if (!targetIdentity.isFile || !sameNativeIdentity(tempIdentity, targetIdentity)) {
|
|
27952
28086
|
throw new errors_FsSafeError("path-mismatch", "native write target changed after rename");
|
|
27953
28087
|
}
|
|
28088
|
+
// Native exclusive creation starts at 0600. Apply the requested mode only
|
|
28089
|
+
// after reopening and fencing the published name, both so mode 000 stays
|
|
28090
|
+
// verifiable and so broader modes are never exposed before that fence.
|
|
28091
|
+
try {
|
|
28092
|
+
external_node_fs_.fchmodSync(targetFd, params.mode);
|
|
28093
|
+
syncNativeFileBestEffort(targetFd);
|
|
28094
|
+
}
|
|
28095
|
+
catch (error) {
|
|
28096
|
+
external_node_fs_.closeSync(targetFd);
|
|
28097
|
+
targetFd = undefined;
|
|
28098
|
+
removeNativeCreatedFileIfStillPinned({
|
|
28099
|
+
binding,
|
|
28100
|
+
parentPath,
|
|
28101
|
+
parentFd,
|
|
28102
|
+
basename: params.basename,
|
|
28103
|
+
created: tempIdentity,
|
|
28104
|
+
});
|
|
28105
|
+
throw error;
|
|
28106
|
+
}
|
|
27954
28107
|
syncNativeFileBestEffort(parentFd);
|
|
27955
28108
|
return { dev: targetIdentity.dev, ino: targetIdentity.ino };
|
|
27956
28109
|
}
|
|
@@ -28087,7 +28240,7 @@ async function readFileDescriptorBounded(fd, maxBytes) {
|
|
|
28087
28240
|
});
|
|
28088
28241
|
}
|
|
28089
28242
|
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
28090
|
-
function
|
|
28243
|
+
function bounded_read_readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
28091
28244
|
assertMaxBytes(maxBytes);
|
|
28092
28245
|
const chunks = [];
|
|
28093
28246
|
const scratch = createScratchBuffer(maxBytes);
|
|
@@ -28102,6 +28255,21 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
28102
28255
|
}
|
|
28103
28256
|
}
|
|
28104
28257
|
|
|
28258
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
|
|
28259
|
+
let test_hooks_fsSafeTestHooks;
|
|
28260
|
+
function allowFsSafeTestHooks() {
|
|
28261
|
+
return false || process.env.VITEST === "true";
|
|
28262
|
+
}
|
|
28263
|
+
function getFsSafeTestHooks() {
|
|
28264
|
+
return test_hooks_fsSafeTestHooks;
|
|
28265
|
+
}
|
|
28266
|
+
function __setFsSafeTestHooksForTest(hooks) {
|
|
28267
|
+
if (hooks && !allowFsSafeTestHooks()) {
|
|
28268
|
+
throw new Error("__setFsSafeTestHooksForTest is only available in tests");
|
|
28269
|
+
}
|
|
28270
|
+
test_hooks_fsSafeTestHooks = hooks;
|
|
28271
|
+
}
|
|
28272
|
+
|
|
28105
28273
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
28106
28274
|
|
|
28107
28275
|
|
|
@@ -28110,6 +28278,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
|
28110
28278
|
|
|
28111
28279
|
|
|
28112
28280
|
|
|
28281
|
+
|
|
28113
28282
|
const MAX_LOCK_PAYLOAD_BYTES = 1024 * 1024;
|
|
28114
28283
|
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
|
|
28115
28284
|
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS = SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES * 8;
|
|
@@ -28160,6 +28329,7 @@ function parseSidecarLockPayload(raw, parser) {
|
|
|
28160
28329
|
}
|
|
28161
28330
|
}
|
|
28162
28331
|
async function readSidecarLockSnapshot(lockPath, options = {}) {
|
|
28332
|
+
let handle;
|
|
28163
28333
|
try {
|
|
28164
28334
|
if (options.lockRoot) {
|
|
28165
28335
|
const opened = await options.lockRoot.open(relativeSidecarLockPath(options.lockRoot, lockPath));
|
|
@@ -28175,9 +28345,44 @@ async function readSidecarLockSnapshot(lockPath, options = {}) {
|
|
|
28175
28345
|
await opened.handle.close().catch(() => undefined);
|
|
28176
28346
|
}
|
|
28177
28347
|
}
|
|
28178
|
-
const
|
|
28179
|
-
|
|
28180
|
-
|
|
28348
|
+
const before = await promises_.lstat(lockPath);
|
|
28349
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
28350
|
+
if (options.rejectNonFile) {
|
|
28351
|
+
throw new errors_FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`);
|
|
28352
|
+
}
|
|
28353
|
+
return null;
|
|
28354
|
+
}
|
|
28355
|
+
await getFsSafeTestHooks()?.beforeSidecarLockSnapshotOpen?.(lockPath);
|
|
28356
|
+
const noFollow = process.platform !== "win32" && typeof external_node_fs_.constants.O_NOFOLLOW === "number"
|
|
28357
|
+
? external_node_fs_.constants.O_NOFOLLOW
|
|
28358
|
+
: 0;
|
|
28359
|
+
try {
|
|
28360
|
+
handle = await promises_.open(lockPath, external_node_fs_.constants.O_RDONLY |
|
|
28361
|
+
noFollow |
|
|
28362
|
+
(typeof external_node_fs_.constants.O_NONBLOCK === "number" ? external_node_fs_.constants.O_NONBLOCK : 0));
|
|
28363
|
+
}
|
|
28364
|
+
catch (error) {
|
|
28365
|
+
if (options.rejectNonFile && error.code === "ELOOP") {
|
|
28366
|
+
throw new errors_FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`, {
|
|
28367
|
+
cause: error,
|
|
28368
|
+
});
|
|
28369
|
+
}
|
|
28370
|
+
throw error;
|
|
28371
|
+
}
|
|
28372
|
+
const opened = await handle.stat();
|
|
28373
|
+
if (!opened.isFile()) {
|
|
28374
|
+
if (options.rejectNonFile) {
|
|
28375
|
+
throw new errors_FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`);
|
|
28376
|
+
}
|
|
28377
|
+
return null;
|
|
28378
|
+
}
|
|
28379
|
+
if (!options.allowDescriptorIdentityDrift && !file_identity_sameFileIdentity(before, opened))
|
|
28380
|
+
return null;
|
|
28381
|
+
const raw = (await readFileHandleBounded(handle, MAX_LOCK_PAYLOAD_BYTES)).toString("utf8");
|
|
28382
|
+
const after = await promises_.lstat(lockPath);
|
|
28383
|
+
if (!after.isFile() || !file_identity_sameFileIdentity(before, after))
|
|
28384
|
+
return null;
|
|
28385
|
+
return { raw, payload: parseSidecarLockPayload(raw, options.parsePayload), stat: after };
|
|
28181
28386
|
}
|
|
28182
28387
|
catch (err) {
|
|
28183
28388
|
if (err.code === "ENOENT" ||
|
|
@@ -28186,19 +28391,26 @@ async function readSidecarLockSnapshot(lockPath, options = {}) {
|
|
|
28186
28391
|
}
|
|
28187
28392
|
throw err;
|
|
28188
28393
|
}
|
|
28394
|
+
finally {
|
|
28395
|
+
await handle?.close().catch(() => undefined);
|
|
28396
|
+
}
|
|
28189
28397
|
}
|
|
28190
|
-
function readSidecarLockSnapshotSync(lockPath, parsePayload) {
|
|
28398
|
+
function readSidecarLockSnapshotSync(lockPath, parsePayload, options = {}) {
|
|
28191
28399
|
let fd;
|
|
28192
28400
|
try {
|
|
28193
28401
|
const before = fsSync.lstatSync(lockPath);
|
|
28194
|
-
if (!before.isFile() || before.isSymbolicLink())
|
|
28402
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
28403
|
+
if (options.rejectNonFile) {
|
|
28404
|
+
throw new FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`);
|
|
28405
|
+
}
|
|
28195
28406
|
return null;
|
|
28407
|
+
}
|
|
28196
28408
|
const noFollow = process.platform !== "win32" && typeof fsSync.constants.O_NOFOLLOW === "number"
|
|
28197
28409
|
? fsSync.constants.O_NOFOLLOW
|
|
28198
28410
|
: 0;
|
|
28199
28411
|
fd = fsSync.openSync(lockPath, fsSync.constants.O_RDONLY | noFollow);
|
|
28200
28412
|
const opened = fsSync.fstatSync(fd);
|
|
28201
|
-
const raw =
|
|
28413
|
+
const raw = readFileDescriptorBoundedSync(fd, MAX_LOCK_PAYLOAD_BYTES).toString("utf8");
|
|
28202
28414
|
const after = fsSync.lstatSync(lockPath);
|
|
28203
28415
|
if (!sameFileIdentity(before, opened) || !sameFileIdentity(opened, after))
|
|
28204
28416
|
return null;
|
|
@@ -28244,7 +28456,10 @@ function sidecarLockSnapshotMatches(current, observed) {
|
|
|
28244
28456
|
return observed.stat !== undefined && current.stat !== undefined;
|
|
28245
28457
|
}
|
|
28246
28458
|
async function removeSidecarLockIfUnchanged(lockPath, observed, options = {}) {
|
|
28247
|
-
const current = await readSidecarLockSnapshot(lockPath,
|
|
28459
|
+
const current = await readSidecarLockSnapshot(lockPath, {
|
|
28460
|
+
...options,
|
|
28461
|
+
allowDescriptorIdentityDrift: observed?.ownershipToken !== undefined,
|
|
28462
|
+
});
|
|
28248
28463
|
if (!current || !observed || !sidecarLockSnapshotMatches(current, observed)) {
|
|
28249
28464
|
return false;
|
|
28250
28465
|
}
|
|
@@ -28257,7 +28472,10 @@ async function removeSidecarLockIfUnchanged(lockPath, observed, options = {}) {
|
|
|
28257
28472
|
return true;
|
|
28258
28473
|
}
|
|
28259
28474
|
async function sidecarLockSnapshotStillPresent(lockPath, observed, options = {}) {
|
|
28260
|
-
const current = await readSidecarLockSnapshot(lockPath,
|
|
28475
|
+
const current = await readSidecarLockSnapshot(lockPath, {
|
|
28476
|
+
...options,
|
|
28477
|
+
allowDescriptorIdentityDrift: observed?.ownershipToken !== undefined,
|
|
28478
|
+
});
|
|
28261
28479
|
return !!current && !!observed && sidecarLockSnapshotMatches(current, observed);
|
|
28262
28480
|
}
|
|
28263
28481
|
async function sidecarReclaimGuardExists(pathname) {
|
|
@@ -28325,36 +28543,6 @@ async function removeStaleSidecarLockIfAllowed(params) {
|
|
|
28325
28543
|
}
|
|
28326
28544
|
}
|
|
28327
28545
|
|
|
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
28546
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-policy.js
|
|
28359
28547
|
|
|
28360
28548
|
function computeSidecarLockDelayMs(retry, attempt) {
|
|
@@ -28365,7 +28553,23 @@ function computeSidecarLockDelayMs(retry, attempt) {
|
|
|
28365
28553
|
const jitter = retry.randomize ? 1 + Math.random() : 1;
|
|
28366
28554
|
return Math.min(maxTimeout, Math.round(base * jitter));
|
|
28367
28555
|
}
|
|
28556
|
+
// Windows denies access to a lock file while a just-unlinked directory entry
|
|
28557
|
+
// is still being torn down, so a contended acquire sees EPERM on a name that is
|
|
28558
|
+
// already gone -- both when creating it exclusively and when reading the
|
|
28559
|
+
// holder's snapshot. The next attempt succeeds, so this is contention rather
|
|
28560
|
+
// than a permission failure. The error must name the lock file itself: the
|
|
28561
|
+
// exclusive-create helper opens the parent directory first, and a denial from
|
|
28562
|
+
// that setup step carries no teardown evidence and has to reach the caller.
|
|
28563
|
+
const maxTransientLockDenials = 8;
|
|
28564
|
+
function isTransientLockFileDenial(error, lockPath) {
|
|
28565
|
+
const denial = error;
|
|
28566
|
+
return process.platform === "win32" && denial?.code === "EPERM" && denial.path === lockPath;
|
|
28567
|
+
}
|
|
28368
28568
|
function sidecarLockPayloadIsStale(payload, staleMs, nowMs) {
|
|
28569
|
+
const createdAtMs = sidecarLockPayloadCreatedAtMs(payload);
|
|
28570
|
+
return createdAtMs !== null && nowMs - createdAtMs > staleMs;
|
|
28571
|
+
}
|
|
28572
|
+
function sidecarLockPayloadCreatedAtMs(payload) {
|
|
28369
28573
|
const createdAt = payload &&
|
|
28370
28574
|
typeof payload === "object" &&
|
|
28371
28575
|
"createdAt" in payload &&
|
|
@@ -28373,11 +28577,12 @@ function sidecarLockPayloadIsStale(payload, staleMs, nowMs) {
|
|
|
28373
28577
|
? payload.createdAt
|
|
28374
28578
|
: "";
|
|
28375
28579
|
const createdAtMs = Date.parse(createdAt);
|
|
28376
|
-
return Number.isFinite(createdAtMs)
|
|
28580
|
+
return Number.isFinite(createdAtMs) ? createdAtMs : null;
|
|
28377
28581
|
}
|
|
28378
28582
|
async function defaultSidecarLockShouldReclaim(params) {
|
|
28379
|
-
|
|
28380
|
-
|
|
28583
|
+
const createdAtMs = sidecarLockPayloadCreatedAtMs(params.payload);
|
|
28584
|
+
if (createdAtMs !== null)
|
|
28585
|
+
return params.nowMs - createdAtMs > params.staleMs;
|
|
28381
28586
|
try {
|
|
28382
28587
|
return params.nowMs - (await promises_.stat(params.lockPath)).mtimeMs > params.staleMs;
|
|
28383
28588
|
}
|
|
@@ -28386,17 +28591,307 @@ async function defaultSidecarLockShouldReclaim(params) {
|
|
|
28386
28591
|
}
|
|
28387
28592
|
}
|
|
28388
28593
|
|
|
28389
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
28594
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js
|
|
28595
|
+
|
|
28596
|
+
|
|
28597
|
+
|
|
28598
|
+
|
|
28390
28599
|
|
|
28391
28600
|
|
|
28601
|
+
async function resolveNormalizedTargetPath(targetPath) {
|
|
28602
|
+
const resolved = external_node_path_.resolve(targetPath);
|
|
28603
|
+
const dir = external_node_path_.dirname(resolved);
|
|
28604
|
+
await promises_.mkdir(dir, { recursive: true });
|
|
28605
|
+
try {
|
|
28606
|
+
return external_node_path_.join(await promises_.realpath(dir), external_node_path_.basename(resolved));
|
|
28607
|
+
}
|
|
28608
|
+
catch {
|
|
28609
|
+
return resolved;
|
|
28610
|
+
}
|
|
28611
|
+
}
|
|
28612
|
+
async function acquireSidecarLock(options, context) {
|
|
28613
|
+
context.ensureExitCleanupRegistered();
|
|
28614
|
+
const normalizedTargetPath = await resolveNormalizedTargetPath(options.targetPath);
|
|
28615
|
+
const lockPath = options.lockPath ?? `${normalizedTargetPath}.lock`;
|
|
28616
|
+
const held = context.held.get(normalizedTargetPath);
|
|
28617
|
+
if (held &&
|
|
28618
|
+
options.reentrantOwner !== undefined &&
|
|
28619
|
+
held.reentrantOwner !== undefined &&
|
|
28620
|
+
options.reentrantOwner === held.reentrantOwner) {
|
|
28621
|
+
held.refCount += 1;
|
|
28622
|
+
return context.handleForHeldLock(normalizedTargetPath, held);
|
|
28623
|
+
}
|
|
28624
|
+
const startedAt = Date.now();
|
|
28625
|
+
const retry = options.retry ?? {};
|
|
28626
|
+
const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
|
|
28627
|
+
const reclaimGuardPath = `${lockPath}.reclaim`;
|
|
28628
|
+
let ownsReclaimGuard = false;
|
|
28629
|
+
let attempt = 0;
|
|
28630
|
+
// Bounded so a genuine denial still surfaces as EPERM, not a lock timeout.
|
|
28631
|
+
let transientDenials = 0;
|
|
28632
|
+
const withinDenialBudget = () => ++transientDenials <= (/* inlined export .maxTransientLockDenials */8);
|
|
28633
|
+
const waitForRetry = async () => {
|
|
28634
|
+
const elapsed = Date.now() - startedAt;
|
|
28635
|
+
if ((options.timeoutMs !== undefined &&
|
|
28636
|
+
options.timeoutMs !== Number.POSITIVE_INFINITY &&
|
|
28637
|
+
elapsed >= options.timeoutMs) ||
|
|
28638
|
+
(maxRetries !== undefined && attempt >= maxRetries)) {
|
|
28639
|
+
throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
28640
|
+
code: "file_lock_timeout",
|
|
28641
|
+
lockPath,
|
|
28642
|
+
normalizedTargetPath,
|
|
28643
|
+
});
|
|
28644
|
+
}
|
|
28645
|
+
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
28646
|
+
? Number.POSITIVE_INFINITY
|
|
28647
|
+
: Math.max(0, options.timeoutMs - elapsed);
|
|
28648
|
+
const delay = Math.min(computeSidecarLockDelayMs(retry, attempt), remaining);
|
|
28649
|
+
attempt += 1;
|
|
28650
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28651
|
+
};
|
|
28652
|
+
// Waiting can fail on the caller's own retry or deadline limits. Classifying
|
|
28653
|
+
// a denial as contention must not cost them the original diagnosis, so hand
|
|
28654
|
+
// the denial back when no further attempt can be scheduled.
|
|
28655
|
+
const retryOrRethrowDenial = async (denial) => {
|
|
28656
|
+
try {
|
|
28657
|
+
await waitForRetry();
|
|
28658
|
+
}
|
|
28659
|
+
catch (waitError) {
|
|
28660
|
+
if (waitError.code === "file_lock_timeout")
|
|
28661
|
+
throw denial;
|
|
28662
|
+
throw waitError;
|
|
28663
|
+
}
|
|
28664
|
+
};
|
|
28665
|
+
try {
|
|
28666
|
+
while (true) {
|
|
28667
|
+
if (!ownsReclaimGuard && (await sidecarReclaimGuardExists(reclaimGuardPath))) {
|
|
28668
|
+
await waitForRetry();
|
|
28669
|
+
continue;
|
|
28670
|
+
}
|
|
28671
|
+
let handle = null;
|
|
28672
|
+
let createdSnapshot = null;
|
|
28673
|
+
let lockFileCreateDenied = false;
|
|
28674
|
+
try {
|
|
28675
|
+
const payload = await options.payload();
|
|
28676
|
+
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
28677
|
+
if (options.lockRoot) {
|
|
28678
|
+
const relativeLockPath = relativeSidecarLockPath(options.lockRoot, lockPath);
|
|
28679
|
+
try {
|
|
28680
|
+
await options.lockRoot.create(relativeLockPath, raw, { mkdir: true, mode: 0o600 });
|
|
28681
|
+
}
|
|
28682
|
+
catch (error) {
|
|
28683
|
+
if (error instanceof errors_FsSafeError && error.code === "already-exists") {
|
|
28684
|
+
throw Object.assign(new Error("sidecar lock exists"), { code: "EEXIST" });
|
|
28685
|
+
}
|
|
28686
|
+
throw error;
|
|
28687
|
+
}
|
|
28688
|
+
createdSnapshot = { raw, payload, ownershipToken };
|
|
28689
|
+
handle = (await options.lockRoot.open(relativeLockPath)).handle;
|
|
28690
|
+
}
|
|
28691
|
+
else {
|
|
28692
|
+
try {
|
|
28693
|
+
handle =
|
|
28694
|
+
(await createNativeExclusiveFile(lockPath, 0o600)) ??
|
|
28695
|
+
(await promises_.open(lockPath, "wx"));
|
|
28696
|
+
}
|
|
28697
|
+
catch (createError) {
|
|
28698
|
+
lockFileCreateDenied = isTransientLockFileDenial(createError, lockPath);
|
|
28699
|
+
throw createError;
|
|
28700
|
+
}
|
|
28701
|
+
await handle.writeFile(raw, "utf8");
|
|
28702
|
+
}
|
|
28703
|
+
const snapshot = { raw, payload, stat: await handle.stat(), ownershipToken };
|
|
28704
|
+
const createdHeld = {
|
|
28705
|
+
refCount: 1,
|
|
28706
|
+
reentrantOwner: options.reentrantOwner,
|
|
28707
|
+
handle,
|
|
28708
|
+
lockPath,
|
|
28709
|
+
snapshot,
|
|
28710
|
+
acquiredAt: Date.now(),
|
|
28711
|
+
metadata: options.metadata ?? {},
|
|
28712
|
+
lockRoot: options.lockRoot,
|
|
28713
|
+
parsePayload: options.parsePayload,
|
|
28714
|
+
};
|
|
28715
|
+
context.held.set(normalizedTargetPath, createdHeld);
|
|
28716
|
+
if (ownsReclaimGuard) {
|
|
28717
|
+
try {
|
|
28718
|
+
await releaseSidecarReclaimGuard(context.reclaimGuards, reclaimGuardPath);
|
|
28719
|
+
ownsReclaimGuard = false;
|
|
28720
|
+
}
|
|
28721
|
+
catch (err) {
|
|
28722
|
+
await context.releaseHeldLock(normalizedTargetPath, createdHeld, { force: true });
|
|
28723
|
+
throw err;
|
|
28724
|
+
}
|
|
28725
|
+
}
|
|
28726
|
+
const returnedHandle = context.handleForHeldLock(normalizedTargetPath, createdHeld);
|
|
28727
|
+
const interval = options.compromiseCheckIntervalMs;
|
|
28728
|
+
if (options.onCompromised && interval !== undefined && interval > 0) {
|
|
28729
|
+
createdHeld.compromiseTimer = setInterval(() => {
|
|
28730
|
+
void returnedHandle.verifyStillHeld().then((stillHeld) => {
|
|
28731
|
+
if (!stillHeld && createdHeld.compromiseTimer) {
|
|
28732
|
+
clearInterval(createdHeld.compromiseTimer);
|
|
28733
|
+
createdHeld.compromiseTimer = undefined;
|
|
28734
|
+
options.onCompromised?.({ lockPath, normalizedTargetPath });
|
|
28735
|
+
}
|
|
28736
|
+
});
|
|
28737
|
+
}, interval);
|
|
28738
|
+
createdHeld.compromiseTimer.unref();
|
|
28739
|
+
}
|
|
28740
|
+
return returnedHandle;
|
|
28741
|
+
}
|
|
28742
|
+
catch (err) {
|
|
28743
|
+
if (handle) {
|
|
28744
|
+
const failedSnapshot = { payload: null };
|
|
28745
|
+
try {
|
|
28746
|
+
failedSnapshot.stat = await handle.stat();
|
|
28747
|
+
}
|
|
28748
|
+
catch {
|
|
28749
|
+
// Best-effort cleanup of a failed exclusive create.
|
|
28750
|
+
}
|
|
28751
|
+
const current = context.held.get(normalizedTargetPath);
|
|
28752
|
+
if (current?.handle === handle) {
|
|
28753
|
+
context.held.delete(normalizedTargetPath);
|
|
28754
|
+
}
|
|
28755
|
+
await handle.close().catch(() => undefined);
|
|
28756
|
+
// The file may be empty or partial JSON, so remove by the identity
|
|
28757
|
+
// captured from our exclusive handle rather than by pathname alone.
|
|
28758
|
+
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot, {
|
|
28759
|
+
lockRoot: options.lockRoot,
|
|
28760
|
+
parsePayload: options.parsePayload,
|
|
28761
|
+
});
|
|
28762
|
+
}
|
|
28763
|
+
else if (createdSnapshot) {
|
|
28764
|
+
await removeSidecarLockIfUnchanged(lockPath, createdSnapshot, {
|
|
28765
|
+
lockRoot: options.lockRoot,
|
|
28766
|
+
parsePayload: options.parsePayload,
|
|
28767
|
+
});
|
|
28768
|
+
}
|
|
28769
|
+
if (lockFileCreateDenied && withinDenialBudget()) {
|
|
28770
|
+
await retryOrRethrowDenial(err);
|
|
28771
|
+
continue;
|
|
28772
|
+
}
|
|
28773
|
+
if (err.code !== "EEXIST") {
|
|
28774
|
+
throw err;
|
|
28775
|
+
}
|
|
28776
|
+
if (ownsReclaimGuard) {
|
|
28777
|
+
await releaseSidecarReclaimGuard(context.reclaimGuards, reclaimGuardPath);
|
|
28778
|
+
ownsReclaimGuard = false;
|
|
28779
|
+
continue;
|
|
28780
|
+
}
|
|
28781
|
+
const nowMs = Date.now();
|
|
28782
|
+
let snapshot;
|
|
28783
|
+
try {
|
|
28784
|
+
snapshot = await readSidecarLockSnapshot(lockPath, {
|
|
28785
|
+
lockRoot: options.lockRoot,
|
|
28786
|
+
parsePayload: options.parsePayload,
|
|
28787
|
+
rejectNonFile: true,
|
|
28788
|
+
});
|
|
28789
|
+
}
|
|
28790
|
+
catch (readErr) {
|
|
28791
|
+
if (!isTransientLockFileDenial(readErr, lockPath) || !withinDenialBudget())
|
|
28792
|
+
throw readErr;
|
|
28793
|
+
await retryOrRethrowDenial(readErr);
|
|
28794
|
+
continue;
|
|
28795
|
+
}
|
|
28796
|
+
if (!snapshot) {
|
|
28797
|
+
continue;
|
|
28798
|
+
}
|
|
28799
|
+
if (context.held.has(normalizedTargetPath)) {
|
|
28800
|
+
await waitForRetry();
|
|
28801
|
+
continue;
|
|
28802
|
+
}
|
|
28803
|
+
const shouldReclaim = options.shouldReclaim ?? defaultSidecarLockShouldReclaim;
|
|
28804
|
+
if (await shouldReclaim({
|
|
28805
|
+
lockPath,
|
|
28806
|
+
normalizedTargetPath,
|
|
28807
|
+
payload: snapshot?.payload ?? null,
|
|
28808
|
+
staleMs: options.staleMs,
|
|
28809
|
+
nowMs,
|
|
28810
|
+
heldByThisProcess: context.held.has(normalizedTargetPath),
|
|
28811
|
+
})) {
|
|
28812
|
+
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot, {
|
|
28813
|
+
lockRoot: options.lockRoot,
|
|
28814
|
+
parsePayload: options.parsePayload,
|
|
28815
|
+
}))) {
|
|
28816
|
+
continue;
|
|
28817
|
+
}
|
|
28818
|
+
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
28819
|
+
if (staleRecovery === "remove-if-unchanged") {
|
|
28820
|
+
if (!(await tryAcquireSidecarReclaimGuard(context.reclaimGuards, reclaimGuardPath))) {
|
|
28821
|
+
await waitForRetry();
|
|
28822
|
+
continue;
|
|
28823
|
+
}
|
|
28824
|
+
ownsReclaimGuard = true;
|
|
28825
|
+
const removal = await removeStaleSidecarLockIfAllowed({
|
|
28826
|
+
lockPath,
|
|
28827
|
+
normalizedTargetPath,
|
|
28828
|
+
snapshot,
|
|
28829
|
+
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
28830
|
+
lockRoot: options.lockRoot,
|
|
28831
|
+
parsePayload: options.parsePayload,
|
|
28832
|
+
});
|
|
28833
|
+
if (removal === "removed" || removal === "changed") {
|
|
28834
|
+
continue;
|
|
28835
|
+
}
|
|
28836
|
+
await releaseSidecarReclaimGuard(context.reclaimGuards, reclaimGuardPath);
|
|
28837
|
+
ownsReclaimGuard = false;
|
|
28838
|
+
}
|
|
28839
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
28840
|
+
code: "file_lock_stale",
|
|
28841
|
+
lockPath,
|
|
28842
|
+
normalizedTargetPath,
|
|
28843
|
+
});
|
|
28844
|
+
}
|
|
28845
|
+
await waitForRetry();
|
|
28846
|
+
}
|
|
28847
|
+
}
|
|
28848
|
+
}
|
|
28849
|
+
finally {
|
|
28850
|
+
if (ownsReclaimGuard) {
|
|
28851
|
+
await releaseSidecarReclaimGuard(context.reclaimGuards, reclaimGuardPath).catch(() => undefined);
|
|
28852
|
+
}
|
|
28853
|
+
}
|
|
28854
|
+
}
|
|
28855
|
+
|
|
28856
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-handle.js
|
|
28392
28857
|
|
|
28858
|
+
function createSidecarLockHandle(params) {
|
|
28859
|
+
let released = false;
|
|
28860
|
+
const release = async () => {
|
|
28861
|
+
if (released)
|
|
28862
|
+
return;
|
|
28863
|
+
released = true;
|
|
28864
|
+
await params.release();
|
|
28865
|
+
};
|
|
28866
|
+
return {
|
|
28867
|
+
lockPath: params.lockPath,
|
|
28868
|
+
normalizedTargetPath: params.normalizedTargetPath,
|
|
28869
|
+
verifyStillHeld: params.verifyStillHeld,
|
|
28870
|
+
release,
|
|
28871
|
+
[Symbol.asyncDispose]: release,
|
|
28872
|
+
};
|
|
28873
|
+
}
|
|
28874
|
+
function createHeldSidecarLockHandle(params) {
|
|
28875
|
+
return createSidecarLockHandle({
|
|
28876
|
+
lockPath: params.held.lockPath,
|
|
28877
|
+
normalizedTargetPath: params.normalizedTargetPath,
|
|
28878
|
+
verifyStillHeld: async () => await sidecarLockSnapshotStillPresent(params.held.lockPath, params.held.snapshot, {
|
|
28879
|
+
lockRoot: params.held.lockRoot,
|
|
28880
|
+
parsePayload: params.held.parsePayload,
|
|
28881
|
+
}),
|
|
28882
|
+
release: params.release,
|
|
28883
|
+
});
|
|
28884
|
+
}
|
|
28393
28885
|
|
|
28886
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
28394
28887
|
|
|
28395
28888
|
|
|
28396
28889
|
|
|
28397
28890
|
|
|
28398
28891
|
|
|
28399
28892
|
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
28893
|
+
const GLOBAL_CLEANUP_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistered");
|
|
28894
|
+
const GLOBAL_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockCleanupHandler");
|
|
28400
28895
|
function getGlobalManagers() {
|
|
28401
28896
|
const globalWithState = globalThis;
|
|
28402
28897
|
if (!globalWithState[GLOBAL_STATE_KEY]) {
|
|
@@ -28468,17 +28963,6 @@ function snapshotMatchesSync(lockPath, observed) {
|
|
|
28468
28963
|
}
|
|
28469
28964
|
}
|
|
28470
28965
|
}
|
|
28471
|
-
async function resolveNormalizedTargetPath(targetPath) {
|
|
28472
|
-
const resolved = external_node_path_.resolve(targetPath);
|
|
28473
|
-
const dir = external_node_path_.dirname(resolved);
|
|
28474
|
-
await promises_.mkdir(dir, { recursive: true });
|
|
28475
|
-
try {
|
|
28476
|
-
return external_node_path_.join(await promises_.realpath(dir), external_node_path_.basename(resolved));
|
|
28477
|
-
}
|
|
28478
|
-
catch {
|
|
28479
|
-
return resolved;
|
|
28480
|
-
}
|
|
28481
|
-
}
|
|
28482
28966
|
function releaseAllReclaimGuardsSync(state) {
|
|
28483
28967
|
for (const reclaimGuardPath of state.reclaimGuards) {
|
|
28484
28968
|
try {
|
|
@@ -28505,6 +28989,19 @@ function releaseAllLocksSync(state) {
|
|
|
28505
28989
|
}
|
|
28506
28990
|
releaseAllReclaimGuardsSync(state);
|
|
28507
28991
|
}
|
|
28992
|
+
function ensureGlobalExitCleanupRegistered() {
|
|
28993
|
+
const globalWithCleanup = globalThis;
|
|
28994
|
+
if (globalWithCleanup[GLOBAL_CLEANUP_KEY])
|
|
28995
|
+
return;
|
|
28996
|
+
globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
|
|
28997
|
+
const cleanup = () => {
|
|
28998
|
+
for (const state of getGlobalManagers().values()) {
|
|
28999
|
+
releaseAllLocksSync(state);
|
|
29000
|
+
}
|
|
29001
|
+
};
|
|
29002
|
+
globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
|
|
29003
|
+
process.on("exit", cleanup);
|
|
29004
|
+
}
|
|
28508
29005
|
async function releaseHeldLock(state, normalizedTargetPath, held, options = {}) {
|
|
28509
29006
|
const current = state.held.get(normalizedTargetPath);
|
|
28510
29007
|
if (current !== held) {
|
|
@@ -28553,217 +29050,18 @@ function handleForHeldLock(state, normalizedTargetPath, held) {
|
|
|
28553
29050
|
function createSidecarLockManager(key) {
|
|
28554
29051
|
const state = resolveManagerState(key);
|
|
28555
29052
|
function ensureExitCleanupRegistered() {
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
process.on("exit", () => releaseAllLocksSync(state));
|
|
28560
|
-
return;
|
|
28561
|
-
}
|
|
28562
|
-
if (!state.reclaimCleanupRegistered) {
|
|
28563
|
-
state.reclaimCleanupRegistered = true;
|
|
28564
|
-
process.on("exit", () => releaseAllReclaimGuardsSync(state));
|
|
28565
|
-
}
|
|
29053
|
+
state.cleanupRegistered = true;
|
|
29054
|
+
state.reclaimCleanupRegistered = true;
|
|
29055
|
+
ensureGlobalExitCleanupRegistered();
|
|
28566
29056
|
}
|
|
28567
29057
|
async function acquire(options) {
|
|
28568
|
-
|
|
28569
|
-
|
|
28570
|
-
|
|
28571
|
-
|
|
28572
|
-
|
|
28573
|
-
|
|
28574
|
-
|
|
28575
|
-
options.reentrantOwner === held.reentrantOwner) {
|
|
28576
|
-
held.refCount += 1;
|
|
28577
|
-
return handleForHeldLock(state, normalizedTargetPath, held);
|
|
28578
|
-
}
|
|
28579
|
-
const startedAt = Date.now();
|
|
28580
|
-
const retry = options.retry ?? {};
|
|
28581
|
-
const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
|
|
28582
|
-
const reclaimGuardPath = `${lockPath}.reclaim`;
|
|
28583
|
-
let ownsReclaimGuard = false;
|
|
28584
|
-
let attempt = 0;
|
|
28585
|
-
const waitForRetry = async () => {
|
|
28586
|
-
const elapsed = Date.now() - startedAt;
|
|
28587
|
-
if ((options.timeoutMs !== undefined &&
|
|
28588
|
-
options.timeoutMs !== Number.POSITIVE_INFINITY &&
|
|
28589
|
-
elapsed >= options.timeoutMs) ||
|
|
28590
|
-
(maxRetries !== undefined && attempt >= maxRetries)) {
|
|
28591
|
-
throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
28592
|
-
code: "file_lock_timeout",
|
|
28593
|
-
lockPath,
|
|
28594
|
-
normalizedTargetPath,
|
|
28595
|
-
});
|
|
28596
|
-
}
|
|
28597
|
-
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
28598
|
-
? Number.POSITIVE_INFINITY
|
|
28599
|
-
: Math.max(0, options.timeoutMs - elapsed);
|
|
28600
|
-
const delay = Math.min(computeSidecarLockDelayMs(retry, attempt), remaining);
|
|
28601
|
-
attempt += 1;
|
|
28602
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28603
|
-
};
|
|
28604
|
-
try {
|
|
28605
|
-
while (true) {
|
|
28606
|
-
if (!ownsReclaimGuard && (await sidecarReclaimGuardExists(reclaimGuardPath))) {
|
|
28607
|
-
await waitForRetry();
|
|
28608
|
-
continue;
|
|
28609
|
-
}
|
|
28610
|
-
let handle = null;
|
|
28611
|
-
try {
|
|
28612
|
-
const payload = await options.payload();
|
|
28613
|
-
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
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
|
-
}
|
|
28631
|
-
const snapshot = { raw, payload, stat: await handle.stat(), ownershipToken };
|
|
28632
|
-
const createdHeld = {
|
|
28633
|
-
refCount: 1,
|
|
28634
|
-
reentrantOwner: options.reentrantOwner,
|
|
28635
|
-
handle,
|
|
28636
|
-
lockPath,
|
|
28637
|
-
snapshot,
|
|
28638
|
-
acquiredAt: Date.now(),
|
|
28639
|
-
metadata: options.metadata ?? {},
|
|
28640
|
-
lockRoot: options.lockRoot,
|
|
28641
|
-
parsePayload: options.parsePayload,
|
|
28642
|
-
};
|
|
28643
|
-
state.held.set(normalizedTargetPath, createdHeld);
|
|
28644
|
-
if (ownsReclaimGuard) {
|
|
28645
|
-
try {
|
|
28646
|
-
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28647
|
-
ownsReclaimGuard = false;
|
|
28648
|
-
}
|
|
28649
|
-
catch (err) {
|
|
28650
|
-
await releaseHeldLock(state, normalizedTargetPath, createdHeld, { force: true });
|
|
28651
|
-
throw err;
|
|
28652
|
-
}
|
|
28653
|
-
}
|
|
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;
|
|
28669
|
-
}
|
|
28670
|
-
catch (err) {
|
|
28671
|
-
if (handle) {
|
|
28672
|
-
const failedSnapshot = { payload: null };
|
|
28673
|
-
try {
|
|
28674
|
-
failedSnapshot.stat = await handle.stat();
|
|
28675
|
-
}
|
|
28676
|
-
catch {
|
|
28677
|
-
// Best-effort cleanup of a failed exclusive create.
|
|
28678
|
-
}
|
|
28679
|
-
const current = state.held.get(normalizedTargetPath);
|
|
28680
|
-
if (current?.handle === handle) {
|
|
28681
|
-
state.held.delete(normalizedTargetPath);
|
|
28682
|
-
}
|
|
28683
|
-
// If payload serialization/write fails, the file may be empty or
|
|
28684
|
-
// partial JSON, so remove while our exclusive handle is still open.
|
|
28685
|
-
if (!options.lockRoot) {
|
|
28686
|
-
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28687
|
-
}
|
|
28688
|
-
await handle.close().catch(() => undefined);
|
|
28689
|
-
// Windows can refuse removing an open file; retry after close but
|
|
28690
|
-
// only if the path still points at the file identity we created.
|
|
28691
|
-
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot, {
|
|
28692
|
-
lockRoot: options.lockRoot,
|
|
28693
|
-
parsePayload: options.parsePayload,
|
|
28694
|
-
});
|
|
28695
|
-
}
|
|
28696
|
-
if (err.code !== "EEXIST") {
|
|
28697
|
-
throw err;
|
|
28698
|
-
}
|
|
28699
|
-
if (ownsReclaimGuard) {
|
|
28700
|
-
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28701
|
-
ownsReclaimGuard = false;
|
|
28702
|
-
continue;
|
|
28703
|
-
}
|
|
28704
|
-
const nowMs = Date.now();
|
|
28705
|
-
const snapshot = await readSidecarLockSnapshot(lockPath, {
|
|
28706
|
-
lockRoot: options.lockRoot,
|
|
28707
|
-
parsePayload: options.parsePayload,
|
|
28708
|
-
});
|
|
28709
|
-
if (!snapshot) {
|
|
28710
|
-
continue;
|
|
28711
|
-
}
|
|
28712
|
-
if (state.held.has(normalizedTargetPath)) {
|
|
28713
|
-
await waitForRetry();
|
|
28714
|
-
continue;
|
|
28715
|
-
}
|
|
28716
|
-
const shouldReclaim = options.shouldReclaim ?? defaultSidecarLockShouldReclaim;
|
|
28717
|
-
if (await shouldReclaim({
|
|
28718
|
-
lockPath,
|
|
28719
|
-
normalizedTargetPath,
|
|
28720
|
-
payload: snapshot?.payload ?? null,
|
|
28721
|
-
staleMs: options.staleMs,
|
|
28722
|
-
nowMs,
|
|
28723
|
-
heldByThisProcess: state.held.has(normalizedTargetPath),
|
|
28724
|
-
})) {
|
|
28725
|
-
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot, {
|
|
28726
|
-
lockRoot: options.lockRoot,
|
|
28727
|
-
parsePayload: options.parsePayload,
|
|
28728
|
-
}))) {
|
|
28729
|
-
continue;
|
|
28730
|
-
}
|
|
28731
|
-
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
28732
|
-
if (staleRecovery === "remove-if-unchanged") {
|
|
28733
|
-
if (!(await tryAcquireSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath))) {
|
|
28734
|
-
await waitForRetry();
|
|
28735
|
-
continue;
|
|
28736
|
-
}
|
|
28737
|
-
ownsReclaimGuard = true;
|
|
28738
|
-
const removal = await removeStaleSidecarLockIfAllowed({
|
|
28739
|
-
lockPath,
|
|
28740
|
-
normalizedTargetPath,
|
|
28741
|
-
snapshot,
|
|
28742
|
-
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
28743
|
-
lockRoot: options.lockRoot,
|
|
28744
|
-
parsePayload: options.parsePayload,
|
|
28745
|
-
});
|
|
28746
|
-
if (removal === "removed" || removal === "changed") {
|
|
28747
|
-
continue;
|
|
28748
|
-
}
|
|
28749
|
-
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28750
|
-
ownsReclaimGuard = false;
|
|
28751
|
-
}
|
|
28752
|
-
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
28753
|
-
code: "file_lock_stale",
|
|
28754
|
-
lockPath,
|
|
28755
|
-
normalizedTargetPath,
|
|
28756
|
-
});
|
|
28757
|
-
}
|
|
28758
|
-
await waitForRetry();
|
|
28759
|
-
}
|
|
28760
|
-
}
|
|
28761
|
-
}
|
|
28762
|
-
finally {
|
|
28763
|
-
if (ownsReclaimGuard) {
|
|
28764
|
-
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath).catch(() => undefined);
|
|
28765
|
-
}
|
|
28766
|
-
}
|
|
29058
|
+
return await acquireSidecarLock(options, {
|
|
29059
|
+
held: state.held,
|
|
29060
|
+
reclaimGuards: state.reclaimGuards,
|
|
29061
|
+
ensureExitCleanupRegistered,
|
|
29062
|
+
handleForHeldLock: (normalizedTargetPath, held) => handleForHeldLock(state, normalizedTargetPath, held),
|
|
29063
|
+
releaseHeldLock: async (normalizedTargetPath, held, releaseOptions) => await releaseHeldLock(state, normalizedTargetPath, held, releaseOptions),
|
|
29064
|
+
});
|
|
28767
29065
|
}
|
|
28768
29066
|
async function withLock(options, fn) {
|
|
28769
29067
|
const lock = await acquire(options);
|
|
@@ -28799,21 +29097,6 @@ async function withSidecarLock(targetPath, options, fn) {
|
|
|
28799
29097
|
return await manager.withLock({ ...acquireOptions, targetPath }, fn);
|
|
28800
29098
|
}
|
|
28801
29099
|
|
|
28802
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
|
|
28803
|
-
let test_hooks_fsSafeTestHooks;
|
|
28804
|
-
function allowFsSafeTestHooks() {
|
|
28805
|
-
return false || process.env.VITEST === "true";
|
|
28806
|
-
}
|
|
28807
|
-
function getFsSafeTestHooks() {
|
|
28808
|
-
return test_hooks_fsSafeTestHooks;
|
|
28809
|
-
}
|
|
28810
|
-
function __setFsSafeTestHooksForTest(hooks) {
|
|
28811
|
-
if (hooks && !allowFsSafeTestHooks()) {
|
|
28812
|
-
throw new Error("__setFsSafeTestHooksForTest is only available in tests");
|
|
28813
|
-
}
|
|
28814
|
-
test_hooks_fsSafeTestHooks = hooks;
|
|
28815
|
-
}
|
|
28816
|
-
|
|
28817
29100
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-write.js
|
|
28818
29101
|
|
|
28819
29102
|
|
|
@@ -28886,7 +29169,7 @@ async function runPinnedWriteHelper(params) {
|
|
|
28886
29169
|
return await runPinnedWriteFallback(params);
|
|
28887
29170
|
}
|
|
28888
29171
|
const native = getNativeBinding();
|
|
28889
|
-
if (native
|
|
29172
|
+
if (native) {
|
|
28890
29173
|
return await runPinnedWriteNative(native, params);
|
|
28891
29174
|
}
|
|
28892
29175
|
return await runPinnedWriteFallback(params);
|
|
@@ -28924,6 +29207,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
28924
29207
|
parentPath = await mkdirPathComponentsWithGuards({
|
|
28925
29208
|
rootReal: params.rootPath,
|
|
28926
29209
|
targetPath: parentPath,
|
|
29210
|
+
beforeComponent: async (componentPath) => await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("mkdir", componentPath),
|
|
28927
29211
|
});
|
|
28928
29212
|
}
|
|
28929
29213
|
const parentGuard = params.mkdir
|
|
@@ -29063,77 +29347,135 @@ async function runPinnedWriteFallback(params) {
|
|
|
29063
29347
|
return { dev: targetStat.dev, ino: targetStat.ino };
|
|
29064
29348
|
}
|
|
29065
29349
|
|
|
29066
|
-
|
|
29067
|
-
|
|
29068
|
-
|
|
29350
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/error-detail.js
|
|
29351
|
+
const UNSAFE_ERROR_DETAIL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/gu;
|
|
29352
|
+
function formatErrorDetail(value) {
|
|
29353
|
+
return value.replace(UNSAFE_ERROR_DETAIL_CHARACTERS, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
29354
|
+
}
|
|
29069
29355
|
|
|
29356
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path-symlink.js
|
|
29070
29357
|
|
|
29071
29358
|
|
|
29072
29359
|
|
|
29073
|
-
|
|
29074
|
-
|
|
29360
|
+
|
|
29361
|
+
|
|
29362
|
+
|
|
29363
|
+
function normalizeSymlinkResolutionError(error) {
|
|
29364
|
+
if (isSymlinkOpenError(error)) {
|
|
29365
|
+
throw new errors_FsSafeError("symlink", "symlink path could not be resolved", {
|
|
29366
|
+
cause: error instanceof Error ? error : undefined,
|
|
29367
|
+
});
|
|
29368
|
+
}
|
|
29369
|
+
if (!path_isNotFoundPathError(error))
|
|
29370
|
+
throw error;
|
|
29075
29371
|
}
|
|
29076
|
-
async function
|
|
29372
|
+
async function resolveSymlinkHopPath(symlinkPath) {
|
|
29077
29373
|
try {
|
|
29078
|
-
await promises_.
|
|
29079
|
-
return true;
|
|
29374
|
+
return external_node_path_.resolve(await promises_.realpath(symlinkPath));
|
|
29080
29375
|
}
|
|
29081
29376
|
catch (error) {
|
|
29082
|
-
|
|
29083
|
-
|
|
29084
|
-
|
|
29085
|
-
throw error;
|
|
29377
|
+
normalizeSymlinkResolutionError(error);
|
|
29378
|
+
const linkTarget = await promises_.readlink(symlinkPath);
|
|
29379
|
+
return resolvePathViaExistingAncestor(external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget));
|
|
29086
29380
|
}
|
|
29087
29381
|
}
|
|
29088
|
-
|
|
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
|
-
}
|
|
29382
|
+
function root_path_symlink_resolveSymlinkHopPathSync(symlinkPath) {
|
|
29103
29383
|
try {
|
|
29104
|
-
|
|
29105
|
-
return missingSuffix.length === 0
|
|
29106
|
-
? resolvedAncestor
|
|
29107
|
-
: external_node_path_.resolve(resolvedAncestor, ...missingSuffix);
|
|
29384
|
+
return path.resolve(fs.realpathSync(symlinkPath));
|
|
29108
29385
|
}
|
|
29109
|
-
catch {
|
|
29110
|
-
|
|
29386
|
+
catch (error) {
|
|
29387
|
+
normalizeSymlinkResolutionError(error);
|
|
29388
|
+
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
29389
|
+
return resolvePathViaExistingAncestorSync(path.resolve(path.dirname(symlinkPath), linkTarget));
|
|
29111
29390
|
}
|
|
29112
29391
|
}
|
|
29113
|
-
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29117
|
-
|
|
29118
|
-
|
|
29119
|
-
|
|
29120
|
-
|
|
29121
|
-
|
|
29122
|
-
|
|
29123
|
-
|
|
29392
|
+
|
|
29393
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/safe-path-segment.js
|
|
29394
|
+
|
|
29395
|
+
const SAFE_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
|
|
29396
|
+
const SAFE_DOT_PREFIX_PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
29397
|
+
// Windows treats "C:name" as relative to the drive's current directory even
|
|
29398
|
+
// though path.win32.isAbsolute() reports false.
|
|
29399
|
+
const DRIVE_RELATIVE_PREFIX = /^[A-Za-z]:(?![\\/])/;
|
|
29400
|
+
const HYPHEN_CHAR_CODE = 0x2d;
|
|
29401
|
+
function safe_path_segment_isDriveRelativePath(value) {
|
|
29402
|
+
return DRIVE_RELATIVE_PREFIX.test(value);
|
|
29403
|
+
}
|
|
29404
|
+
function assertNoDriveRelativePathSegments(value, label) {
|
|
29405
|
+
if (value.split("/").some(safe_path_segment_isDriveRelativePath)) {
|
|
29406
|
+
throw new errors_FsSafeError("invalid-path", `${label} must not contain a drive letter`);
|
|
29124
29407
|
}
|
|
29125
|
-
|
|
29126
|
-
|
|
29408
|
+
return value;
|
|
29409
|
+
}
|
|
29410
|
+
function trimHyphenEdges(value) {
|
|
29411
|
+
let start = 0;
|
|
29412
|
+
let end = value.length;
|
|
29413
|
+
while (start < end && value.charCodeAt(start) === HYPHEN_CHAR_CODE) {
|
|
29414
|
+
start += 1;
|
|
29127
29415
|
}
|
|
29128
|
-
|
|
29129
|
-
|
|
29130
|
-
return missingSuffix.length === 0
|
|
29131
|
-
? resolvedAncestor
|
|
29132
|
-
: path.resolve(resolvedAncestor, ...missingSuffix);
|
|
29416
|
+
while (end > start && value.charCodeAt(end - 1) === HYPHEN_CHAR_CODE) {
|
|
29417
|
+
end -= 1;
|
|
29133
29418
|
}
|
|
29134
|
-
|
|
29135
|
-
|
|
29419
|
+
return start === 0 && end === value.length ? value : value.slice(start, end);
|
|
29420
|
+
}
|
|
29421
|
+
function isSafePathSegment(segment, options = {}) {
|
|
29422
|
+
return (segment !== "" &&
|
|
29423
|
+
segment !== "." &&
|
|
29424
|
+
segment !== ".." &&
|
|
29425
|
+
!segment.includes("/") &&
|
|
29426
|
+
!segment.includes("\\") &&
|
|
29427
|
+
!segment.includes("\0") &&
|
|
29428
|
+
(options.allowDotPrefix === true || !segment.startsWith(".")) &&
|
|
29429
|
+
(options.allowDotPrefix === true
|
|
29430
|
+
? SAFE_DOT_PREFIX_PATH_SEGMENT_PATTERN.test(segment)
|
|
29431
|
+
: SAFE_PATH_SEGMENT_PATTERN.test(segment)));
|
|
29432
|
+
}
|
|
29433
|
+
function assertSafePathSegment(segment, options = {}) {
|
|
29434
|
+
// Validate the exact value callers will later join into paths; trimming here
|
|
29435
|
+
// would let whitespace-padded ids pass and then be used verbatim.
|
|
29436
|
+
if (!isSafePathSegment(segment, options)) {
|
|
29437
|
+
throw new FsSafeError("invalid-path", `${options.label ?? "path segment"} must be a safe path segment`);
|
|
29438
|
+
}
|
|
29439
|
+
return segment;
|
|
29440
|
+
}
|
|
29441
|
+
function sanitizeSafePathSegment(value, fallback, options = {}) {
|
|
29442
|
+
const sanitized = value
|
|
29443
|
+
.trim()
|
|
29444
|
+
.replace(/[\\/]+/g, "-")
|
|
29445
|
+
.replace(/\0/g, "")
|
|
29446
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-");
|
|
29447
|
+
const trimmed = trimHyphenEdges(sanitized);
|
|
29448
|
+
if (isSafePathSegment(trimmed, options)) {
|
|
29449
|
+
return trimmed;
|
|
29450
|
+
}
|
|
29451
|
+
return assertSafePathSegment(fallback, { ...options, label: "fallback path segment" });
|
|
29452
|
+
}
|
|
29453
|
+
function assertSafePathPrefix(prefix, options = {}) {
|
|
29454
|
+
// Prefixes are often derived from safe filenames. Normalize harmless
|
|
29455
|
+
// filename characters first, but still reject real path-control bytes.
|
|
29456
|
+
if (prefix.includes("/") || prefix.includes("\\") || prefix.includes("\0")) {
|
|
29457
|
+
return assertSafePathSegment(prefix, {
|
|
29458
|
+
allowDotPrefix: true,
|
|
29459
|
+
...options,
|
|
29460
|
+
label: options.label ?? "path prefix",
|
|
29461
|
+
});
|
|
29136
29462
|
}
|
|
29463
|
+
return assertSafePathSegment(prefix.replace(/[^A-Za-z0-9._-]+/g, "-"), {
|
|
29464
|
+
allowDotPrefix: true,
|
|
29465
|
+
...options,
|
|
29466
|
+
label: options.label ?? "path prefix",
|
|
29467
|
+
});
|
|
29468
|
+
}
|
|
29469
|
+
|
|
29470
|
+
// EXTERNAL MODULE: external "node:os"
|
|
29471
|
+
var external_node_os_ = __webpack_require__(8161);
|
|
29472
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/short-path.js
|
|
29473
|
+
|
|
29474
|
+
|
|
29475
|
+
function shortPath(value) {
|
|
29476
|
+
const home = external_node_os_.homedir();
|
|
29477
|
+
const shortened = value.startsWith(home) ? `~${value.slice(home.length)}` : value;
|
|
29478
|
+
return formatErrorDetail(shortened);
|
|
29137
29479
|
}
|
|
29138
29480
|
|
|
29139
29481
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-path.js
|
|
@@ -29144,6 +29486,10 @@ function root_path_existing_resolvePathViaExistingAncestorSync(targetPath) {
|
|
|
29144
29486
|
|
|
29145
29487
|
|
|
29146
29488
|
|
|
29489
|
+
|
|
29490
|
+
|
|
29491
|
+
|
|
29492
|
+
|
|
29147
29493
|
const ROOT_PATH_ALIAS_POLICIES = {
|
|
29148
29494
|
strict: Object.freeze({
|
|
29149
29495
|
allowFinalSymlinkForUnlink: false,
|
|
@@ -29155,11 +29501,20 @@ const ROOT_PATH_ALIAS_POLICIES = {
|
|
|
29155
29501
|
}),
|
|
29156
29502
|
};
|
|
29157
29503
|
async function resolveRootPath(params) {
|
|
29504
|
+
try {
|
|
29505
|
+
return await resolveRootPathInternal(params);
|
|
29506
|
+
}
|
|
29507
|
+
catch (error) {
|
|
29508
|
+
throw sanitizeRootPathError(error);
|
|
29509
|
+
}
|
|
29510
|
+
}
|
|
29511
|
+
async function resolveRootPathInternal(params) {
|
|
29512
|
+
assertValidRootPathInputs(params);
|
|
29158
29513
|
const rootPath = external_node_path_.resolve(params.rootPath);
|
|
29159
29514
|
const absolutePath = external_node_path_.resolve(params.absolutePath);
|
|
29160
29515
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
29161
29516
|
? external_node_path_.resolve(params.rootCanonicalPath)
|
|
29162
|
-
: await
|
|
29517
|
+
: await resolvePathViaExistingAncestor(rootPath);
|
|
29163
29518
|
const context = createBoundaryResolutionContext({
|
|
29164
29519
|
resolveParams: params,
|
|
29165
29520
|
rootPath,
|
|
@@ -29185,6 +29540,15 @@ async function resolveRootPath(params) {
|
|
|
29185
29540
|
});
|
|
29186
29541
|
}
|
|
29187
29542
|
function resolveRootPathSync(params) {
|
|
29543
|
+
try {
|
|
29544
|
+
return resolveRootPathSyncInternal(params);
|
|
29545
|
+
}
|
|
29546
|
+
catch (error) {
|
|
29547
|
+
throw sanitizeRootPathError(error);
|
|
29548
|
+
}
|
|
29549
|
+
}
|
|
29550
|
+
function resolveRootPathSyncInternal(params) {
|
|
29551
|
+
assertValidRootPathInputs(params);
|
|
29188
29552
|
const rootPath = path.resolve(params.rootPath);
|
|
29189
29553
|
const absolutePath = path.resolve(params.absolutePath);
|
|
29190
29554
|
const rootCanonicalPath = params.rootCanonicalPath
|
|
@@ -29214,6 +29578,29 @@ function resolveRootPathSync(params) {
|
|
|
29214
29578
|
rootCanonicalPath: context.rootCanonicalPath,
|
|
29215
29579
|
});
|
|
29216
29580
|
}
|
|
29581
|
+
function sanitizeRootPathError(error) {
|
|
29582
|
+
if (error instanceof Error) {
|
|
29583
|
+
error.message = formatErrorDetail(error.message);
|
|
29584
|
+
}
|
|
29585
|
+
return error;
|
|
29586
|
+
}
|
|
29587
|
+
function assertValidRootPathInputs(params) {
|
|
29588
|
+
path_assertNoNulPathInput(params.rootPath, "root path contains a NUL byte");
|
|
29589
|
+
path_assertNoNulPathInput(params.absolutePath, "absolute path contains a NUL byte");
|
|
29590
|
+
assertNoEmbeddedDriveRelativeSegment(params.rootPath, "root path");
|
|
29591
|
+
assertNoEmbeddedDriveRelativeSegment(params.absolutePath, "absolute path");
|
|
29592
|
+
if (params.rootCanonicalPath !== undefined) {
|
|
29593
|
+
path_assertNoNulPathInput(params.rootCanonicalPath, "canonical root path contains a NUL byte");
|
|
29594
|
+
assertNoEmbeddedDriveRelativeSegment(params.rootCanonicalPath, "canonical root path");
|
|
29595
|
+
}
|
|
29596
|
+
}
|
|
29597
|
+
function assertNoEmbeddedDriveRelativeSegment(filePath, label) {
|
|
29598
|
+
if (process.platform !== "win32") {
|
|
29599
|
+
return;
|
|
29600
|
+
}
|
|
29601
|
+
const root = external_node_path_.parse(filePath).root;
|
|
29602
|
+
assertNoDriveRelativePathSegments(filePath.slice(root.length).replaceAll("\\", "/"), label);
|
|
29603
|
+
}
|
|
29217
29604
|
function isPromiseLike(value) {
|
|
29218
29605
|
return Boolean(value &&
|
|
29219
29606
|
(typeof value === "object" || typeof value === "function") &&
|
|
@@ -29232,6 +29619,15 @@ function createLexicalTraversalState(params) {
|
|
|
29232
29619
|
preserveFinalSymlink: false,
|
|
29233
29620
|
};
|
|
29234
29621
|
}
|
|
29622
|
+
function createLexicalTraversalContext(params) {
|
|
29623
|
+
return {
|
|
29624
|
+
state: createLexicalTraversalState(params),
|
|
29625
|
+
resolveParams: params.params,
|
|
29626
|
+
rootPath: params.rootPath,
|
|
29627
|
+
rootCanonicalPath: params.rootCanonicalPath,
|
|
29628
|
+
absolutePath: params.absolutePath,
|
|
29629
|
+
};
|
|
29630
|
+
}
|
|
29235
29631
|
function splitTraversalSegments(value) {
|
|
29236
29632
|
return value
|
|
29237
29633
|
.split(process.platform === "win32" ? /[\\/]+/ : /\/+/)
|
|
@@ -29255,149 +29651,97 @@ function rawPathRelativeToRoot(rootPath, candidatePath) {
|
|
|
29255
29651
|
: candidatePrefix === rootWithSep;
|
|
29256
29652
|
return prefixMatches ? candidate.slice(rootWithSep.length) : undefined;
|
|
29257
29653
|
}
|
|
29258
|
-
function assertLexicalCursorInsideBoundary(
|
|
29654
|
+
function assertLexicalCursorInsideBoundary(context, candidatePath) {
|
|
29259
29655
|
assertInsideBoundary({
|
|
29260
|
-
boundaryLabel:
|
|
29261
|
-
rootCanonicalPath:
|
|
29262
|
-
candidatePath
|
|
29263
|
-
absolutePath:
|
|
29656
|
+
boundaryLabel: context.resolveParams.boundaryLabel,
|
|
29657
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
29658
|
+
candidatePath,
|
|
29659
|
+
absolutePath: context.absolutePath,
|
|
29264
29660
|
});
|
|
29265
29661
|
}
|
|
29266
|
-
function applyMissingSuffixToCanonicalCursor(
|
|
29267
|
-
const missingSuffix =
|
|
29662
|
+
function applyMissingSuffixToCanonicalCursor(context, missingFromIndex) {
|
|
29663
|
+
const missingSuffix = context.state.segments.slice(missingFromIndex);
|
|
29268
29664
|
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
|
-
});
|
|
29665
|
+
advanceCanonicalCursorForSegment(context, segment);
|
|
29276
29666
|
}
|
|
29277
29667
|
}
|
|
29278
|
-
function advanceCanonicalCursorForSegment(
|
|
29279
|
-
|
|
29280
|
-
assertLexicalCursorInsideBoundary(
|
|
29281
|
-
params: params.params,
|
|
29282
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29283
|
-
candidatePath: params.state.canonicalCursor,
|
|
29284
|
-
absolutePath: params.absolutePath,
|
|
29285
|
-
});
|
|
29668
|
+
function advanceCanonicalCursorForSegment(context, segment) {
|
|
29669
|
+
context.state.canonicalCursor = external_node_path_.resolve(context.state.canonicalCursor, segment);
|
|
29670
|
+
assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
|
|
29286
29671
|
}
|
|
29287
|
-
function finalizeLexicalResolution(
|
|
29288
|
-
assertLexicalCursorInsideBoundary(
|
|
29289
|
-
params: params.params,
|
|
29290
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29291
|
-
candidatePath: params.state.canonicalCursor,
|
|
29292
|
-
absolutePath: params.absolutePath,
|
|
29293
|
-
});
|
|
29672
|
+
function finalizeLexicalResolution(context, kind) {
|
|
29673
|
+
assertLexicalCursorInsideBoundary(context, context.state.canonicalCursor);
|
|
29294
29674
|
return buildResolvedRootPath({
|
|
29295
|
-
absolutePath:
|
|
29296
|
-
canonicalPath:
|
|
29297
|
-
rootPath:
|
|
29298
|
-
rootCanonicalPath:
|
|
29299
|
-
kind
|
|
29675
|
+
absolutePath: context.absolutePath,
|
|
29676
|
+
canonicalPath: context.state.canonicalCursor,
|
|
29677
|
+
rootPath: context.rootPath,
|
|
29678
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
29679
|
+
kind,
|
|
29300
29680
|
});
|
|
29301
29681
|
}
|
|
29302
|
-
function handleLexicalLstatFailure(
|
|
29303
|
-
if (!path_isNotFoundPathError(
|
|
29682
|
+
function handleLexicalLstatFailure(context, error, missingFromIndex) {
|
|
29683
|
+
if (!path_isNotFoundPathError(error)) {
|
|
29304
29684
|
return false;
|
|
29305
29685
|
}
|
|
29306
|
-
applyMissingSuffixToCanonicalCursor(
|
|
29307
|
-
state: params.state,
|
|
29308
|
-
missingFromIndex: params.missingFromIndex,
|
|
29309
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29310
|
-
params: params.resolveParams,
|
|
29311
|
-
absolutePath: params.absolutePath,
|
|
29312
|
-
});
|
|
29686
|
+
applyMissingSuffixToCanonicalCursor(context, missingFromIndex);
|
|
29313
29687
|
return true;
|
|
29314
29688
|
}
|
|
29315
|
-
function handleLexicalStatReadFailure(
|
|
29316
|
-
if (handleLexicalLstatFailure({
|
|
29317
|
-
error: params.error,
|
|
29318
|
-
state: params.state,
|
|
29319
|
-
missingFromIndex: params.missingFromIndex,
|
|
29320
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29321
|
-
resolveParams: params.resolveParams,
|
|
29322
|
-
absolutePath: params.absolutePath,
|
|
29323
|
-
})) {
|
|
29689
|
+
function handleLexicalStatReadFailure(context, error, missingFromIndex) {
|
|
29690
|
+
if (handleLexicalLstatFailure(context, error, missingFromIndex)) {
|
|
29324
29691
|
return null;
|
|
29325
29692
|
}
|
|
29326
|
-
throw
|
|
29693
|
+
throw error;
|
|
29327
29694
|
}
|
|
29328
|
-
function handleLexicalStatDisposition(params) {
|
|
29695
|
+
function handleLexicalStatDisposition(context, params) {
|
|
29329
29696
|
if (!params.isSymbolicLink) {
|
|
29330
|
-
advanceCanonicalCursorForSegment(
|
|
29331
|
-
state: params.state,
|
|
29332
|
-
segment: params.segment,
|
|
29333
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29334
|
-
params: params.resolveParams,
|
|
29335
|
-
absolutePath: params.absolutePath,
|
|
29336
|
-
});
|
|
29697
|
+
advanceCanonicalCursorForSegment(context, params.segment);
|
|
29337
29698
|
return "continue";
|
|
29338
29699
|
}
|
|
29339
|
-
if (
|
|
29340
|
-
|
|
29341
|
-
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
|
|
29345
|
-
params: params.resolveParams,
|
|
29346
|
-
absolutePath: params.absolutePath,
|
|
29347
|
-
});
|
|
29700
|
+
if (context.resolveParams.rejectSymlinks === true && params.isLast) {
|
|
29701
|
+
throw new errors_FsSafeError("symlink", "symlink path component not allowed");
|
|
29702
|
+
}
|
|
29703
|
+
if (context.state.allowFinalSymlink && params.isLast) {
|
|
29704
|
+
context.state.preserveFinalSymlink = true;
|
|
29705
|
+
advanceCanonicalCursorForSegment(context, params.segment);
|
|
29348
29706
|
return "break";
|
|
29349
29707
|
}
|
|
29350
29708
|
return "resolve-link";
|
|
29351
29709
|
}
|
|
29352
|
-
function applyResolvedSymlinkHop(
|
|
29353
|
-
if (!path_isPathInside(
|
|
29710
|
+
function applyResolvedSymlinkHop(context, linkCanonical) {
|
|
29711
|
+
if (!path_isPathInside(context.rootCanonicalPath, linkCanonical)) {
|
|
29354
29712
|
throw symlinkEscapeError({
|
|
29355
|
-
boundaryLabel:
|
|
29356
|
-
rootCanonicalPath:
|
|
29357
|
-
symlinkPath:
|
|
29713
|
+
boundaryLabel: context.resolveParams.boundaryLabel,
|
|
29714
|
+
rootCanonicalPath: context.rootCanonicalPath,
|
|
29715
|
+
symlinkPath: context.state.lexicalCursor,
|
|
29358
29716
|
});
|
|
29359
29717
|
}
|
|
29360
|
-
|
|
29361
|
-
|
|
29718
|
+
context.state.canonicalCursor = linkCanonical;
|
|
29719
|
+
context.state.lexicalCursor = linkCanonical;
|
|
29362
29720
|
}
|
|
29363
|
-
function readLexicalStat(params) {
|
|
29721
|
+
function readLexicalStat(context, params) {
|
|
29364
29722
|
try {
|
|
29365
|
-
const stat = params.read(
|
|
29723
|
+
const stat = params.read(context.state.lexicalCursor);
|
|
29366
29724
|
if (isPromiseLike(stat)) {
|
|
29367
|
-
return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure(
|
|
29725
|
+
return Promise.resolve(stat).catch((error) => handleLexicalStatReadFailure(context, error, params.missingFromIndex));
|
|
29368
29726
|
}
|
|
29369
29727
|
return stat;
|
|
29370
29728
|
}
|
|
29371
29729
|
catch (error) {
|
|
29372
|
-
return handleLexicalStatReadFailure(
|
|
29730
|
+
return handleLexicalStatReadFailure(context, error, params.missingFromIndex);
|
|
29373
29731
|
}
|
|
29374
29732
|
}
|
|
29375
|
-
function resolveAndApplySymlinkHop(params) {
|
|
29376
|
-
const linkCanonical = params.resolveLinkCanonical(
|
|
29733
|
+
function resolveAndApplySymlinkHop(context, params) {
|
|
29734
|
+
const linkCanonical = params.resolveLinkCanonical(context.state.lexicalCursor);
|
|
29377
29735
|
if (isPromiseLike(linkCanonical)) {
|
|
29378
|
-
return Promise.resolve(linkCanonical).then((value) =>
|
|
29379
|
-
|
|
29380
|
-
|
|
29381
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29382
|
-
boundaryLabel: params.boundaryLabel,
|
|
29383
|
-
}));
|
|
29736
|
+
return Promise.resolve(linkCanonical).then((value) => {
|
|
29737
|
+
applyResolvedSymlinkHop(context, value);
|
|
29738
|
+
});
|
|
29384
29739
|
}
|
|
29385
|
-
applyResolvedSymlinkHop(
|
|
29386
|
-
state: params.state,
|
|
29387
|
-
linkCanonical,
|
|
29388
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29389
|
-
boundaryLabel: params.boundaryLabel,
|
|
29390
|
-
});
|
|
29740
|
+
applyResolvedSymlinkHop(context, linkCanonical);
|
|
29391
29741
|
}
|
|
29392
|
-
function applyParentTraversalStep(
|
|
29393
|
-
|
|
29394
|
-
advanceCanonicalCursorForSegment(
|
|
29395
|
-
state: params.state,
|
|
29396
|
-
segment: "..",
|
|
29397
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29398
|
-
params: params.resolveParams,
|
|
29399
|
-
absolutePath: params.absolutePath,
|
|
29400
|
-
});
|
|
29742
|
+
function applyParentTraversalStep(context) {
|
|
29743
|
+
context.state.lexicalCursor = external_node_path_.resolve(context.state.lexicalCursor, "..");
|
|
29744
|
+
advanceCanonicalCursorForSegment(context, "..");
|
|
29401
29745
|
}
|
|
29402
29746
|
function* iterateLexicalTraversal(state) {
|
|
29403
29747
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
@@ -29407,32 +29751,22 @@ function* iterateLexicalTraversal(state) {
|
|
|
29407
29751
|
}
|
|
29408
29752
|
}
|
|
29409
29753
|
async function resolveRootPathLexicalAsync(params) {
|
|
29410
|
-
const
|
|
29411
|
-
const
|
|
29412
|
-
state,
|
|
29413
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29414
|
-
resolveParams: params.params,
|
|
29415
|
-
absolutePath: params.absolutePath,
|
|
29416
|
-
};
|
|
29754
|
+
const context = createLexicalTraversalContext(params);
|
|
29755
|
+
const { state } = context;
|
|
29417
29756
|
for (const { idx, segment, isLast } of iterateLexicalTraversal(state)) {
|
|
29418
29757
|
if (segment === "..") {
|
|
29419
|
-
applyParentTraversalStep(
|
|
29420
|
-
...sharedStepParams,
|
|
29421
|
-
resolveParams: params.params,
|
|
29422
|
-
});
|
|
29758
|
+
applyParentTraversalStep(context);
|
|
29423
29759
|
continue;
|
|
29424
29760
|
}
|
|
29425
29761
|
state.lexicalCursor = external_node_path_.join(state.lexicalCursor, segment);
|
|
29426
|
-
const stat = await readLexicalStat({
|
|
29427
|
-
...sharedStepParams,
|
|
29762
|
+
const stat = await readLexicalStat(context, {
|
|
29428
29763
|
missingFromIndex: idx,
|
|
29429
29764
|
read: (cursor) => promises_.lstat(cursor),
|
|
29430
29765
|
});
|
|
29431
29766
|
if (!stat) {
|
|
29432
29767
|
break;
|
|
29433
29768
|
}
|
|
29434
|
-
const disposition = handleLexicalStatDisposition({
|
|
29435
|
-
...sharedStepParams,
|
|
29769
|
+
const disposition = handleLexicalStatDisposition(context, {
|
|
29436
29770
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
29437
29771
|
segment,
|
|
29438
29772
|
isLast,
|
|
@@ -29443,41 +29777,29 @@ async function resolveRootPathLexicalAsync(params) {
|
|
|
29443
29777
|
if (disposition === "break") {
|
|
29444
29778
|
break;
|
|
29445
29779
|
}
|
|
29446
|
-
await resolveAndApplySymlinkHop({
|
|
29447
|
-
state,
|
|
29448
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29449
|
-
boundaryLabel: params.params.boundaryLabel,
|
|
29780
|
+
await resolveAndApplySymlinkHop(context, {
|
|
29450
29781
|
resolveLinkCanonical: (cursor) => resolveSymlinkHopPath(cursor),
|
|
29451
29782
|
});
|
|
29783
|
+
if (context.resolveParams.rejectSymlinks === true) {
|
|
29784
|
+
throw new errors_FsSafeError("symlink", "symlink path component not allowed");
|
|
29785
|
+
}
|
|
29452
29786
|
}
|
|
29453
29787
|
const kind = await getPathKind(state.canonicalCursor, state.preserveFinalSymlink);
|
|
29454
|
-
return finalizeLexicalResolution(
|
|
29455
|
-
...params,
|
|
29456
|
-
state,
|
|
29457
|
-
kind,
|
|
29458
|
-
});
|
|
29788
|
+
return finalizeLexicalResolution(context, kind);
|
|
29459
29789
|
}
|
|
29460
29790
|
function resolveRootPathLexicalSync(params) {
|
|
29461
|
-
const
|
|
29791
|
+
const context = createLexicalTraversalContext(params);
|
|
29792
|
+
const { state } = context;
|
|
29462
29793
|
for (let idx = 0; idx < state.segments.length; idx += 1) {
|
|
29463
29794
|
const segment = state.segments[idx] ?? "";
|
|
29464
29795
|
const isLast = idx === state.segments.length - 1;
|
|
29465
29796
|
if (segment === "..") {
|
|
29466
|
-
applyParentTraversalStep(
|
|
29467
|
-
state,
|
|
29468
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29469
|
-
resolveParams: params.params,
|
|
29470
|
-
absolutePath: params.absolutePath,
|
|
29471
|
-
});
|
|
29797
|
+
applyParentTraversalStep(context);
|
|
29472
29798
|
continue;
|
|
29473
29799
|
}
|
|
29474
29800
|
state.lexicalCursor = path.join(state.lexicalCursor, segment);
|
|
29475
|
-
const maybeStat = readLexicalStat({
|
|
29476
|
-
state,
|
|
29801
|
+
const maybeStat = readLexicalStat(context, {
|
|
29477
29802
|
missingFromIndex: idx,
|
|
29478
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29479
|
-
resolveParams: params.params,
|
|
29480
|
-
absolutePath: params.absolutePath,
|
|
29481
29803
|
read: (cursor) => fs.lstatSync(cursor),
|
|
29482
29804
|
});
|
|
29483
29805
|
if (isPromiseLike(maybeStat)) {
|
|
@@ -29487,14 +29809,10 @@ function resolveRootPathLexicalSync(params) {
|
|
|
29487
29809
|
if (!stat) {
|
|
29488
29810
|
break;
|
|
29489
29811
|
}
|
|
29490
|
-
const disposition = handleLexicalStatDisposition({
|
|
29491
|
-
state,
|
|
29812
|
+
const disposition = handleLexicalStatDisposition(context, {
|
|
29492
29813
|
isSymbolicLink: stat.isSymbolicLink(),
|
|
29493
29814
|
segment,
|
|
29494
29815
|
isLast,
|
|
29495
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29496
|
-
resolveParams: params.params,
|
|
29497
|
-
absolutePath: params.absolutePath,
|
|
29498
29816
|
});
|
|
29499
29817
|
if (disposition === "continue") {
|
|
29500
29818
|
continue;
|
|
@@ -29502,22 +29820,18 @@ function resolveRootPathLexicalSync(params) {
|
|
|
29502
29820
|
if (disposition === "break") {
|
|
29503
29821
|
break;
|
|
29504
29822
|
}
|
|
29505
|
-
const maybeApplied = resolveAndApplySymlinkHop({
|
|
29506
|
-
state,
|
|
29507
|
-
rootCanonicalPath: params.rootCanonicalPath,
|
|
29508
|
-
boundaryLabel: params.params.boundaryLabel,
|
|
29823
|
+
const maybeApplied = resolveAndApplySymlinkHop(context, {
|
|
29509
29824
|
resolveLinkCanonical: (cursor) => resolveSymlinkHopPathSync(cursor),
|
|
29510
29825
|
});
|
|
29511
29826
|
if (isPromiseLike(maybeApplied)) {
|
|
29512
29827
|
throw new Error("Unexpected async symlink resolution");
|
|
29513
29828
|
}
|
|
29829
|
+
if (context.resolveParams.rejectSymlinks === true) {
|
|
29830
|
+
throw new FsSafeError("symlink", "symlink path component not allowed");
|
|
29831
|
+
}
|
|
29514
29832
|
}
|
|
29515
29833
|
const kind = getPathKindSync(state.canonicalCursor, state.preserveFinalSymlink);
|
|
29516
|
-
return finalizeLexicalResolution(
|
|
29517
|
-
...params,
|
|
29518
|
-
state,
|
|
29519
|
-
kind,
|
|
29520
|
-
});
|
|
29834
|
+
return finalizeLexicalResolution(context, kind);
|
|
29521
29835
|
}
|
|
29522
29836
|
function resolveCanonicalOutsideLexicalPath(params) {
|
|
29523
29837
|
return params.outsideLexicalCanonicalPath ?? params.absolutePath;
|
|
@@ -29581,7 +29895,7 @@ async function resolveOutsideLexicalCanonicalPathAsync(params) {
|
|
|
29581
29895
|
if (path_isPathInside(params.rootPath, params.absolutePath)) {
|
|
29582
29896
|
return undefined;
|
|
29583
29897
|
}
|
|
29584
|
-
return await
|
|
29898
|
+
return await resolvePathViaExistingAncestor(params.absolutePath);
|
|
29585
29899
|
}
|
|
29586
29900
|
function resolveOutsideLexicalCanonicalPathSync(params) {
|
|
29587
29901
|
if (isPathInside(params.rootPath, params.absolutePath)) {
|
|
@@ -29688,39 +30002,6 @@ function pathEscapeError(params) {
|
|
|
29688
30002
|
function symlinkEscapeError(params) {
|
|
29689
30003
|
return new Error(`Symlink escapes ${params.boundaryLabel} (${shortPath(params.rootCanonicalPath)}): ${shortPath(params.symlinkPath)}`);
|
|
29690
30004
|
}
|
|
29691
|
-
function shortPath(value) {
|
|
29692
|
-
const home = external_node_os_.homedir();
|
|
29693
|
-
if (value.startsWith(home)) {
|
|
29694
|
-
return `~${value.slice(home.length)}`;
|
|
29695
|
-
}
|
|
29696
|
-
return value;
|
|
29697
|
-
}
|
|
29698
|
-
async function resolveSymlinkHopPath(symlinkPath) {
|
|
29699
|
-
try {
|
|
29700
|
-
return external_node_path_.resolve(await promises_.realpath(symlinkPath));
|
|
29701
|
-
}
|
|
29702
|
-
catch (error) {
|
|
29703
|
-
if (!path_isNotFoundPathError(error)) {
|
|
29704
|
-
throw error;
|
|
29705
|
-
}
|
|
29706
|
-
const linkTarget = await promises_.readlink(symlinkPath);
|
|
29707
|
-
const linkAbsolute = external_node_path_.resolve(external_node_path_.dirname(symlinkPath), linkTarget);
|
|
29708
|
-
return root_path_existing_resolvePathViaExistingAncestor(linkAbsolute);
|
|
29709
|
-
}
|
|
29710
|
-
}
|
|
29711
|
-
function resolveSymlinkHopPathSync(symlinkPath) {
|
|
29712
|
-
try {
|
|
29713
|
-
return path.resolve(fs.realpathSync(symlinkPath));
|
|
29714
|
-
}
|
|
29715
|
-
catch (error) {
|
|
29716
|
-
if (!isNotFoundPathError(error)) {
|
|
29717
|
-
throw error;
|
|
29718
|
-
}
|
|
29719
|
-
const linkTarget = fs.readlinkSync(symlinkPath);
|
|
29720
|
-
const linkAbsolute = path.resolve(path.dirname(symlinkPath), linkTarget);
|
|
29721
|
-
return resolvePathViaExistingAncestorSync(linkAbsolute);
|
|
29722
|
-
}
|
|
29723
|
-
}
|
|
29724
30005
|
|
|
29725
30006
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-policy.js
|
|
29726
30007
|
|
|
@@ -29764,14 +30045,8 @@ async function assertNoHardlinkedFinalPath(params) {
|
|
|
29764
30045
|
return;
|
|
29765
30046
|
}
|
|
29766
30047
|
if (stat.nlink > 1) {
|
|
29767
|
-
throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${
|
|
29768
|
-
}
|
|
29769
|
-
}
|
|
29770
|
-
function path_policy_shortPath(value) {
|
|
29771
|
-
if (value.startsWith(external_node_os_.homedir())) {
|
|
29772
|
-
return `~${value.slice(external_node_os_.homedir().length)}`;
|
|
30048
|
+
throw new Error(`Hardlinked path is not allowed under ${params.boundaryLabel} (${shortPath(params.root)}): ${shortPath(params.filePath)}`);
|
|
29773
30049
|
}
|
|
29774
|
-
return value;
|
|
29775
30050
|
}
|
|
29776
30051
|
|
|
29777
30052
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/string-coerce.js
|
|
@@ -29853,6 +30128,10 @@ function hasNonEmptyString(value) {
|
|
|
29853
30128
|
|
|
29854
30129
|
|
|
29855
30130
|
const ENCODED_FILE_URL_SEPARATOR_RE = /%(?:2f|5c)/i;
|
|
30131
|
+
const FILE_URL_PREFIX_RE = /^file:\/\//i;
|
|
30132
|
+
function isFileUrl(input) {
|
|
30133
|
+
return FILE_URL_PREFIX_RE.test(input);
|
|
30134
|
+
}
|
|
29856
30135
|
function isLocalFileUrlHost(hostname) {
|
|
29857
30136
|
const normalized = normalizeLowercaseStringOrEmpty(hostname);
|
|
29858
30137
|
return normalized === "" || normalized === "localhost";
|
|
@@ -29883,7 +30162,7 @@ function assertNoWindowsNetworkPath(filePath, label = "Path") {
|
|
|
29883
30162
|
throw new Error(`${label} cannot use Windows network paths: ${filePath}`);
|
|
29884
30163
|
}
|
|
29885
30164
|
}
|
|
29886
|
-
function safeFileURLToPath(fileUrl) {
|
|
30165
|
+
function safeFileURLToPath(fileUrl, platform = process.platform) {
|
|
29887
30166
|
let parsed;
|
|
29888
30167
|
try {
|
|
29889
30168
|
parsed = new external_node_url_.URL(fileUrl);
|
|
@@ -29900,13 +30179,15 @@ function safeFileURLToPath(fileUrl) {
|
|
|
29900
30179
|
if (hasEncodedFileUrlSeparator(parsed.pathname)) {
|
|
29901
30180
|
throw new Error(`file:// URLs cannot encode path separators: ${fileUrl}`);
|
|
29902
30181
|
}
|
|
29903
|
-
const filePath = (0,external_node_url_.fileURLToPath)(parsed);
|
|
29904
|
-
|
|
30182
|
+
const filePath = (0,external_node_url_.fileURLToPath)(parsed, { windows: platform === "win32" });
|
|
30183
|
+
if (isWindowsNetworkPath(filePath, platform)) {
|
|
30184
|
+
throw new Error(`Local file URL cannot use Windows network paths: ${filePath}`);
|
|
30185
|
+
}
|
|
29905
30186
|
return filePath;
|
|
29906
30187
|
}
|
|
29907
|
-
function trySafeFileURLToPath(fileUrl) {
|
|
30188
|
+
function trySafeFileURLToPath(fileUrl, platform = process.platform) {
|
|
29908
30189
|
try {
|
|
29909
|
-
return safeFileURLToPath(fileUrl);
|
|
30190
|
+
return safeFileURLToPath(fileUrl, platform);
|
|
29910
30191
|
}
|
|
29911
30192
|
catch {
|
|
29912
30193
|
return undefined;
|
|
@@ -29916,7 +30197,7 @@ function basenameFromMediaSource(source) {
|
|
|
29916
30197
|
if (!source) {
|
|
29917
30198
|
return undefined;
|
|
29918
30199
|
}
|
|
29919
|
-
if (source
|
|
30200
|
+
if (isFileUrl(source)) {
|
|
29920
30201
|
const filePath = trySafeFileURLToPath(source);
|
|
29921
30202
|
return filePath ? path.basename(filePath) || undefined : undefined;
|
|
29922
30203
|
}
|
|
@@ -30001,11 +30282,11 @@ function trimTrailingWindowsIgnoredChars(value) {
|
|
|
30001
30282
|
}
|
|
30002
30283
|
return end === value.length ? value : value.slice(0, end);
|
|
30003
30284
|
}
|
|
30004
|
-
function candidateReadPaths(filePath) {
|
|
30005
|
-
if (!filePath
|
|
30285
|
+
function candidateReadPaths(filePath, platform) {
|
|
30286
|
+
if (!isFileUrl(filePath)) {
|
|
30006
30287
|
return [filePath];
|
|
30007
30288
|
}
|
|
30008
|
-
const parsed = trySafeFileURLToPath(filePath);
|
|
30289
|
+
const parsed = trySafeFileURLToPath(filePath, platform);
|
|
30009
30290
|
return parsed === undefined ? [filePath] : [filePath, parsed];
|
|
30010
30291
|
}
|
|
30011
30292
|
function normalizePosixPath(filePath, cwd) {
|
|
@@ -30048,7 +30329,7 @@ function matchWindowsDeviceReadPath(filePath) {
|
|
|
30048
30329
|
}
|
|
30049
30330
|
function matchUnsafeDeviceReadPath(filePath, options = {}) {
|
|
30050
30331
|
const platform = options.platform ?? process.platform;
|
|
30051
|
-
for (const candidate of candidateReadPaths(filePath)) {
|
|
30332
|
+
for (const candidate of candidateReadPaths(filePath, platform)) {
|
|
30052
30333
|
const match = platform === "win32"
|
|
30053
30334
|
? matchWindowsDeviceReadPath(candidate)
|
|
30054
30335
|
: matchPosixDeviceReadPath(candidate, options.cwd);
|
|
@@ -30085,23 +30366,6 @@ async function read_opened_file_readOpenedFileSafely(params) {
|
|
|
30085
30366
|
};
|
|
30086
30367
|
}
|
|
30087
30368
|
|
|
30088
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/path-stat.js
|
|
30089
|
-
function pathStatFromStats(stat) {
|
|
30090
|
-
return {
|
|
30091
|
-
dev: Number(stat.dev),
|
|
30092
|
-
gid: Number(stat.gid),
|
|
30093
|
-
ino: Number(stat.ino),
|
|
30094
|
-
isDirectory: stat.isDirectory(),
|
|
30095
|
-
isFile: stat.isFile(),
|
|
30096
|
-
isSymbolicLink: stat.isSymbolicLink(),
|
|
30097
|
-
mode: stat.mode,
|
|
30098
|
-
mtimeMs: stat.mtimeMs,
|
|
30099
|
-
nlink: stat.nlink,
|
|
30100
|
-
size: stat.size,
|
|
30101
|
-
uid: stat.uid,
|
|
30102
|
-
};
|
|
30103
|
-
}
|
|
30104
|
-
|
|
30105
30369
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/home-dir.js
|
|
30106
30370
|
|
|
30107
30371
|
|
|
@@ -30224,10 +30488,19 @@ function resolveOsHomeRelativePath(input, opts) {
|
|
|
30224
30488
|
|
|
30225
30489
|
|
|
30226
30490
|
|
|
30491
|
+
|
|
30492
|
+
|
|
30493
|
+
|
|
30227
30494
|
const ensureTrailingSep = (value) => value.endsWith(external_node_path_.sep) ? value : value + external_node_path_.sep;
|
|
30228
30495
|
function assertValidRootRelativePath(relativePath) {
|
|
30229
30496
|
path_assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
|
|
30230
30497
|
}
|
|
30498
|
+
function assertValidRootDestinationPath(relativePath) {
|
|
30499
|
+
assertValidRootRelativePath(relativePath);
|
|
30500
|
+
if (safe_path_segment_isDriveRelativePath(relativePath)) {
|
|
30501
|
+
throw new errors_FsSafeError("invalid-path", "relative path must not start with a drive letter");
|
|
30502
|
+
}
|
|
30503
|
+
}
|
|
30231
30504
|
let cachedHomePath;
|
|
30232
30505
|
async function expandRelativePathWithHome(relativePath) {
|
|
30233
30506
|
const rawHome = process.env.HOME || process.env.USERPROFILE || external_node_os_.homedir();
|
|
@@ -30246,12 +30519,14 @@ async function expandRelativePathWithHome(relativePath) {
|
|
|
30246
30519
|
async function resolveRootContext(rootDir) {
|
|
30247
30520
|
path_assertNoNulPathInput(rootDir, "root dir contains a NUL byte");
|
|
30248
30521
|
let rootReal;
|
|
30522
|
+
let rootIdentity;
|
|
30249
30523
|
try {
|
|
30250
30524
|
rootReal = await promises_.realpath(rootDir);
|
|
30251
30525
|
const rootStat = await promises_.stat(rootReal);
|
|
30252
30526
|
if (!rootStat.isDirectory()) {
|
|
30253
30527
|
throw new errors_FsSafeError("invalid-path", "root dir is not a directory");
|
|
30254
30528
|
}
|
|
30529
|
+
rootIdentity = { dev: rootStat.dev, ino: rootStat.ino };
|
|
30255
30530
|
}
|
|
30256
30531
|
catch (err) {
|
|
30257
30532
|
if (err instanceof errors_FsSafeError) {
|
|
@@ -30264,16 +30539,37 @@ async function resolveRootContext(rootDir) {
|
|
|
30264
30539
|
}
|
|
30265
30540
|
return {
|
|
30266
30541
|
rootDir: external_node_path_.resolve(rootDir),
|
|
30542
|
+
rootIdentity,
|
|
30267
30543
|
rootReal,
|
|
30268
30544
|
rootWithSep: ensureTrailingSep(rootReal),
|
|
30269
30545
|
};
|
|
30270
30546
|
}
|
|
30547
|
+
async function assertRootIdentityCurrent(root) {
|
|
30548
|
+
let current;
|
|
30549
|
+
try {
|
|
30550
|
+
current = await promises_.lstat(root.rootReal);
|
|
30551
|
+
}
|
|
30552
|
+
catch (error) {
|
|
30553
|
+
throw new errors_FsSafeError("path-mismatch", "root path changed during operation", {
|
|
30554
|
+
cause: error instanceof Error ? error : undefined,
|
|
30555
|
+
});
|
|
30556
|
+
}
|
|
30557
|
+
if (current.isSymbolicLink() ||
|
|
30558
|
+
!current.isDirectory() ||
|
|
30559
|
+
!file_identity_sameFileIdentity(current, root.rootIdentity)) {
|
|
30560
|
+
throw new errors_FsSafeError("path-mismatch", "root path changed during operation");
|
|
30561
|
+
}
|
|
30562
|
+
}
|
|
30271
30563
|
async function resolvePathInRoot(root, relativePath, options) {
|
|
30272
30564
|
assertValidRootRelativePath(relativePath);
|
|
30565
|
+
await assertRootIdentityCurrent(root);
|
|
30273
30566
|
const expanded = await expandRelativePathWithHome(relativePath);
|
|
30274
30567
|
const resolved = external_node_path_.resolve(root.rootWithSep, expanded);
|
|
30275
30568
|
if (!path_isPathInside(root.rootWithSep, resolved)) {
|
|
30276
|
-
throw
|
|
30569
|
+
throw outsideWorkspaceError();
|
|
30570
|
+
}
|
|
30571
|
+
if (options?.rejectUnsafeDeviceReads === true) {
|
|
30572
|
+
assertNoUnsafeDeviceReadPath(resolved);
|
|
30277
30573
|
}
|
|
30278
30574
|
const rawAbsolutePath = external_node_path_.isAbsolute(expanded)
|
|
30279
30575
|
? expanded
|
|
@@ -30285,9 +30581,18 @@ async function resolvePathInRoot(root, relativePath, options) {
|
|
|
30285
30581
|
rootCanonicalPath: root.rootReal,
|
|
30286
30582
|
boundaryLabel: "root",
|
|
30287
30583
|
policy: options?.allowFinalSymlink ? ROOT_PATH_ALIAS_POLICIES.unlinkTarget : undefined,
|
|
30584
|
+
rejectSymlinks: options?.rejectSymlinks,
|
|
30288
30585
|
});
|
|
30289
30586
|
}
|
|
30290
30587
|
catch (error) {
|
|
30588
|
+
if (error instanceof errors_FsSafeError && error.code === "symlink") {
|
|
30589
|
+
throw error;
|
|
30590
|
+
}
|
|
30591
|
+
if (hasNodeErrorCode(error, "ENAMETOOLONG")) {
|
|
30592
|
+
throw new errors_FsSafeError("invalid-path", "relative path is too long", {
|
|
30593
|
+
cause: error instanceof Error ? error : undefined,
|
|
30594
|
+
});
|
|
30595
|
+
}
|
|
30291
30596
|
const code = options?.aliasErrorCode ?? "outside-workspace";
|
|
30292
30597
|
throw new errors_FsSafeError(code, code === "path-alias" ? "path alias escape blocked" : "file is outside workspace root", {
|
|
30293
30598
|
cause: error instanceof Error ? error : undefined,
|
|
@@ -30299,29 +30604,6 @@ async function resolvePathWithinRoot(params) {
|
|
|
30299
30604
|
return await resolvePathInRoot(await resolveRootContext(params.rootDir), params.relativePath);
|
|
30300
30605
|
}
|
|
30301
30606
|
|
|
30302
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/root-errors.js
|
|
30303
|
-
|
|
30304
|
-
|
|
30305
|
-
function isAlreadyExistsError(error) {
|
|
30306
|
-
return hasNodeErrorCode(error, "EEXIST") || /File exists|EEXIST/i.test(String(error));
|
|
30307
|
-
}
|
|
30308
|
-
function normalizePinnedWriteError(error) {
|
|
30309
|
-
if (error instanceof errors_FsSafeError) {
|
|
30310
|
-
return error;
|
|
30311
|
-
}
|
|
30312
|
-
return new errors_FsSafeError("invalid-path", "path is not a regular file under root", {
|
|
30313
|
-
cause: error instanceof Error ? error : undefined,
|
|
30314
|
-
});
|
|
30315
|
-
}
|
|
30316
|
-
function normalizePinnedPathError(error) {
|
|
30317
|
-
if (error instanceof errors_FsSafeError) {
|
|
30318
|
-
return error;
|
|
30319
|
-
}
|
|
30320
|
-
return new errors_FsSafeError("path-alias", "path is not under root", {
|
|
30321
|
-
cause: error instanceof Error ? error : undefined,
|
|
30322
|
-
});
|
|
30323
|
-
}
|
|
30324
|
-
|
|
30325
30607
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
|
|
30326
30608
|
function stringifyJsonDocument(value, replacer, space) {
|
|
30327
30609
|
const text = JSON.stringify(value, replacer, space);
|
|
@@ -30356,6 +30638,17 @@ function limitEntry(relativePath) {
|
|
|
30356
30638
|
return { relativePath, kind: "truncated", size: 0 };
|
|
30357
30639
|
}
|
|
30358
30640
|
async function* walkRoot(root, relativePath, options) {
|
|
30641
|
+
if (!["skip", "follow-within-root"].includes(options.symlinkPolicy)) {
|
|
30642
|
+
throw new TypeError(`invalid root walk symlink policy: ${String(options.symlinkPolicy)}`);
|
|
30643
|
+
}
|
|
30644
|
+
if (options.limitBehavior !== undefined &&
|
|
30645
|
+
!["truncate", "throw"].includes(options.limitBehavior)) {
|
|
30646
|
+
throw new TypeError(`invalid root walk limit behavior: ${String(options.limitBehavior)}`);
|
|
30647
|
+
}
|
|
30648
|
+
if (options.onDirectoryError !== undefined &&
|
|
30649
|
+
!["throw", "skip-and-report"].includes(options.onDirectoryError)) {
|
|
30650
|
+
throw new TypeError(`invalid root walk directory error behavior: ${String(options.onDirectoryError)}`);
|
|
30651
|
+
}
|
|
30359
30652
|
const maxDepth = validateBudget("maxDepth", options.maxDepth);
|
|
30360
30653
|
const maxEntries = validateBudget("maxEntries", options.maxEntries);
|
|
30361
30654
|
const visitedDirectories = new Set();
|
|
@@ -30383,17 +30676,21 @@ async function* walkRoot(root, relativePath, options) {
|
|
|
30383
30676
|
return;
|
|
30384
30677
|
}
|
|
30385
30678
|
visitedDirectories.add(resolvedDirectory.canonicalPath);
|
|
30679
|
+
options.signal?.throwIfAborted();
|
|
30386
30680
|
const listingDirectory = external_node_path_.relative(root.rootReal, resolvedDirectory.canonicalPath)
|
|
30387
30681
|
.split(external_node_path_.sep)
|
|
30388
30682
|
.join(external_node_path_.posix.sep);
|
|
30389
30683
|
entries = await root.list(listingDirectory, { withFileTypes: true });
|
|
30390
30684
|
}
|
|
30391
30685
|
catch (error) {
|
|
30686
|
+
// Cancellation is never a recoverable directory read failure.
|
|
30687
|
+
options.signal?.throwIfAborted();
|
|
30392
30688
|
if ((options.onDirectoryError ?? "throw") === "throw")
|
|
30393
30689
|
throw error;
|
|
30394
30690
|
yield { relativePath: directory, kind: "directory-error", size: 0, error };
|
|
30395
30691
|
return;
|
|
30396
30692
|
}
|
|
30693
|
+
options.signal?.throwIfAborted();
|
|
30397
30694
|
for (const entry of entries) {
|
|
30398
30695
|
options.signal?.throwIfAborted();
|
|
30399
30696
|
const child = directory
|
|
@@ -30574,11 +30871,11 @@ function logWarn(message) {
|
|
|
30574
30871
|
}
|
|
30575
30872
|
}
|
|
30576
30873
|
const SUPPORTS_NOFOLLOW = process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants;
|
|
30577
|
-
const NONBLOCK_OPEN_FLAG = "O_NONBLOCK" in external_node_fs_.constants ? external_node_fs_.constants.O_NONBLOCK : 0;
|
|
30578
|
-
const OPEN_READ_FLAGS = external_node_fs_.constants.O_RDONLY |
|
|
30579
|
-
|
|
30580
|
-
|
|
30581
|
-
const
|
|
30874
|
+
const NONBLOCK_OPEN_FLAG = process.platform !== "win32" && "O_NONBLOCK" in external_node_fs_.constants ? external_node_fs_.constants.O_NONBLOCK : 0;
|
|
30875
|
+
const OPEN_READ_FLAGS = external_node_fs_.constants.O_RDONLY |
|
|
30876
|
+
(SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0) |
|
|
30877
|
+
NONBLOCK_OPEN_FLAG;
|
|
30878
|
+
const OPEN_READ_FOLLOW_FLAGS = external_node_fs_.constants.O_RDONLY | NONBLOCK_OPEN_FLAG;
|
|
30582
30879
|
const OPEN_WRITE_EXISTING_FLAGS = external_node_fs_.constants.O_WRONLY | (SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
30583
30880
|
const OPEN_WRITE_CREATE_FLAGS = external_node_fs_.constants.O_WRONLY |
|
|
30584
30881
|
external_node_fs_.constants.O_CREAT |
|
|
@@ -30591,6 +30888,21 @@ const OPEN_APPEND_CREATE_FLAGS = external_node_fs_.constants.O_RDWR |
|
|
|
30591
30888
|
external_node_fs_.constants.O_EXCL |
|
|
30592
30889
|
(SUPPORTS_NOFOLLOW ? external_node_fs_.constants.O_NOFOLLOW : 0);
|
|
30593
30890
|
const DEFAULT_ROOT_MAX_BYTES = 16 * 1024 * 1024;
|
|
30891
|
+
function pathStatFromStats(stat) {
|
|
30892
|
+
return {
|
|
30893
|
+
dev: Number(stat.dev),
|
|
30894
|
+
gid: Number(stat.gid),
|
|
30895
|
+
ino: Number(stat.ino),
|
|
30896
|
+
isDirectory: stat.isDirectory(),
|
|
30897
|
+
isFile: stat.isFile(),
|
|
30898
|
+
isSymbolicLink: stat.isSymbolicLink(),
|
|
30899
|
+
mode: stat.mode,
|
|
30900
|
+
mtimeMs: stat.mtimeMs,
|
|
30901
|
+
nlink: stat.nlink,
|
|
30902
|
+
size: stat.size,
|
|
30903
|
+
uid: stat.uid,
|
|
30904
|
+
};
|
|
30905
|
+
}
|
|
30594
30906
|
function openResult(params) {
|
|
30595
30907
|
return {
|
|
30596
30908
|
handle: params.handle,
|
|
@@ -30603,11 +30915,15 @@ function openResult(params) {
|
|
|
30603
30915
|
async function openVerifiedLocalFile(filePath, options) {
|
|
30604
30916
|
assertNoUnsafeDeviceReadPath(filePath);
|
|
30605
30917
|
const fsSafeTestHooks = getFsSafeTestHooks();
|
|
30918
|
+
let preOpenStat;
|
|
30606
30919
|
// Reject directories before opening so we never surface EISDIR to callers (e.g. tool
|
|
30607
30920
|
// results that get sent to messaging channels). See openclaw/openclaw#31186.
|
|
30608
30921
|
try {
|
|
30609
|
-
|
|
30610
|
-
if (
|
|
30922
|
+
preOpenStat = await promises_.lstat(filePath);
|
|
30923
|
+
if (preOpenStat.isSymbolicLink() && options?.symlinks !== "follow-within-root") {
|
|
30924
|
+
throw new errors_FsSafeError("symlink", "symlink not allowed");
|
|
30925
|
+
}
|
|
30926
|
+
if (!preOpenStat.isFile() && !preOpenStat.isSymbolicLink()) {
|
|
30611
30927
|
throw new errors_FsSafeError("not-file", "not a file");
|
|
30612
30928
|
}
|
|
30613
30929
|
await fsSafeTestHooks?.afterPreOpenLstat?.(filePath);
|
|
@@ -30621,12 +30937,8 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
30621
30937
|
let handle;
|
|
30622
30938
|
try {
|
|
30623
30939
|
const openFlags = options?.symlinks === "follow-within-root"
|
|
30624
|
-
?
|
|
30625
|
-
|
|
30626
|
-
: OPEN_READ_FOLLOW_FLAGS
|
|
30627
|
-
: options?.nonBlockingRead
|
|
30628
|
-
? OPEN_READ_NONBLOCK_FLAGS
|
|
30629
|
-
: OPEN_READ_FLAGS;
|
|
30940
|
+
? OPEN_READ_FOLLOW_FLAGS
|
|
30941
|
+
: OPEN_READ_FLAGS;
|
|
30630
30942
|
await fsSafeTestHooks?.beforeOpen?.(filePath, openFlags);
|
|
30631
30943
|
handle = await promises_.open(filePath, openFlags);
|
|
30632
30944
|
try {
|
|
@@ -30639,7 +30951,7 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
30639
30951
|
}
|
|
30640
30952
|
catch (err) {
|
|
30641
30953
|
if (path_isNotFoundPathError(err)) {
|
|
30642
|
-
throw
|
|
30954
|
+
throw fileNotFoundError();
|
|
30643
30955
|
}
|
|
30644
30956
|
if (isSymlinkOpenError(err)) {
|
|
30645
30957
|
throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
|
|
@@ -30655,8 +30967,13 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
30655
30967
|
if (!stat.isFile()) {
|
|
30656
30968
|
throw new errors_FsSafeError("not-file", "not a file");
|
|
30657
30969
|
}
|
|
30970
|
+
if (preOpenStat &&
|
|
30971
|
+
!preOpenStat.isSymbolicLink() &&
|
|
30972
|
+
!file_identity_sameFileIdentity(stat, preOpenStat)) {
|
|
30973
|
+
throw new errors_FsSafeError("path-mismatch", "path changed before open");
|
|
30974
|
+
}
|
|
30658
30975
|
if (options?.hardlinks === "reject" && stat.nlink > 1) {
|
|
30659
|
-
throw
|
|
30976
|
+
throw hardlinkedPathNotAllowedError();
|
|
30660
30977
|
}
|
|
30661
30978
|
if (options?.symlinks === "follow-within-root") {
|
|
30662
30979
|
const pathStat = await promises_.stat(filePath);
|
|
@@ -30676,7 +30993,7 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
30676
30993
|
const realPath = await resolveOpenedFileRealPathForHandle(handle, filePath);
|
|
30677
30994
|
const realStat = await promises_.stat(realPath);
|
|
30678
30995
|
if (options?.hardlinks === "reject" && realStat.nlink > 1) {
|
|
30679
|
-
throw
|
|
30996
|
+
throw hardlinkedPathNotAllowedError();
|
|
30680
30997
|
}
|
|
30681
30998
|
if (!file_identity_sameFileIdentity(stat, realStat)) {
|
|
30682
30999
|
throw new errors_FsSafeError("path-mismatch", "path mismatch");
|
|
@@ -30689,17 +31006,19 @@ async function openVerifiedLocalFile(filePath, options) {
|
|
|
30689
31006
|
throw err;
|
|
30690
31007
|
}
|
|
30691
31008
|
if (path_isNotFoundPathError(err)) {
|
|
30692
|
-
throw
|
|
31009
|
+
throw fileNotFoundError();
|
|
30693
31010
|
}
|
|
30694
31011
|
throw err;
|
|
30695
31012
|
}
|
|
30696
31013
|
}
|
|
30697
31014
|
class RootHandle {
|
|
31015
|
+
rootIdentity;
|
|
30698
31016
|
rootDir;
|
|
30699
31017
|
rootReal;
|
|
30700
31018
|
rootWithSep;
|
|
30701
31019
|
defaults;
|
|
30702
31020
|
constructor(context, defaults = {}) {
|
|
31021
|
+
this.rootIdentity = context.rootIdentity;
|
|
30703
31022
|
this.rootDir = context.rootDir;
|
|
30704
31023
|
this.rootReal = context.rootReal;
|
|
30705
31024
|
this.rootWithSep = context.rootWithSep;
|
|
@@ -30708,11 +31027,19 @@ class RootHandle {
|
|
|
30708
31027
|
get context() {
|
|
30709
31028
|
return {
|
|
30710
31029
|
rootDir: this.rootDir,
|
|
31030
|
+
rootIdentity: this.rootIdentity,
|
|
30711
31031
|
rootReal: this.rootReal,
|
|
30712
31032
|
rootWithSep: this.rootWithSep,
|
|
30713
31033
|
};
|
|
30714
31034
|
}
|
|
31035
|
+
mutationOptions(options) {
|
|
31036
|
+
return {
|
|
31037
|
+
...options,
|
|
31038
|
+
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31039
|
+
};
|
|
31040
|
+
}
|
|
30715
31041
|
async resolve(relativePath) {
|
|
31042
|
+
assertValidRootDestinationPath(relativePath);
|
|
30716
31043
|
return (await resolvePathInRoot(this.context, relativePath, { allowFinalSymlink: true })).resolved;
|
|
30717
31044
|
}
|
|
30718
31045
|
async open(relativePath, options = {}) {
|
|
@@ -30752,67 +31079,67 @@ class RootHandle {
|
|
|
30752
31079
|
};
|
|
30753
31080
|
}
|
|
30754
31081
|
async openWritable(relativePath, options = {}) {
|
|
31082
|
+
assertValidRootDestinationPath(relativePath);
|
|
30755
31083
|
const writeMode = options.writeMode ?? "replace";
|
|
30756
31084
|
return await openWritableFileInRoot(this.context, {
|
|
30757
31085
|
relativePath,
|
|
30758
31086
|
mkdir: this.defaults.mkdir,
|
|
30759
31087
|
mode: this.defaults.mode,
|
|
30760
|
-
...options,
|
|
30761
|
-
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31088
|
+
...this.mutationOptions(options),
|
|
30762
31089
|
append: writeMode === "append",
|
|
30763
31090
|
truncateExisting: writeMode === "replace",
|
|
30764
31091
|
});
|
|
30765
31092
|
}
|
|
30766
31093
|
async append(relativePath, data, options = {}) {
|
|
31094
|
+
assertValidRootDestinationPath(relativePath);
|
|
30767
31095
|
await appendFileInRoot(this.context, {
|
|
30768
31096
|
relativePath,
|
|
30769
31097
|
data,
|
|
30770
31098
|
mkdir: this.defaults.mkdir,
|
|
30771
31099
|
mode: this.defaults.mode,
|
|
30772
|
-
...options,
|
|
30773
|
-
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31100
|
+
...this.mutationOptions(options),
|
|
30774
31101
|
});
|
|
30775
31102
|
}
|
|
30776
31103
|
async remove(relativePath, options = {}) {
|
|
30777
31104
|
assertValidRootRelativePath(relativePath);
|
|
30778
31105
|
await removePathInRoot(this.context, {
|
|
30779
31106
|
relativePath,
|
|
30780
|
-
|
|
31107
|
+
...this.mutationOptions(options),
|
|
30781
31108
|
});
|
|
30782
31109
|
}
|
|
30783
31110
|
async mkdir(relativePath, options = {}) {
|
|
30784
|
-
|
|
31111
|
+
assertValidRootDestinationPath(relativePath);
|
|
30785
31112
|
await mkdirPathInRoot(this.context, {
|
|
30786
31113
|
relativePath,
|
|
30787
|
-
|
|
31114
|
+
...this.mutationOptions(options),
|
|
30788
31115
|
});
|
|
30789
31116
|
}
|
|
30790
31117
|
async ensureRoot(options = {}) {
|
|
30791
31118
|
await mkdirPathInRoot(this.context, {
|
|
30792
31119
|
relativePath: "",
|
|
30793
31120
|
allowRoot: true,
|
|
30794
|
-
|
|
31121
|
+
...this.mutationOptions(options),
|
|
30795
31122
|
});
|
|
30796
31123
|
}
|
|
30797
31124
|
async write(relativePath, data, options = {}) {
|
|
31125
|
+
assertValidRootDestinationPath(relativePath);
|
|
30798
31126
|
await writeFileInRoot(this.context, {
|
|
30799
31127
|
relativePath,
|
|
30800
31128
|
data,
|
|
30801
31129
|
mkdir: this.defaults.mkdir,
|
|
30802
31130
|
mode: this.defaults.mode,
|
|
30803
31131
|
renameIdentity: this.defaults.renameIdentity,
|
|
30804
|
-
...options,
|
|
30805
|
-
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31132
|
+
...this.mutationOptions(options),
|
|
30806
31133
|
});
|
|
30807
31134
|
}
|
|
30808
31135
|
async create(relativePath, data, options = {}) {
|
|
31136
|
+
assertValidRootDestinationPath(relativePath);
|
|
30809
31137
|
await writeFileInRoot(this.context, {
|
|
30810
31138
|
relativePath,
|
|
30811
31139
|
data,
|
|
30812
31140
|
mkdir: this.defaults.mkdir,
|
|
30813
31141
|
mode: this.defaults.mode,
|
|
30814
|
-
...options,
|
|
30815
|
-
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31142
|
+
...this.mutationOptions(options),
|
|
30816
31143
|
overwrite: false,
|
|
30817
31144
|
});
|
|
30818
31145
|
}
|
|
@@ -30827,15 +31154,14 @@ class RootHandle {
|
|
|
30827
31154
|
await this.create(relativePath, trailingNewline ? `${json}\n` : json, writeOptions);
|
|
30828
31155
|
}
|
|
30829
31156
|
async copyIn(relativePath, sourcePath, options = {}) {
|
|
30830
|
-
|
|
31157
|
+
assertValidRootDestinationPath(relativePath);
|
|
30831
31158
|
await copyFileInRoot(this.context, {
|
|
30832
31159
|
sourcePath,
|
|
30833
31160
|
relativePath,
|
|
30834
31161
|
maxBytes: this.defaults.maxBytes,
|
|
30835
31162
|
mkdir: this.defaults.mkdir,
|
|
30836
31163
|
mode: this.defaults.mode,
|
|
30837
|
-
...options,
|
|
30838
|
-
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
31164
|
+
...this.mutationOptions(options),
|
|
30839
31165
|
});
|
|
30840
31166
|
}
|
|
30841
31167
|
async exists(relativePath) {
|
|
@@ -30862,9 +31188,9 @@ class RootHandle {
|
|
|
30862
31188
|
}
|
|
30863
31189
|
async move(fromRelative, toRelative, options = {}) {
|
|
30864
31190
|
assertValidRootRelativePath(fromRelative);
|
|
30865
|
-
|
|
31191
|
+
assertValidRootDestinationPath(toRelative);
|
|
30866
31192
|
validatePinnedOperationPayload({ from: fromRelative, to: toRelative });
|
|
30867
|
-
const denyMutations =
|
|
31193
|
+
const { denyMutations } = this.mutationOptions(options);
|
|
30868
31194
|
await assertMoveMutationAllowed(this.context, {
|
|
30869
31195
|
fromRelative,
|
|
30870
31196
|
toRelative,
|
|
@@ -30896,6 +31222,8 @@ async function root_impl_root(rootDir, defaults = {}) {
|
|
|
30896
31222
|
async function openFileInRoot(root, params) {
|
|
30897
31223
|
const { rootWithSep, resolved } = await resolvePathInRoot(root, params.relativePath, {
|
|
30898
31224
|
allowFinalSymlink: true,
|
|
31225
|
+
rejectUnsafeDeviceReads: true,
|
|
31226
|
+
rejectSymlinks: params.symlinks !== "follow-within-root",
|
|
30899
31227
|
});
|
|
30900
31228
|
let opened;
|
|
30901
31229
|
try {
|
|
@@ -30912,11 +31240,11 @@ async function openFileInRoot(root, params) {
|
|
|
30912
31240
|
}
|
|
30913
31241
|
if (params.hardlinks !== "allow" && opened.stat.nlink > 1) {
|
|
30914
31242
|
await opened.handle.close().catch(() => { });
|
|
30915
|
-
throw
|
|
31243
|
+
throw hardlinkedPathNotAllowedError();
|
|
30916
31244
|
}
|
|
30917
31245
|
if (!path_isPathInside(rootWithSep, opened.realPath)) {
|
|
30918
31246
|
await opened.handle.close().catch(() => { });
|
|
30919
|
-
throw
|
|
31247
|
+
throw outsideWorkspaceError();
|
|
30920
31248
|
}
|
|
30921
31249
|
return opened;
|
|
30922
31250
|
}
|
|
@@ -30998,33 +31326,47 @@ async function verifyAtomicWriteResult(params) {
|
|
|
30998
31326
|
throw new errors_FsSafeError("path-mismatch", "path changed during write");
|
|
30999
31327
|
}
|
|
31000
31328
|
if (!path_isPathInside(params.root.rootWithSep, opened.realPath)) {
|
|
31001
|
-
throw
|
|
31329
|
+
throw outsideWorkspaceError();
|
|
31002
31330
|
}
|
|
31003
31331
|
}
|
|
31004
31332
|
finally {
|
|
31005
31333
|
await opened.handle.close().catch(() => { });
|
|
31006
31334
|
}
|
|
31007
31335
|
}
|
|
31008
|
-
async function
|
|
31009
|
-
const
|
|
31010
|
-
|
|
31011
|
-
|
|
31012
|
-
|
|
31013
|
-
|
|
31014
|
-
|
|
31015
|
-
|
|
31016
|
-
|
|
31017
|
-
|
|
31018
|
-
|
|
31019
|
-
|
|
31336
|
+
async function resolveGuardedWritePathInRoot(root, params) {
|
|
31337
|
+
const resolvedPath = await resolvePathInRoot(root, params.relativePath, {
|
|
31338
|
+
aliasErrorCode: "path-alias",
|
|
31339
|
+
allowFinalSymlink: params.allowFinalSymlink,
|
|
31340
|
+
});
|
|
31341
|
+
await assertMutationNotDenied(resolvedPath.resolved, params.denyMutations, params.protectDeniedAncestors ? { protectAncestors: true } : undefined);
|
|
31342
|
+
if (await (params.shouldAssertNoPathAlias?.(resolvedPath) ?? true)) {
|
|
31343
|
+
try {
|
|
31344
|
+
await assertNoPathAliasEscape({
|
|
31345
|
+
absolutePath: resolvedPath.resolved,
|
|
31346
|
+
rootPath: resolvedPath.rootReal,
|
|
31347
|
+
boundaryLabel: "root",
|
|
31348
|
+
});
|
|
31349
|
+
}
|
|
31350
|
+
catch (error) {
|
|
31351
|
+
throw new errors_FsSafeError("path-alias", "path alias escape blocked", {
|
|
31352
|
+
cause: error instanceof Error ? error : undefined,
|
|
31353
|
+
});
|
|
31354
|
+
}
|
|
31020
31355
|
}
|
|
31356
|
+
return resolvedPath;
|
|
31357
|
+
}
|
|
31358
|
+
async function openWritableFileInRoot(root, params) {
|
|
31359
|
+
const { rootReal, rootWithSep, resolved } = await resolveGuardedWritePathInRoot(root, {
|
|
31360
|
+
relativePath: params.relativePath,
|
|
31361
|
+
denyMutations: params.denyMutations,
|
|
31362
|
+
});
|
|
31021
31363
|
let ioPath = params.mkdir === false
|
|
31022
31364
|
? resolved
|
|
31023
31365
|
: await prepareRootWriteTarget(rootReal, resolved);
|
|
31024
31366
|
try {
|
|
31025
31367
|
const resolvedRealPath = await promises_.realpath(ioPath);
|
|
31026
31368
|
if (!path_isPathInside(rootWithSep, resolvedRealPath)) {
|
|
31027
|
-
throw
|
|
31369
|
+
throw outsideWorkspaceError();
|
|
31028
31370
|
}
|
|
31029
31371
|
ioPath = resolvedRealPath;
|
|
31030
31372
|
}
|
|
@@ -31055,7 +31397,7 @@ async function openWritableFileInRoot(root, params) {
|
|
|
31055
31397
|
}
|
|
31056
31398
|
catch (err) {
|
|
31057
31399
|
if (path_isNotFoundPathError(err)) {
|
|
31058
|
-
throw
|
|
31400
|
+
throw fileNotFoundError();
|
|
31059
31401
|
}
|
|
31060
31402
|
if (isSymlinkOpenError(err)) {
|
|
31061
31403
|
throw new errors_FsSafeError("symlink", "symlink open blocked", { cause: err });
|
|
@@ -31066,13 +31408,17 @@ async function openWritableFileInRoot(root, params) {
|
|
|
31066
31408
|
throw err;
|
|
31067
31409
|
}
|
|
31068
31410
|
let realPathForCleanup = null;
|
|
31411
|
+
let createdIdentity = null;
|
|
31069
31412
|
try {
|
|
31070
31413
|
const stat = await handle.stat();
|
|
31414
|
+
if (createdForWrite) {
|
|
31415
|
+
createdIdentity = stat;
|
|
31416
|
+
}
|
|
31071
31417
|
if (!stat.isFile()) {
|
|
31072
|
-
throw new errors_FsSafeError("
|
|
31418
|
+
throw new errors_FsSafeError("not-file", "path is not a regular file under root");
|
|
31073
31419
|
}
|
|
31074
31420
|
if (stat.nlink > 1) {
|
|
31075
|
-
throw
|
|
31421
|
+
throw hardlinkedPathNotAllowedError();
|
|
31076
31422
|
}
|
|
31077
31423
|
try {
|
|
31078
31424
|
const lstat = await promises_.lstat(ioPath);
|
|
@@ -31095,10 +31441,10 @@ async function openWritableFileInRoot(root, params) {
|
|
|
31095
31441
|
throw new errors_FsSafeError("path-mismatch", "path mismatch");
|
|
31096
31442
|
}
|
|
31097
31443
|
if (realStat.nlink > 1) {
|
|
31098
|
-
throw
|
|
31444
|
+
throw hardlinkedPathNotAllowedError();
|
|
31099
31445
|
}
|
|
31100
31446
|
if (!path_isPathInside(rootWithSep, realPath)) {
|
|
31101
|
-
throw
|
|
31447
|
+
throw outsideWorkspaceError();
|
|
31102
31448
|
}
|
|
31103
31449
|
// Truncate only after boundary and identity checks complete. This avoids
|
|
31104
31450
|
// irreversible side effects if a symlink target changes before validation.
|
|
@@ -31118,8 +31464,8 @@ async function openWritableFileInRoot(root, params) {
|
|
|
31118
31464
|
const cleanupCreatedPath = createdForWrite && err instanceof errors_FsSafeError;
|
|
31119
31465
|
const cleanupPath = realPathForCleanup ?? ioPath;
|
|
31120
31466
|
await handle.close().catch(() => { });
|
|
31121
|
-
if (cleanupCreatedPath) {
|
|
31122
|
-
await
|
|
31467
|
+
if (cleanupCreatedPath && createdIdentity) {
|
|
31468
|
+
await removePathIfIdentityUnchanged(cleanupPath, createdIdentity).catch(() => { });
|
|
31123
31469
|
}
|
|
31124
31470
|
throw err;
|
|
31125
31471
|
}
|
|
@@ -31164,7 +31510,11 @@ async function appendFileInRoot(root, params) {
|
|
|
31164
31510
|
}
|
|
31165
31511
|
async function removePathInRoot(root, params) {
|
|
31166
31512
|
validatePinnedOperationPayload({ relativePath: params.relativePath });
|
|
31167
|
-
const resolved = await
|
|
31513
|
+
const resolved = await resolvePinnedPathInRoot(root, {
|
|
31514
|
+
relativePath: params.relativePath,
|
|
31515
|
+
denyMutations: params.denyMutations,
|
|
31516
|
+
remove: true,
|
|
31517
|
+
});
|
|
31168
31518
|
try {
|
|
31169
31519
|
await removePathFallback(resolved);
|
|
31170
31520
|
}
|
|
@@ -31183,15 +31533,16 @@ async function mkdirPathInRoot(root, params) {
|
|
|
31183
31533
|
}
|
|
31184
31534
|
}
|
|
31185
31535
|
async function writeFileInRoot(root, params) {
|
|
31186
|
-
|
|
31187
|
-
|
|
31536
|
+
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
31537
|
+
if (process.platform === "win32" &&
|
|
31538
|
+
(params.renameIdentity === "verify-content-with-lock" || !getNativeBinding())) {
|
|
31188
31539
|
await writeFileFallback(root, params);
|
|
31540
|
+
return;
|
|
31541
|
+
}
|
|
31542
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
31543
|
+
await serializePathWrite(pinned.targetPath, async () => {
|
|
31544
|
+
await commitPinnedWriteInRoot(root, pinned, params);
|
|
31189
31545
|
});
|
|
31190
|
-
return;
|
|
31191
|
-
}
|
|
31192
|
-
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
31193
|
-
await serializePathWrite(pinned.targetPath, async () => {
|
|
31194
|
-
await commitPinnedWriteInRoot(root, pinned, params);
|
|
31195
31546
|
});
|
|
31196
31547
|
}
|
|
31197
31548
|
async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
@@ -31207,6 +31558,7 @@ async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
|
31207
31558
|
mode: params.mode ?? pinned.mode,
|
|
31208
31559
|
overwrite: params.overwrite,
|
|
31209
31560
|
input: { kind: "buffer", data: params.data, encoding: params.encoding },
|
|
31561
|
+
rootIdentity: root.rootIdentity,
|
|
31210
31562
|
});
|
|
31211
31563
|
}
|
|
31212
31564
|
catch (error) {
|
|
@@ -31244,26 +31596,35 @@ async function copyFileInRoot(root, params) {
|
|
|
31244
31596
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${source.stat.size})`);
|
|
31245
31597
|
}
|
|
31246
31598
|
try {
|
|
31247
|
-
|
|
31248
|
-
|
|
31249
|
-
await
|
|
31250
|
-
|
|
31251
|
-
|
|
31252
|
-
|
|
31253
|
-
|
|
31254
|
-
|
|
31255
|
-
|
|
31256
|
-
|
|
31257
|
-
|
|
31258
|
-
|
|
31599
|
+
await serializePathWrite(rootWriteQueueKey(root, params.relativePath), async () => {
|
|
31600
|
+
const pinned = await resolvePinnedWriteTargetInRoot(root, params.relativePath, params.mode, params.denyMutations);
|
|
31601
|
+
await serializePathWrite(pinned.targetPath, async () => {
|
|
31602
|
+
await assertCopySourceCurrent(source);
|
|
31603
|
+
let identity;
|
|
31604
|
+
try {
|
|
31605
|
+
identity = await runPinnedWriteHelper({
|
|
31606
|
+
rootPath: pinned.rootReal,
|
|
31607
|
+
relativeParentPath: pinned.relativeParentPath,
|
|
31608
|
+
basename: pinned.basename,
|
|
31609
|
+
mkdir: params.mkdir !== false,
|
|
31610
|
+
mode: pinned.mode,
|
|
31611
|
+
overwrite: true,
|
|
31612
|
+
maxBytes: params.maxBytes,
|
|
31613
|
+
input: { kind: "stream", stream: source.handle.createReadStream() },
|
|
31614
|
+
rootIdentity: root.rootIdentity,
|
|
31615
|
+
});
|
|
31616
|
+
}
|
|
31617
|
+
catch (error) {
|
|
31618
|
+
throw normalizePinnedWriteError(error);
|
|
31619
|
+
}
|
|
31620
|
+
try {
|
|
31621
|
+
await assertCopySourcePathCurrent(source);
|
|
31622
|
+
}
|
|
31623
|
+
catch (error) {
|
|
31624
|
+
await removePathIfIdentityUnchanged(pinned.targetPath, identity).catch(() => undefined);
|
|
31625
|
+
throw error;
|
|
31626
|
+
}
|
|
31259
31627
|
});
|
|
31260
|
-
try {
|
|
31261
|
-
await assertCopySourcePathCurrent(source);
|
|
31262
|
-
}
|
|
31263
|
-
catch (error) {
|
|
31264
|
-
await removeCopyTargetIfUnchanged(pinned.targetPath, identity).catch(() => undefined);
|
|
31265
|
-
throw error;
|
|
31266
|
-
}
|
|
31267
31628
|
});
|
|
31268
31629
|
}
|
|
31269
31630
|
finally {
|
|
@@ -31283,7 +31644,7 @@ async function assertCopySourcePathCurrent(source) {
|
|
|
31283
31644
|
throw new errors_FsSafeError("path-mismatch", "copy source path changed");
|
|
31284
31645
|
}
|
|
31285
31646
|
}
|
|
31286
|
-
async function
|
|
31647
|
+
async function removePathIfIdentityUnchanged(targetPath, identity) {
|
|
31287
31648
|
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(targetPath));
|
|
31288
31649
|
const current = await promises_.lstat(targetPath);
|
|
31289
31650
|
if (current.isSymbolicLink() || !file_identity_sameFileIdentity(current, identity)) {
|
|
@@ -31294,25 +31655,15 @@ async function removeCopyTargetIfUnchanged(targetPath, identity) {
|
|
|
31294
31655
|
});
|
|
31295
31656
|
}
|
|
31296
31657
|
async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode, denyMutations) {
|
|
31297
|
-
const { rootReal, rootWithSep, resolved } = await
|
|
31298
|
-
|
|
31658
|
+
const { rootReal, rootWithSep, resolved } = await resolveGuardedWritePathInRoot(root, {
|
|
31659
|
+
relativePath,
|
|
31660
|
+
denyMutations,
|
|
31299
31661
|
});
|
|
31300
|
-
await assertMutationNotDenied(resolved, denyMutations);
|
|
31301
|
-
try {
|
|
31302
|
-
await assertNoPathAliasEscape({
|
|
31303
|
-
absolutePath: resolved,
|
|
31304
|
-
rootPath: rootReal,
|
|
31305
|
-
boundaryLabel: "root",
|
|
31306
|
-
});
|
|
31307
|
-
}
|
|
31308
|
-
catch (err) {
|
|
31309
|
-
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
31310
|
-
}
|
|
31311
31662
|
// resolvePathInRoot already enforces isPathInside, so any actual escape
|
|
31312
31663
|
// is rejected upstream.
|
|
31313
31664
|
const relativeResolved = external_node_path_.relative(rootReal, resolved);
|
|
31314
31665
|
if (external_node_path_.isAbsolute(relativeResolved)) {
|
|
31315
|
-
throw
|
|
31666
|
+
throw outsideWorkspaceError();
|
|
31316
31667
|
}
|
|
31317
31668
|
const relativePosix = relativeResolved
|
|
31318
31669
|
? relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep)
|
|
@@ -31327,11 +31678,12 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode,
|
|
|
31327
31678
|
relativePath,
|
|
31328
31679
|
hardlinks: "reject",
|
|
31329
31680
|
nonBlockingRead: true,
|
|
31681
|
+
symlinks: "follow-within-root",
|
|
31330
31682
|
});
|
|
31331
31683
|
try {
|
|
31332
31684
|
mode = requestedMode ?? (opened.stat.mode & 0o777);
|
|
31333
31685
|
if (!path_isPathInside(rootWithSep, opened.realPath)) {
|
|
31334
|
-
throw
|
|
31686
|
+
throw outsideWorkspaceError();
|
|
31335
31687
|
}
|
|
31336
31688
|
}
|
|
31337
31689
|
finally {
|
|
@@ -31355,17 +31707,9 @@ async function resolvePinnedPathInRoot(root, params) {
|
|
|
31355
31707
|
return await resolvePinnedOperationPathInRoot(root, {
|
|
31356
31708
|
allowRoot: params.allowRoot,
|
|
31357
31709
|
denyMutations: params.denyMutations,
|
|
31358
|
-
protectDenyMutationAncestors:
|
|
31710
|
+
protectDenyMutationAncestors: params.remove === true,
|
|
31359
31711
|
relativePath: params.relativePath,
|
|
31360
|
-
policy: PATH_ALIAS_POLICIES.strict,
|
|
31361
|
-
});
|
|
31362
|
-
}
|
|
31363
|
-
async function resolvePinnedRemovePathInRoot(root, relativePath, denyMutations) {
|
|
31364
|
-
return await resolvePinnedOperationPathInRoot(root, {
|
|
31365
|
-
denyMutations,
|
|
31366
|
-
protectDenyMutationAncestors: true,
|
|
31367
|
-
relativePath,
|
|
31368
|
-
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
31712
|
+
policy: params.remove ? PATH_ALIAS_POLICIES.unlinkTarget : PATH_ALIAS_POLICIES.strict,
|
|
31369
31713
|
});
|
|
31370
31714
|
}
|
|
31371
31715
|
async function resolvePinnedOperationPathInRoot(root, params) {
|
|
@@ -31383,11 +31727,11 @@ async function resolvePinnedOperationPathInRoot(root, params) {
|
|
|
31383
31727
|
relativeResolved === "." ||
|
|
31384
31728
|
firstSegment === ".." ||
|
|
31385
31729
|
external_node_path_.isAbsolute(relativeResolved)) {
|
|
31386
|
-
throw
|
|
31730
|
+
throw outsideWorkspaceError();
|
|
31387
31731
|
}
|
|
31388
31732
|
const relativePosix = relativeResolved.split(external_node_path_.sep).join(external_node_path_.posix.sep);
|
|
31389
31733
|
if (!path_isPathInside(resolved.rootWithSep, resolved.canonicalPath)) {
|
|
31390
|
-
throw
|
|
31734
|
+
throw outsideWorkspaceError();
|
|
31391
31735
|
}
|
|
31392
31736
|
await assertMutationNotDenied(resolved.canonicalPath, params.denyMutations, {
|
|
31393
31737
|
protectAncestors: params.protectDenyMutationAncestors,
|
|
@@ -31395,6 +31739,7 @@ async function resolvePinnedOperationPathInRoot(root, params) {
|
|
|
31395
31739
|
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix };
|
|
31396
31740
|
}
|
|
31397
31741
|
async function resolvePinnedRootPathInRoot(root, params) {
|
|
31742
|
+
await assertRootIdentityCurrent(root);
|
|
31398
31743
|
const rootReal = root.rootReal;
|
|
31399
31744
|
let resolved;
|
|
31400
31745
|
try {
|
|
@@ -31419,11 +31764,25 @@ async function resolvePinnedRootPathInRoot(root, params) {
|
|
|
31419
31764
|
canonicalPath: resolved.canonicalPath,
|
|
31420
31765
|
};
|
|
31421
31766
|
}
|
|
31767
|
+
async function prepareRemoveGuard(targetPath) {
|
|
31768
|
+
try {
|
|
31769
|
+
const guard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(targetPath));
|
|
31770
|
+
await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("remove", targetPath);
|
|
31771
|
+
await assertAsyncDirectoryGuard(guard);
|
|
31772
|
+
return guard;
|
|
31773
|
+
}
|
|
31774
|
+
catch (error) {
|
|
31775
|
+
throw normalizeRemoveGuardError(error);
|
|
31776
|
+
}
|
|
31777
|
+
}
|
|
31422
31778
|
async function removePathFallback(resolved) {
|
|
31423
|
-
const guard = await
|
|
31424
|
-
|
|
31425
|
-
|
|
31426
|
-
|
|
31779
|
+
const guard = await prepareRemoveGuard(resolved.resolved);
|
|
31780
|
+
try {
|
|
31781
|
+
await ((await promises_.lstat(resolved.resolved)).isDirectory() ? promises_.rmdir(resolved.resolved) : promises_.rm(resolved.resolved));
|
|
31782
|
+
}
|
|
31783
|
+
catch (error) {
|
|
31784
|
+
throw normalizeRemovePathError(error);
|
|
31785
|
+
}
|
|
31427
31786
|
await assertAsyncDirectoryGuard(guard).catch(() => undefined);
|
|
31428
31787
|
}
|
|
31429
31788
|
async function mkdirPathFallback(resolved) {
|
|
@@ -31435,13 +31794,13 @@ async function mkdirPathFallback(resolved) {
|
|
|
31435
31794
|
async function statPathFallback(root, relativePath) {
|
|
31436
31795
|
const resolved = await resolvePinnedPathInRoot(root, { relativePath, allowRoot: true });
|
|
31437
31796
|
try {
|
|
31438
|
-
|
|
31797
|
+
const stat = pathStatFromStats(await promises_.lstat(resolved.resolved));
|
|
31798
|
+
await assertRootIdentityCurrent(root);
|
|
31799
|
+
return stat;
|
|
31439
31800
|
}
|
|
31440
31801
|
catch (error) {
|
|
31441
31802
|
if (path_isNotFoundPathError(error)) {
|
|
31442
|
-
throw
|
|
31443
|
-
cause: error instanceof Error ? error : undefined,
|
|
31444
|
-
});
|
|
31803
|
+
throw fileNotFoundError(error instanceof Error ? error : undefined);
|
|
31445
31804
|
}
|
|
31446
31805
|
throw error;
|
|
31447
31806
|
}
|
|
@@ -31452,6 +31811,7 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
31452
31811
|
const names = await promises_.readdir(resolved.resolved);
|
|
31453
31812
|
const sortedNames = names.toSorted();
|
|
31454
31813
|
if (!withFileTypes) {
|
|
31814
|
+
await assertRootIdentityCurrent(root);
|
|
31455
31815
|
return sortedNames;
|
|
31456
31816
|
}
|
|
31457
31817
|
const entries = [];
|
|
@@ -31461,6 +31821,7 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
31461
31821
|
...pathStatFromStats(await promises_.lstat(external_node_path_.join(resolved.resolved, name))),
|
|
31462
31822
|
});
|
|
31463
31823
|
}
|
|
31824
|
+
await assertRootIdentityCurrent(root);
|
|
31464
31825
|
return entries;
|
|
31465
31826
|
}
|
|
31466
31827
|
catch (error) {
|
|
@@ -31473,6 +31834,8 @@ async function listPathFallback(root, relativePath, withFileTypes) {
|
|
|
31473
31834
|
}
|
|
31474
31835
|
}
|
|
31475
31836
|
async function assertMoveMutationAllowed(root, params) {
|
|
31837
|
+
// Keep this preflight separate from the pinned resolutions in movePathFallback:
|
|
31838
|
+
// mutation denials must take precedence over source alias or identity failures.
|
|
31476
31839
|
const source = await resolvePathInRoot(root, params.fromRelative, {
|
|
31477
31840
|
aliasErrorCode: "path-alias",
|
|
31478
31841
|
allowFinalSymlink: true,
|
|
@@ -31494,40 +31857,29 @@ async function movePathFallback(root, params) {
|
|
|
31494
31857
|
relativePath: params.fromRelative,
|
|
31495
31858
|
policy: PATH_ALIAS_POLICIES.strict,
|
|
31496
31859
|
});
|
|
31497
|
-
const target = await
|
|
31498
|
-
aliasErrorCode: "path-alias",
|
|
31499
|
-
allowFinalSymlink: true,
|
|
31500
|
-
});
|
|
31501
|
-
await assertMutationNotDenied(target.resolved, params.denyMutations, { protectAncestors: true });
|
|
31502
|
-
await resolvePinnedRootPathInRoot(root, {
|
|
31860
|
+
const target = await resolveGuardedWritePathInRoot(root, {
|
|
31503
31861
|
relativePath: params.toRelative,
|
|
31504
|
-
|
|
31505
|
-
|
|
31506
|
-
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
|
|
31510
|
-
|
|
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,
|
|
31862
|
+
denyMutations: params.denyMutations,
|
|
31863
|
+
allowFinalSymlink: true,
|
|
31864
|
+
protectDeniedAncestors: true,
|
|
31865
|
+
shouldAssertNoPathAlias: async (resolvedTarget) => {
|
|
31866
|
+
await resolvePinnedRootPathInRoot(root, {
|
|
31867
|
+
relativePath: params.toRelative,
|
|
31868
|
+
policy: PATH_ALIAS_POLICIES.unlinkTarget,
|
|
31519
31869
|
});
|
|
31520
|
-
|
|
31521
|
-
|
|
31870
|
+
const targetStat = await promises_.lstat(resolvedTarget.resolved).catch(() => undefined);
|
|
31871
|
+
return !(process.platform !== "win32" &&
|
|
31872
|
+
params.overwrite &&
|
|
31873
|
+
targetStat?.isSymbolicLink() === true);
|
|
31874
|
+
},
|
|
31875
|
+
});
|
|
31522
31876
|
let sourceStat;
|
|
31523
31877
|
try {
|
|
31524
31878
|
sourceStat = await promises_.lstat(source.resolved);
|
|
31525
31879
|
}
|
|
31526
31880
|
catch (error) {
|
|
31527
31881
|
if (path_isNotFoundPathError(error)) {
|
|
31528
|
-
throw
|
|
31529
|
-
cause: error instanceof Error ? error : undefined,
|
|
31530
|
-
});
|
|
31882
|
+
throw fileNotFoundError(error instanceof Error ? error : undefined);
|
|
31531
31883
|
}
|
|
31532
31884
|
throw error;
|
|
31533
31885
|
}
|
|
@@ -31535,7 +31887,7 @@ async function movePathFallback(root, params) {
|
|
|
31535
31887
|
throw new errors_FsSafeError("symlink", "symlink not allowed");
|
|
31536
31888
|
}
|
|
31537
31889
|
if (sourceStat.isFile() && sourceStat.nlink > 1) {
|
|
31538
|
-
throw
|
|
31890
|
+
throw hardlinkedPathNotAllowedError();
|
|
31539
31891
|
}
|
|
31540
31892
|
if (!params.overwrite && sourceStat.isDirectory()) {
|
|
31541
31893
|
throw new errors_FsSafeError("invalid-path", "directory moves require overwrite: true");
|
|
@@ -31564,9 +31916,7 @@ async function movePathFallback(root, params) {
|
|
|
31564
31916
|
}
|
|
31565
31917
|
catch (error) {
|
|
31566
31918
|
if (path_isNotFoundPathError(error)) {
|
|
31567
|
-
throw
|
|
31568
|
-
cause: error instanceof Error ? error : undefined,
|
|
31569
|
-
});
|
|
31919
|
+
throw fileNotFoundError(error instanceof Error ? error : undefined);
|
|
31570
31920
|
}
|
|
31571
31921
|
if (hasNodeErrorCode(error, "EEXIST")) {
|
|
31572
31922
|
throw new errors_FsSafeError("already-exists", "destination exists", {
|
|
@@ -31632,30 +31982,22 @@ async function writeFileFallback(root, params) {
|
|
|
31632
31982
|
}
|
|
31633
31983
|
}
|
|
31634
31984
|
async function writeMissingFileFallback(root, params) {
|
|
31635
|
-
const { rootReal, resolved } = await
|
|
31636
|
-
|
|
31985
|
+
const { rootReal, resolved } = await resolveGuardedWritePathInRoot(root, {
|
|
31986
|
+
relativePath: params.relativePath,
|
|
31987
|
+
denyMutations: params.denyMutations,
|
|
31637
31988
|
});
|
|
31638
|
-
await assertMutationNotDenied(resolved, params.denyMutations);
|
|
31639
|
-
try {
|
|
31640
|
-
await assertNoPathAliasEscape({
|
|
31641
|
-
absolutePath: resolved,
|
|
31642
|
-
rootPath: rootReal,
|
|
31643
|
-
boundaryLabel: "root",
|
|
31644
|
-
});
|
|
31645
|
-
}
|
|
31646
|
-
catch (err) {
|
|
31647
|
-
throw new errors_FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
31648
|
-
}
|
|
31649
31989
|
const targetPath = params.mkdir === false
|
|
31650
31990
|
? resolved
|
|
31651
31991
|
: await prepareRootWriteTarget(rootReal, resolved);
|
|
31652
31992
|
const parentGuard = await directory_guard_createAsyncDirectoryGuard(external_node_path_.dirname(targetPath));
|
|
31653
31993
|
let created = false;
|
|
31994
|
+
let createdIdentity;
|
|
31654
31995
|
try {
|
|
31655
31996
|
const { handle, writtenStat } = await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
31656
31997
|
const handle = await promises_.open(targetPath, OPEN_WRITE_CREATE_FLAGS, params.mode ?? 0o600);
|
|
31657
31998
|
created = true;
|
|
31658
31999
|
try {
|
|
32000
|
+
createdIdentity = await handle.stat();
|
|
31659
32001
|
if (typeof params.data === "string") {
|
|
31660
32002
|
await handle.writeFile(params.data, params.encoding ?? "utf8");
|
|
31661
32003
|
}
|
|
@@ -31691,8 +32033,8 @@ async function writeMissingFileFallback(root, params) {
|
|
|
31691
32033
|
throw err;
|
|
31692
32034
|
}
|
|
31693
32035
|
finally {
|
|
31694
|
-
if (created) {
|
|
31695
|
-
await
|
|
32036
|
+
if (created && createdIdentity) {
|
|
32037
|
+
await removePathIfIdentityUnchanged(targetPath, createdIdentity).catch(() => undefined);
|
|
31696
32038
|
}
|
|
31697
32039
|
}
|
|
31698
32040
|
}
|
|
@@ -32428,7 +32770,7 @@ function getExpectedArgumentLength(message) {
|
|
|
32428
32770
|
while (regex.exec(message) !== null) expectedLength++;
|
|
32429
32771
|
return expectedLength;
|
|
32430
32772
|
}
|
|
32431
|
-
function
|
|
32773
|
+
function dist_createError(sym, value, constructor) {
|
|
32432
32774
|
dist_messages.set(sym, value);
|
|
32433
32775
|
return makeNodeErrorWithCode(constructor, sym);
|
|
32434
32776
|
}
|
|
@@ -32546,7 +32888,7 @@ function determineSpecificType(value) {
|
|
|
32546
32888
|
}
|
|
32547
32889
|
}
|
|
32548
32890
|
}
|
|
32549
|
-
|
|
32891
|
+
dist_createError("ERR_INVALID_ARG_TYPE", (name, expected, actual) => {
|
|
32550
32892
|
external_node_assert_.ok(typeof name === "string", "'name' must be a string");
|
|
32551
32893
|
if (!Array.isArray(expected)) expected = [expected];
|
|
32552
32894
|
let message = "The ";
|
|
@@ -32590,13 +32932,13 @@ createError("ERR_INVALID_ARG_TYPE", (name, expected, actual) => {
|
|
|
32590
32932
|
message += `. Received ${determineSpecificType(actual)}`;
|
|
32591
32933
|
return message;
|
|
32592
32934
|
}, TypeError);
|
|
32593
|
-
const ERR_INVALID_MODULE_SPECIFIER =
|
|
32935
|
+
const ERR_INVALID_MODULE_SPECIFIER = dist_createError("ERR_INVALID_MODULE_SPECIFIER", (request, reason, base) => {
|
|
32594
32936
|
return `Invalid module "${request}" ${reason}${base ? ` imported from ${base}` : ""}`;
|
|
32595
32937
|
}, TypeError);
|
|
32596
|
-
const ERR_INVALID_PACKAGE_CONFIG =
|
|
32938
|
+
const ERR_INVALID_PACKAGE_CONFIG = dist_createError("ERR_INVALID_PACKAGE_CONFIG", (path, base, message) => {
|
|
32597
32939
|
return `Invalid package config ${path}${base ? ` while importing ${base}` : ""}${message ? `. ${message}` : ""}`;
|
|
32598
32940
|
}, Error);
|
|
32599
|
-
const ERR_INVALID_PACKAGE_TARGET =
|
|
32941
|
+
const ERR_INVALID_PACKAGE_TARGET = dist_createError("ERR_INVALID_PACKAGE_TARGET", (packagePath, key, target, isImport = false, base) => {
|
|
32600
32942
|
const relatedError = typeof target === "string" && !isImport && target.length > 0 && !target.startsWith("./");
|
|
32601
32943
|
if (key === ".") {
|
|
32602
32944
|
external_node_assert_.ok(isImport === false);
|
|
@@ -32604,26 +32946,26 @@ const ERR_INVALID_PACKAGE_TARGET = createError("ERR_INVALID_PACKAGE_TARGET", (pa
|
|
|
32604
32946
|
}
|
|
32605
32947
|
return `Invalid "${isImport ? "imports" : "exports"}" target ${JSON.stringify(target)} defined for '${key}' in the package config ${packagePath}package.json${base ? ` imported from ${base}` : ""}${relatedError ? "; targets must start with \"./\"" : ""}`;
|
|
32606
32948
|
}, Error);
|
|
32607
|
-
const ERR_MODULE_NOT_FOUND =
|
|
32949
|
+
const ERR_MODULE_NOT_FOUND = dist_createError("ERR_MODULE_NOT_FOUND", function(path, base, exactUrl = false) {
|
|
32608
32950
|
if (exactUrl && typeof exactUrl === "string") this.url = `${exactUrl}`;
|
|
32609
32951
|
return `Cannot find ${exactUrl ? "module" : "package"} '${path}' imported from ${base}`;
|
|
32610
32952
|
}, Error);
|
|
32611
|
-
const ERR_PACKAGE_IMPORT_NOT_DEFINED =
|
|
32953
|
+
const ERR_PACKAGE_IMPORT_NOT_DEFINED = dist_createError("ERR_PACKAGE_IMPORT_NOT_DEFINED", (specifier, packagePath, base) => {
|
|
32612
32954
|
return `Package import specifier "${specifier}" is not defined${packagePath ? ` in package ${packagePath || ""}package.json` : ""} imported from ${base}`;
|
|
32613
32955
|
}, TypeError);
|
|
32614
|
-
const ERR_PACKAGE_PATH_NOT_EXPORTED =
|
|
32956
|
+
const ERR_PACKAGE_PATH_NOT_EXPORTED = dist_createError("ERR_PACKAGE_PATH_NOT_EXPORTED", (packagePath, subpath, base) => {
|
|
32615
32957
|
if (subpath === ".") return `No "exports" main defined in ${packagePath}package.json${base ? ` imported from ${base}` : ""}`;
|
|
32616
32958
|
return `Package subpath '${subpath}' is not defined by "exports" in ${packagePath}package.json${base ? ` imported from ${base}` : ""}`;
|
|
32617
32959
|
}, Error);
|
|
32618
|
-
const ERR_UNSUPPORTED_DIR_IMPORT =
|
|
32960
|
+
const ERR_UNSUPPORTED_DIR_IMPORT = dist_createError("ERR_UNSUPPORTED_DIR_IMPORT", function(path, base, exactUrl = void 0) {
|
|
32619
32961
|
this.url = exactUrl;
|
|
32620
32962
|
return `Directory import '${path}' is not supported resolving ES modules imported from ${base}`;
|
|
32621
32963
|
}, Error);
|
|
32622
|
-
const ERR_UNSUPPORTED_RESOLVE_REQUEST =
|
|
32623
|
-
const ERR_UNKNOWN_FILE_EXTENSION =
|
|
32964
|
+
const ERR_UNSUPPORTED_RESOLVE_REQUEST = dist_createError("ERR_UNSUPPORTED_RESOLVE_REQUEST", "Failed to resolve module specifier \"%s\" from \"%s\": Invalid relative URL or base scheme is not hierarchical.", TypeError);
|
|
32965
|
+
const ERR_UNKNOWN_FILE_EXTENSION = dist_createError("ERR_UNKNOWN_FILE_EXTENSION", (extension, path) => {
|
|
32624
32966
|
return `Unknown file extension "${extension}" for ${path}`;
|
|
32625
32967
|
}, TypeError);
|
|
32626
|
-
|
|
32968
|
+
dist_createError("ERR_INVALID_ARG_VALUE", (name, value, reason = "is invalid") => {
|
|
32627
32969
|
let inspected = (0,external_node_util_.inspect)(value);
|
|
32628
32970
|
if (inspected.length > 128) inspected = `${inspected.slice(0, 128)}...`;
|
|
32629
32971
|
return `The ${name.includes(".") ? "property" : "argument"} '${name}' ${reason}. Received ${inspected}`;
|
|
@@ -70046,4 +70388,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
70046
70388
|
// module factories are used so entry inlining is disabled
|
|
70047
70389
|
// startup
|
|
70048
70390
|
// Load entry module and return exports
|
|
70049
|
-
var __webpack_exports__ = __webpack_require__(
|
|
70391
|
+
var __webpack_exports__ = __webpack_require__(4583);
|