@lousy-agents/mcp 5.17.8 → 5.17.10
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 +421 -151
- 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
|
+
3503(__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);
|
|
@@ -27264,15 +27264,11 @@ var external_node_child_process_ = __webpack_require__(1421);
|
|
|
27264
27264
|
const PINNED_PYTHON_WORKER_SOURCE = String.raw `
|
|
27265
27265
|
import base64, errno, json, os, secrets, stat, sys
|
|
27266
27266
|
DIR_FLAGS = os.O_RDONLY
|
|
27267
|
-
if hasattr(os, "O_DIRECTORY"):
|
|
27268
|
-
|
|
27269
|
-
if hasattr(os, "O_NOFOLLOW"):
|
|
27270
|
-
DIR_FLAGS |= os.O_NOFOLLOW
|
|
27267
|
+
if hasattr(os, "O_DIRECTORY"): DIR_FLAGS |= os.O_DIRECTORY
|
|
27268
|
+
if hasattr(os, "O_NOFOLLOW"): DIR_FLAGS |= os.O_NOFOLLOW
|
|
27271
27269
|
READ_FLAGS = os.O_RDONLY
|
|
27272
|
-
if hasattr(os, "O_NONBLOCK"):
|
|
27273
|
-
|
|
27274
|
-
if hasattr(os, "O_NOFOLLOW"):
|
|
27275
|
-
READ_FLAGS |= os.O_NOFOLLOW
|
|
27270
|
+
if hasattr(os, "O_NONBLOCK"): READ_FLAGS |= os.O_NONBLOCK
|
|
27271
|
+
if hasattr(os, "O_NOFOLLOW"): READ_FLAGS |= os.O_NOFOLLOW
|
|
27276
27272
|
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL
|
|
27277
27273
|
if hasattr(os, "O_NOFOLLOW"):
|
|
27278
27274
|
WRITE_FLAGS |= os.O_NOFOLLOW
|
|
@@ -27352,6 +27348,10 @@ def write_all(fd, data):
|
|
|
27352
27348
|
if written <= 0:
|
|
27353
27349
|
raise OSError(errno.EIO, "short write")
|
|
27354
27350
|
view = view[written:]
|
|
27351
|
+
def fsync_best_effort(fd):
|
|
27352
|
+
try: os.fsync(fd)
|
|
27353
|
+
except OSError as error:
|
|
27354
|
+
if error.errno != errno.EPERM: raise
|
|
27355
27355
|
def link_unsupported(exc):
|
|
27356
27356
|
unsupported = (errno.EPERM, errno.EOPNOTSUPP, getattr(errno, "ENOTSUP", errno.EOPNOTSUPP))
|
|
27357
27357
|
return getattr(exc, "errno", None) in unsupported
|
|
@@ -27557,13 +27557,13 @@ def write_path(root_fd, payload):
|
|
|
27557
27557
|
temp_name, temp_fd = create_temp_file(parent_fd, basename, mode)
|
|
27558
27558
|
os.fchmod(temp_fd, mode)
|
|
27559
27559
|
write_all(temp_fd, data)
|
|
27560
|
-
|
|
27560
|
+
fsync_best_effort(temp_fd)
|
|
27561
27561
|
temp_stat = os.fstat(temp_fd)
|
|
27562
27562
|
os.close(temp_fd)
|
|
27563
27563
|
temp_fd = None
|
|
27564
27564
|
result_stat = commit_temp_file(parent_fd, temp_name, basename, overwrite, mode, temp_stat)
|
|
27565
27565
|
temp_name = None
|
|
27566
|
-
|
|
27566
|
+
fsync_best_effort(parent_fd)
|
|
27567
27567
|
return {"dev": result_stat.st_dev, "ino": result_stat.st_ino}
|
|
27568
27568
|
finally:
|
|
27569
27569
|
if temp_fd is not None:
|
|
@@ -27990,29 +27990,34 @@ async function runPinnedPathHelper(params) {
|
|
|
27990
27990
|
}
|
|
27991
27991
|
}
|
|
27992
27992
|
|
|
27993
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
27994
|
-
|
|
27993
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
|
|
27995
27994
|
|
|
27996
27995
|
|
|
27997
27996
|
|
|
27998
|
-
const
|
|
27999
|
-
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
27997
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
|
|
27998
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS = SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES * 8;
|
|
27999
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX = "\t".repeat(8);
|
|
28000
|
+
const SIDECAR_LOCK_OWNERSHIP_TOKEN_PATTERN = new RegExp(`\\n(${SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX}[ \\t]{${SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS}})\\n$`);
|
|
28001
|
+
function createSidecarLockOwnershipToken() {
|
|
28002
|
+
let token = SIDECAR_LOCK_OWNERSHIP_TOKEN_PREFIX;
|
|
28003
|
+
for (const byte of (0,external_node_crypto_.randomBytes)(SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES)) {
|
|
28004
|
+
for (let bit = 7; bit >= 0; bit -= 1) {
|
|
28005
|
+
token += byte & (1 << bit) ? "\t" : " ";
|
|
28006
|
+
}
|
|
28003
28007
|
}
|
|
28004
|
-
return
|
|
28008
|
+
return token;
|
|
28005
28009
|
}
|
|
28006
|
-
function
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28010
|
+
function readSidecarLockOwnershipToken(raw) {
|
|
28011
|
+
return SIDECAR_LOCK_OWNERSHIP_TOKEN_PATTERN.exec(raw)?.[1];
|
|
28012
|
+
}
|
|
28013
|
+
function serializeSidecarLockPayload(payload) {
|
|
28014
|
+
const ownershipToken = createSidecarLockOwnershipToken();
|
|
28015
|
+
return {
|
|
28016
|
+
raw: `${JSON.stringify(payload, null, 2)}\n${ownershipToken}\n`,
|
|
28017
|
+
ownershipToken,
|
|
28018
|
+
};
|
|
28014
28019
|
}
|
|
28015
|
-
async function
|
|
28020
|
+
async function readSidecarLockSnapshot(lockPath) {
|
|
28016
28021
|
try {
|
|
28017
28022
|
const stat = await promises_.lstat(lockPath);
|
|
28018
28023
|
const raw = await promises_.readFile(lockPath, "utf8");
|
|
@@ -28034,7 +28039,15 @@ async function readLockSnapshot(lockPath) {
|
|
|
28034
28039
|
throw err;
|
|
28035
28040
|
}
|
|
28036
28041
|
}
|
|
28037
|
-
function
|
|
28042
|
+
function sidecarLockSnapshotMatches(current, observed) {
|
|
28043
|
+
if (observed.ownershipToken !== undefined) {
|
|
28044
|
+
return (current.stat?.isFile() === true &&
|
|
28045
|
+
current.raw !== undefined &&
|
|
28046
|
+
observed.raw !== undefined &&
|
|
28047
|
+
readSidecarLockOwnershipToken(current.raw) === observed.ownershipToken &&
|
|
28048
|
+
readSidecarLockOwnershipToken(observed.raw) === observed.ownershipToken &&
|
|
28049
|
+
current.raw === observed.raw);
|
|
28050
|
+
}
|
|
28038
28051
|
if (observed.stat && current.stat && !file_identity_sameFileIdentity(observed.stat, current.stat)) {
|
|
28039
28052
|
return false;
|
|
28040
28053
|
}
|
|
@@ -28043,30 +28056,54 @@ function snapshotMatches(current, observed) {
|
|
|
28043
28056
|
}
|
|
28044
28057
|
return observed.stat !== undefined && current.stat !== undefined;
|
|
28045
28058
|
}
|
|
28046
|
-
async function
|
|
28047
|
-
const current = await
|
|
28048
|
-
if (!current || !observed) {
|
|
28049
|
-
return false;
|
|
28050
|
-
}
|
|
28051
|
-
if (!snapshotMatches(current, observed)) {
|
|
28052
|
-
// The lock changed after we decided it was stale. Leave the fresh holder's
|
|
28053
|
-
// file alone; deleting by path here would break mutual exclusion.
|
|
28059
|
+
async function removeSidecarLockIfUnchanged(lockPath, observed) {
|
|
28060
|
+
const current = await readSidecarLockSnapshot(lockPath);
|
|
28061
|
+
if (!current || !observed || !sidecarLockSnapshotMatches(current, observed)) {
|
|
28054
28062
|
return false;
|
|
28055
28063
|
}
|
|
28056
28064
|
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28057
28065
|
return true;
|
|
28058
28066
|
}
|
|
28059
|
-
async function
|
|
28060
|
-
const current = await
|
|
28061
|
-
return !!current && !!observed &&
|
|
28067
|
+
async function sidecarLockSnapshotStillPresent(lockPath, observed) {
|
|
28068
|
+
const current = await readSidecarLockSnapshot(lockPath);
|
|
28069
|
+
return !!current && !!observed && sidecarLockSnapshotMatches(current, observed);
|
|
28062
28070
|
}
|
|
28063
|
-
async function
|
|
28064
|
-
|
|
28065
|
-
|
|
28071
|
+
async function sidecarReclaimGuardExists(pathname) {
|
|
28072
|
+
try {
|
|
28073
|
+
await promises_.lstat(pathname);
|
|
28074
|
+
return true;
|
|
28066
28075
|
}
|
|
28067
|
-
|
|
28076
|
+
catch (err) {
|
|
28077
|
+
if (err.code === "ENOENT") {
|
|
28078
|
+
return false;
|
|
28079
|
+
}
|
|
28080
|
+
throw err;
|
|
28081
|
+
}
|
|
28082
|
+
}
|
|
28083
|
+
async function tryAcquireSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
|
|
28084
|
+
try {
|
|
28085
|
+
await promises_.mkdir(reclaimGuardPath);
|
|
28086
|
+
reclaimGuards.add(reclaimGuardPath);
|
|
28087
|
+
return true;
|
|
28088
|
+
}
|
|
28089
|
+
catch (err) {
|
|
28090
|
+
if (err.code === "EEXIST") {
|
|
28091
|
+
return false;
|
|
28092
|
+
}
|
|
28093
|
+
throw err;
|
|
28094
|
+
}
|
|
28095
|
+
}
|
|
28096
|
+
async function releaseSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
|
|
28097
|
+
await promises_.rmdir(reclaimGuardPath);
|
|
28098
|
+
reclaimGuards.delete(reclaimGuardPath);
|
|
28099
|
+
}
|
|
28100
|
+
async function removeStaleSidecarLockIfAllowed(params) {
|
|
28101
|
+
if (!params.shouldRemoveStaleLock || params.snapshot.raw === undefined) {
|
|
28068
28102
|
return "not-approved";
|
|
28069
28103
|
}
|
|
28104
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot))) {
|
|
28105
|
+
return "changed";
|
|
28106
|
+
}
|
|
28070
28107
|
if (!(await params.shouldRemoveStaleLock({
|
|
28071
28108
|
lockPath: params.lockPath,
|
|
28072
28109
|
normalizedTargetPath: params.normalizedTargetPath,
|
|
@@ -28075,32 +28112,95 @@ async function removeStaleLockIfAllowed(params) {
|
|
|
28075
28112
|
}))) {
|
|
28076
28113
|
return "not-approved";
|
|
28077
28114
|
}
|
|
28078
|
-
|
|
28079
|
-
if (!current || !snapshotMatches(current, params.snapshot)) {
|
|
28115
|
+
if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot))) {
|
|
28080
28116
|
return "changed";
|
|
28081
28117
|
}
|
|
28082
28118
|
try {
|
|
28083
|
-
await promises_.rm(params.lockPath
|
|
28119
|
+
await promises_.rm(params.lockPath);
|
|
28120
|
+
return "removed";
|
|
28084
28121
|
}
|
|
28085
28122
|
catch (err) {
|
|
28086
28123
|
if (err.code === "ENOENT") {
|
|
28087
28124
|
return "changed";
|
|
28088
28125
|
}
|
|
28089
|
-
|
|
28126
|
+
throw err;
|
|
28127
|
+
}
|
|
28128
|
+
}
|
|
28129
|
+
|
|
28130
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
28131
|
+
|
|
28132
|
+
|
|
28133
|
+
|
|
28134
|
+
|
|
28135
|
+
|
|
28136
|
+
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
28137
|
+
function getGlobalManagers() {
|
|
28138
|
+
const globalWithState = globalThis;
|
|
28139
|
+
if (!globalWithState[GLOBAL_STATE_KEY]) {
|
|
28140
|
+
globalWithState[GLOBAL_STATE_KEY] = new Map();
|
|
28141
|
+
}
|
|
28142
|
+
return globalWithState[GLOBAL_STATE_KEY];
|
|
28143
|
+
}
|
|
28144
|
+
function resolveManagerState(key) {
|
|
28145
|
+
const managers = getGlobalManagers();
|
|
28146
|
+
let state = managers.get(key);
|
|
28147
|
+
if (!state) {
|
|
28148
|
+
state = {
|
|
28149
|
+
cleanupRegistered: false,
|
|
28150
|
+
held: new Map(),
|
|
28151
|
+
reclaimCleanupRegistered: false,
|
|
28152
|
+
reclaimGuards: new Set(),
|
|
28153
|
+
};
|
|
28154
|
+
managers.set(key, state);
|
|
28155
|
+
}
|
|
28156
|
+
else {
|
|
28157
|
+
// The global manager symbol is shared across package copies and hot reloads.
|
|
28158
|
+
// Backfill state created by fs-safe versions that predate reclaim guards.
|
|
28159
|
+
state.reclaimCleanupRegistered ??= false;
|
|
28160
|
+
state.reclaimGuards ??= new Set();
|
|
28090
28161
|
}
|
|
28091
|
-
return
|
|
28162
|
+
return state;
|
|
28092
28163
|
}
|
|
28093
28164
|
function snapshotMatchesSync(lockPath, observed) {
|
|
28165
|
+
let fd;
|
|
28094
28166
|
try {
|
|
28095
|
-
const
|
|
28096
|
-
if (
|
|
28167
|
+
const beforeStat = external_node_fs_.lstatSync(lockPath);
|
|
28168
|
+
if (!beforeStat.isFile()) {
|
|
28169
|
+
return false;
|
|
28170
|
+
}
|
|
28171
|
+
const openFlags = external_node_fs_.constants.O_RDONLY |
|
|
28172
|
+
(process.platform !== "win32" && typeof external_node_fs_.constants.O_NOFOLLOW === "number"
|
|
28173
|
+
? external_node_fs_.constants.O_NOFOLLOW
|
|
28174
|
+
: 0) |
|
|
28175
|
+
(typeof external_node_fs_.constants.O_NONBLOCK === "number" ? external_node_fs_.constants.O_NONBLOCK : 0);
|
|
28176
|
+
fd = external_node_fs_.openSync(lockPath, openFlags);
|
|
28177
|
+
const openedStat = external_node_fs_.fstatSync(fd);
|
|
28178
|
+
if (!openedStat.isFile()) {
|
|
28097
28179
|
return false;
|
|
28098
28180
|
}
|
|
28099
|
-
|
|
28181
|
+
if (observed.raw !== undefined && openedStat.size !== Buffer.byteLength(observed.raw)) {
|
|
28182
|
+
return false;
|
|
28183
|
+
}
|
|
28184
|
+
const raw = external_node_fs_.readFileSync(fd, "utf8");
|
|
28185
|
+
const afterStat = external_node_fs_.lstatSync(lockPath);
|
|
28186
|
+
if (!afterStat.isFile() || !file_identity_sameFileIdentity(beforeStat, afterStat)) {
|
|
28187
|
+
return false;
|
|
28188
|
+
}
|
|
28189
|
+
return sidecarLockSnapshotMatches({ raw, payload: null, stat: afterStat }, observed);
|
|
28100
28190
|
}
|
|
28101
28191
|
catch {
|
|
28102
28192
|
return false;
|
|
28103
28193
|
}
|
|
28194
|
+
finally {
|
|
28195
|
+
if (fd !== undefined) {
|
|
28196
|
+
try {
|
|
28197
|
+
external_node_fs_.closeSync(fd);
|
|
28198
|
+
}
|
|
28199
|
+
catch {
|
|
28200
|
+
// Best-effort process-exit cleanup.
|
|
28201
|
+
}
|
|
28202
|
+
}
|
|
28203
|
+
}
|
|
28104
28204
|
}
|
|
28105
28205
|
async function resolveNormalizedTargetPath(targetPath) {
|
|
28106
28206
|
const resolved = external_node_path_.resolve(targetPath);
|
|
@@ -28135,6 +28235,17 @@ async function defaultShouldReclaim(params) {
|
|
|
28135
28235
|
return true;
|
|
28136
28236
|
}
|
|
28137
28237
|
}
|
|
28238
|
+
function releaseAllReclaimGuardsSync(state) {
|
|
28239
|
+
for (const reclaimGuardPath of state.reclaimGuards) {
|
|
28240
|
+
try {
|
|
28241
|
+
external_node_fs_.rmdirSync(reclaimGuardPath);
|
|
28242
|
+
state.reclaimGuards.delete(reclaimGuardPath);
|
|
28243
|
+
}
|
|
28244
|
+
catch {
|
|
28245
|
+
// Best-effort process-exit cleanup. A surviving guard fails closed.
|
|
28246
|
+
}
|
|
28247
|
+
}
|
|
28248
|
+
}
|
|
28138
28249
|
function releaseAllLocksSync(state) {
|
|
28139
28250
|
for (const [normalizedTargetPath, held] of state.held) {
|
|
28140
28251
|
void held.handle.close().catch(() => undefined);
|
|
@@ -28148,6 +28259,7 @@ function releaseAllLocksSync(state) {
|
|
|
28148
28259
|
}
|
|
28149
28260
|
state.held.delete(normalizedTargetPath);
|
|
28150
28261
|
}
|
|
28262
|
+
releaseAllReclaimGuardsSync(state);
|
|
28151
28263
|
}
|
|
28152
28264
|
async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
28153
28265
|
const current = state.held.get(normalizedTargetPath);
|
|
@@ -28170,7 +28282,7 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
28170
28282
|
state.held.delete(normalizedTargetPath);
|
|
28171
28283
|
held.releasePromise = (async () => {
|
|
28172
28284
|
await held.handle.close().catch(() => undefined);
|
|
28173
|
-
await
|
|
28285
|
+
await removeSidecarLockIfUnchanged(held.lockPath, held.snapshot);
|
|
28174
28286
|
})();
|
|
28175
28287
|
try {
|
|
28176
28288
|
await held.releasePromise;
|
|
@@ -28183,11 +28295,16 @@ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
|
28183
28295
|
function createSidecarLockManager(key) {
|
|
28184
28296
|
const state = resolveManagerState(key);
|
|
28185
28297
|
function ensureExitCleanupRegistered() {
|
|
28186
|
-
if (state.cleanupRegistered) {
|
|
28298
|
+
if (!state.cleanupRegistered) {
|
|
28299
|
+
state.cleanupRegistered = true;
|
|
28300
|
+
state.reclaimCleanupRegistered = true;
|
|
28301
|
+
process.on("exit", () => releaseAllLocksSync(state));
|
|
28187
28302
|
return;
|
|
28188
28303
|
}
|
|
28189
|
-
state.
|
|
28190
|
-
|
|
28304
|
+
if (!state.reclaimCleanupRegistered) {
|
|
28305
|
+
state.reclaimCleanupRegistered = true;
|
|
28306
|
+
process.on("exit", () => releaseAllReclaimGuardsSync(state));
|
|
28307
|
+
}
|
|
28191
28308
|
}
|
|
28192
28309
|
async function acquire(options) {
|
|
28193
28310
|
ensureExitCleanupRegistered();
|
|
@@ -28207,108 +28324,146 @@ function createSidecarLockManager(key) {
|
|
|
28207
28324
|
const startedAt = Date.now();
|
|
28208
28325
|
const retry = options.retry ?? {};
|
|
28209
28326
|
const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
|
|
28327
|
+
const reclaimGuardPath = `${lockPath}.reclaim`;
|
|
28328
|
+
let ownsReclaimGuard = false;
|
|
28210
28329
|
let attempt = 0;
|
|
28211
|
-
|
|
28212
|
-
|
|
28213
|
-
|
|
28214
|
-
|
|
28215
|
-
|
|
28216
|
-
|
|
28217
|
-
|
|
28218
|
-
|
|
28219
|
-
const createdHeld = {
|
|
28220
|
-
count: 1,
|
|
28221
|
-
handle,
|
|
28222
|
-
lockPath,
|
|
28223
|
-
snapshot,
|
|
28224
|
-
acquiredAt: Date.now(),
|
|
28225
|
-
metadata: options.metadata ?? {},
|
|
28226
|
-
};
|
|
28227
|
-
state.held.set(normalizedTargetPath, createdHeld);
|
|
28228
|
-
const release = () => releaseHeldLock(state, normalizedTargetPath, createdHeld).then(() => undefined);
|
|
28229
|
-
return {
|
|
28330
|
+
const waitForRetry = async () => {
|
|
28331
|
+
const elapsed = Date.now() - startedAt;
|
|
28332
|
+
if ((options.timeoutMs !== undefined &&
|
|
28333
|
+
options.timeoutMs !== Number.POSITIVE_INFINITY &&
|
|
28334
|
+
elapsed >= options.timeoutMs) ||
|
|
28335
|
+
(maxRetries !== undefined && attempt >= maxRetries)) {
|
|
28336
|
+
throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
28337
|
+
code: "file_lock_timeout",
|
|
28230
28338
|
lockPath,
|
|
28231
28339
|
normalizedTargetPath,
|
|
28232
|
-
|
|
28233
|
-
[Symbol.asyncDispose]: release,
|
|
28234
|
-
};
|
|
28340
|
+
});
|
|
28235
28341
|
}
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
|
|
28242
|
-
|
|
28243
|
-
|
|
28244
|
-
|
|
28245
|
-
|
|
28246
|
-
|
|
28247
|
-
state.held.delete(normalizedTargetPath);
|
|
28248
|
-
}
|
|
28249
|
-
// If payload serialization/write fails, the file may be empty or
|
|
28250
|
-
// partial JSON, so remove while our exclusive handle is still open.
|
|
28251
|
-
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28252
|
-
await handle.close().catch(() => undefined);
|
|
28253
|
-
// Windows can refuse removing an open file; retry after close but
|
|
28254
|
-
// only if the path still points at the file identity we created.
|
|
28255
|
-
await removeLockIfUnchanged(lockPath, failedSnapshot);
|
|
28256
|
-
}
|
|
28257
|
-
if (err.code !== "EEXIST") {
|
|
28258
|
-
throw err;
|
|
28259
|
-
}
|
|
28260
|
-
const nowMs = Date.now();
|
|
28261
|
-
const snapshot = await readLockSnapshot(lockPath);
|
|
28262
|
-
if (!snapshot) {
|
|
28342
|
+
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
28343
|
+
? Number.POSITIVE_INFINITY
|
|
28344
|
+
: Math.max(0, options.timeoutMs - elapsed);
|
|
28345
|
+
const delay = Math.min(computeDelayMs(retry, attempt), remaining);
|
|
28346
|
+
attempt += 1;
|
|
28347
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28348
|
+
};
|
|
28349
|
+
try {
|
|
28350
|
+
while (true) {
|
|
28351
|
+
if (!ownsReclaimGuard && (await sidecarReclaimGuardExists(reclaimGuardPath))) {
|
|
28352
|
+
await waitForRetry();
|
|
28263
28353
|
continue;
|
|
28264
28354
|
}
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
lockPath,
|
|
28268
|
-
|
|
28269
|
-
|
|
28270
|
-
|
|
28271
|
-
|
|
28272
|
-
|
|
28273
|
-
|
|
28274
|
-
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
28278
|
-
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28355
|
+
let handle = null;
|
|
28356
|
+
try {
|
|
28357
|
+
handle = await promises_.open(lockPath, "wx");
|
|
28358
|
+
const payload = await options.payload();
|
|
28359
|
+
const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
|
|
28360
|
+
await handle.writeFile(raw, "utf8");
|
|
28361
|
+
const snapshot = { raw, payload, stat: await handle.stat(), ownershipToken };
|
|
28362
|
+
const createdHeld = {
|
|
28363
|
+
count: 1,
|
|
28364
|
+
handle,
|
|
28365
|
+
lockPath,
|
|
28366
|
+
snapshot,
|
|
28367
|
+
acquiredAt: Date.now(),
|
|
28368
|
+
metadata: options.metadata ?? {},
|
|
28369
|
+
};
|
|
28370
|
+
state.held.set(normalizedTargetPath, createdHeld);
|
|
28371
|
+
if (ownsReclaimGuard) {
|
|
28372
|
+
try {
|
|
28373
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28374
|
+
ownsReclaimGuard = false;
|
|
28375
|
+
}
|
|
28376
|
+
catch (err) {
|
|
28377
|
+
await releaseHeldLock(state, normalizedTargetPath, createdHeld, { force: true });
|
|
28378
|
+
throw err;
|
|
28287
28379
|
}
|
|
28288
28380
|
}
|
|
28289
|
-
|
|
28290
|
-
|
|
28381
|
+
const release = () => releaseHeldLock(state, normalizedTargetPath, createdHeld).then(() => undefined);
|
|
28382
|
+
return {
|
|
28291
28383
|
lockPath,
|
|
28292
28384
|
normalizedTargetPath,
|
|
28293
|
-
|
|
28385
|
+
release,
|
|
28386
|
+
[Symbol.asyncDispose]: release,
|
|
28387
|
+
};
|
|
28294
28388
|
}
|
|
28295
|
-
|
|
28296
|
-
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28301
|
-
|
|
28389
|
+
catch (err) {
|
|
28390
|
+
if (handle) {
|
|
28391
|
+
const failedSnapshot = { payload: null };
|
|
28392
|
+
try {
|
|
28393
|
+
failedSnapshot.stat = await handle.stat();
|
|
28394
|
+
}
|
|
28395
|
+
catch {
|
|
28396
|
+
// Best-effort cleanup of a failed exclusive create.
|
|
28397
|
+
}
|
|
28398
|
+
const current = state.held.get(normalizedTargetPath);
|
|
28399
|
+
if (current?.handle === handle) {
|
|
28400
|
+
state.held.delete(normalizedTargetPath);
|
|
28401
|
+
}
|
|
28402
|
+
// If payload serialization/write fails, the file may be empty or
|
|
28403
|
+
// partial JSON, so remove while our exclusive handle is still open.
|
|
28404
|
+
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28405
|
+
await handle.close().catch(() => undefined);
|
|
28406
|
+
// Windows can refuse removing an open file; retry after close but
|
|
28407
|
+
// only if the path still points at the file identity we created.
|
|
28408
|
+
await removeSidecarLockIfUnchanged(lockPath, failedSnapshot);
|
|
28409
|
+
}
|
|
28410
|
+
if (err.code !== "EEXIST") {
|
|
28411
|
+
throw err;
|
|
28412
|
+
}
|
|
28413
|
+
if (ownsReclaimGuard) {
|
|
28414
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28415
|
+
ownsReclaimGuard = false;
|
|
28416
|
+
continue;
|
|
28417
|
+
}
|
|
28418
|
+
const nowMs = Date.now();
|
|
28419
|
+
const snapshot = await readSidecarLockSnapshot(lockPath);
|
|
28420
|
+
if (!snapshot) {
|
|
28421
|
+
continue;
|
|
28422
|
+
}
|
|
28423
|
+
const shouldReclaim = options.shouldReclaim ?? defaultShouldReclaim;
|
|
28424
|
+
if (await shouldReclaim({
|
|
28302
28425
|
lockPath,
|
|
28303
28426
|
normalizedTargetPath,
|
|
28304
|
-
|
|
28427
|
+
payload: snapshot?.payload ?? null,
|
|
28428
|
+
staleMs: options.staleMs,
|
|
28429
|
+
nowMs,
|
|
28430
|
+
heldByThisProcess: state.held.has(normalizedTargetPath),
|
|
28431
|
+
})) {
|
|
28432
|
+
if (!(await sidecarLockSnapshotStillPresent(lockPath, snapshot))) {
|
|
28433
|
+
continue;
|
|
28434
|
+
}
|
|
28435
|
+
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
28436
|
+
if (staleRecovery === "remove-if-unchanged") {
|
|
28437
|
+
if (!(await tryAcquireSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath))) {
|
|
28438
|
+
await waitForRetry();
|
|
28439
|
+
continue;
|
|
28440
|
+
}
|
|
28441
|
+
ownsReclaimGuard = true;
|
|
28442
|
+
const removal = await removeStaleSidecarLockIfAllowed({
|
|
28443
|
+
lockPath,
|
|
28444
|
+
normalizedTargetPath,
|
|
28445
|
+
snapshot,
|
|
28446
|
+
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
28447
|
+
});
|
|
28448
|
+
if (removal === "removed" || removal === "changed") {
|
|
28449
|
+
continue;
|
|
28450
|
+
}
|
|
28451
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath);
|
|
28452
|
+
ownsReclaimGuard = false;
|
|
28453
|
+
}
|
|
28454
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
28455
|
+
code: "file_lock_stale",
|
|
28456
|
+
lockPath,
|
|
28457
|
+
normalizedTargetPath,
|
|
28458
|
+
});
|
|
28459
|
+
}
|
|
28460
|
+
await waitForRetry();
|
|
28305
28461
|
}
|
|
28306
|
-
|
|
28307
|
-
|
|
28308
|
-
|
|
28309
|
-
|
|
28310
|
-
|
|
28311
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28462
|
+
}
|
|
28463
|
+
}
|
|
28464
|
+
finally {
|
|
28465
|
+
if (ownsReclaimGuard) {
|
|
28466
|
+
await releaseSidecarReclaimGuard(state.reclaimGuards, reclaimGuardPath).catch(() => undefined);
|
|
28312
28467
|
}
|
|
28313
28468
|
}
|
|
28314
28469
|
}
|
|
@@ -28395,6 +28550,16 @@ function assertWithinMaxBytes(bytes, maxBytes) {
|
|
|
28395
28550
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
|
|
28396
28551
|
}
|
|
28397
28552
|
}
|
|
28553
|
+
async function syncFileBestEffort(handle) {
|
|
28554
|
+
try {
|
|
28555
|
+
await handle.sync();
|
|
28556
|
+
}
|
|
28557
|
+
catch (error) {
|
|
28558
|
+
if (error?.code !== "EPERM") {
|
|
28559
|
+
throw error;
|
|
28560
|
+
}
|
|
28561
|
+
}
|
|
28562
|
+
}
|
|
28398
28563
|
async function writeStreamToHandle(stream, handle, maxBytes) {
|
|
28399
28564
|
let bytes = 0;
|
|
28400
28565
|
for await (const chunk of stream) {
|
|
@@ -28556,7 +28721,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
28556
28721
|
else {
|
|
28557
28722
|
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
28558
28723
|
}
|
|
28559
|
-
await handle
|
|
28724
|
+
await syncFileBestEffort(handle);
|
|
28560
28725
|
const stat = await handle.stat();
|
|
28561
28726
|
await handle.close().catch(() => undefined);
|
|
28562
28727
|
await syncDirectoryBestEffort(parentPath);
|
|
@@ -28601,7 +28766,7 @@ async function runPinnedWriteFallback(params) {
|
|
|
28601
28766
|
throw new errors_FsSafeError("path-mismatch", "fallback temp path changed during write");
|
|
28602
28767
|
}
|
|
28603
28768
|
const expectedTempStat = tempStat;
|
|
28604
|
-
await handle
|
|
28769
|
+
await syncFileBestEffort(handle);
|
|
28605
28770
|
await handle.close().catch(() => undefined);
|
|
28606
28771
|
handle = undefined;
|
|
28607
28772
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
@@ -29510,16 +29675,104 @@ function assertNoUnsafeDeviceReadPath(filePath, options) {
|
|
|
29510
29675
|
}
|
|
29511
29676
|
}
|
|
29512
29677
|
|
|
29678
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/bounded-read.js
|
|
29679
|
+
|
|
29680
|
+
|
|
29681
|
+
const READ_CHUNK_BYTES = 64 * 1024;
|
|
29682
|
+
function assertMaxBytes(maxBytes) {
|
|
29683
|
+
if (maxBytes === Number.POSITIVE_INFINITY) {
|
|
29684
|
+
return;
|
|
29685
|
+
}
|
|
29686
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
29687
|
+
throw new RangeError("maxBytes must be a non-negative safe integer or Infinity");
|
|
29688
|
+
}
|
|
29689
|
+
}
|
|
29690
|
+
function createScratchBuffer(maxBytes) {
|
|
29691
|
+
const initialReadBytes = Number.isFinite(maxBytes)
|
|
29692
|
+
? Math.min(READ_CHUNK_BYTES, maxBytes + 1)
|
|
29693
|
+
: READ_CHUNK_BYTES;
|
|
29694
|
+
return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
|
|
29695
|
+
}
|
|
29696
|
+
function nextReadLength(total, maxBytes, capacity) {
|
|
29697
|
+
return Number.isFinite(maxBytes)
|
|
29698
|
+
? Math.min(capacity, maxBytes - total + 1)
|
|
29699
|
+
: capacity;
|
|
29700
|
+
}
|
|
29701
|
+
function appendChunk(params) {
|
|
29702
|
+
const total = params.total + params.bytesRead;
|
|
29703
|
+
if (total > params.maxBytes) {
|
|
29704
|
+
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got at least ${total})`);
|
|
29705
|
+
}
|
|
29706
|
+
params.chunks.push(Buffer.from(params.scratch.subarray(0, params.bytesRead)));
|
|
29707
|
+
return total;
|
|
29708
|
+
}
|
|
29709
|
+
async function readBoundedAsync(maxBytes, readChunk) {
|
|
29710
|
+
assertMaxBytes(maxBytes);
|
|
29711
|
+
const chunks = [];
|
|
29712
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
29713
|
+
let total = 0;
|
|
29714
|
+
while (true) {
|
|
29715
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
29716
|
+
const bytesRead = await readChunk(scratch, length);
|
|
29717
|
+
if (bytesRead === 0) {
|
|
29718
|
+
return Buffer.concat(chunks, total);
|
|
29719
|
+
}
|
|
29720
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
29721
|
+
}
|
|
29722
|
+
}
|
|
29723
|
+
/**
|
|
29724
|
+
* Reads from the handle's current offset without closing it. A bounded read
|
|
29725
|
+
* consumes at most maxBytes + 1 bytes so growth after an earlier stat cannot
|
|
29726
|
+
* force an unbounded allocation.
|
|
29727
|
+
*/
|
|
29728
|
+
async function readFileHandleBounded(handle, maxBytes) {
|
|
29729
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
29730
|
+
return (await handle.read(scratch, 0, length, null)).bytesRead;
|
|
29731
|
+
});
|
|
29732
|
+
}
|
|
29733
|
+
function readDescriptorChunk(fd, scratch, length) {
|
|
29734
|
+
return new Promise((resolve, reject) => {
|
|
29735
|
+
fs.read(fd, scratch, 0, length, null, (error, bytesRead) => {
|
|
29736
|
+
if (error) {
|
|
29737
|
+
reject(error);
|
|
29738
|
+
return;
|
|
29739
|
+
}
|
|
29740
|
+
resolve(bytesRead);
|
|
29741
|
+
});
|
|
29742
|
+
});
|
|
29743
|
+
}
|
|
29744
|
+
/** Async bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
29745
|
+
async function readFileDescriptorBounded(fd, maxBytes) {
|
|
29746
|
+
return await readBoundedAsync(maxBytes, async (scratch, length) => {
|
|
29747
|
+
return await readDescriptorChunk(fd, scratch, length);
|
|
29748
|
+
});
|
|
29749
|
+
}
|
|
29750
|
+
/** Sync bounded read from a numeric descriptor. The caller owns the descriptor. */
|
|
29751
|
+
function readFileDescriptorBoundedSync(fd, maxBytes) {
|
|
29752
|
+
assertMaxBytes(maxBytes);
|
|
29753
|
+
const chunks = [];
|
|
29754
|
+
const scratch = createScratchBuffer(maxBytes);
|
|
29755
|
+
let total = 0;
|
|
29756
|
+
while (true) {
|
|
29757
|
+
const length = nextReadLength(total, maxBytes, scratch.length);
|
|
29758
|
+
const bytesRead = fs.readSync(fd, scratch, 0, length, null);
|
|
29759
|
+
if (bytesRead === 0) {
|
|
29760
|
+
return Buffer.concat(chunks, total);
|
|
29761
|
+
}
|
|
29762
|
+
total = appendChunk({ chunks, scratch, bytesRead, total, maxBytes });
|
|
29763
|
+
}
|
|
29764
|
+
}
|
|
29765
|
+
|
|
29513
29766
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/read-opened-file.js
|
|
29514
29767
|
|
|
29768
|
+
|
|
29515
29769
|
async function read_opened_file_readOpenedFileSafely(params) {
|
|
29516
29770
|
if (params.maxBytes !== undefined && params.opened.stat.size > params.maxBytes) {
|
|
29517
29771
|
throw new errors_FsSafeError("too-large", `file exceeds limit of ${params.maxBytes} bytes (got ${params.opened.stat.size})`);
|
|
29518
29772
|
}
|
|
29519
|
-
const buffer =
|
|
29520
|
-
|
|
29521
|
-
|
|
29522
|
-
}
|
|
29773
|
+
const buffer = params.maxBytes === undefined
|
|
29774
|
+
? await params.opened.handle.readFile()
|
|
29775
|
+
: await readFileHandleBounded(params.opened.handle, params.maxBytes);
|
|
29523
29776
|
return {
|
|
29524
29777
|
buffer,
|
|
29525
29778
|
realPath: params.opened.realPath,
|
|
@@ -59503,6 +59756,10 @@ function serialize (cmpts, opts) {
|
|
|
59503
59756
|
|
|
59504
59757
|
const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u
|
|
59505
59758
|
|
|
59759
|
+
// Captures the authority component (between "//" and the next "/", "?" or "#"),
|
|
59760
|
+
// with or without a scheme prefix, for the literal-backslash rejection below.
|
|
59761
|
+
const AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/
|
|
59762
|
+
|
|
59506
59763
|
/**
|
|
59507
59764
|
* @param {import('./types/index').URIComponent} parsed
|
|
59508
59765
|
* @param {RegExpMatchArray} matches
|
|
@@ -59549,6 +59806,19 @@ function parseWithStatus (uri, opts) {
|
|
|
59549
59806
|
}
|
|
59550
59807
|
}
|
|
59551
59808
|
|
|
59809
|
+
// A literal backslash (U+005C) is not a valid RFC 3986 URI character and is
|
|
59810
|
+
// not an authority delimiter. Reject it in the authority rather than
|
|
59811
|
+
// rewriting it: normalizing "\" -> "/" (WHATWG error recovery) could silently
|
|
59812
|
+
// change the resource identified by an otherwise-invalid input, and lets "\"
|
|
59813
|
+
// act as a host delimiter here while Node's native URL parses a different
|
|
59814
|
+
// host (SSRF / redirect / origin-allowlist bypass). Percent-encoded %5C is
|
|
59815
|
+
// untouched and remains valid encoded data.
|
|
59816
|
+
const authorityMatch = uri.match(AUTHORITY_PREFIX)
|
|
59817
|
+
if (authorityMatch !== null && authorityMatch[1].indexOf('\\') !== -1) {
|
|
59818
|
+
parsed.error = 'URI authority must not contain a literal backslash.'
|
|
59819
|
+
malformedAuthorityOrPort = true
|
|
59820
|
+
}
|
|
59821
|
+
|
|
59552
59822
|
const matches = uri.match(URI_PARSE)
|
|
59553
59823
|
|
|
59554
59824
|
if (matches) {
|
|
@@ -68989,4 +69259,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
68989
69259
|
// module factories are used so entry inlining is disabled
|
|
68990
69260
|
// startup
|
|
68991
69261
|
// Load entry module and return exports
|
|
68992
|
-
var __webpack_exports__ = __webpack_require__(
|
|
69262
|
+
var __webpack_exports__ = __webpack_require__(3503);
|