@lousy-agents/mcp 5.15.7 → 5.17.0
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 +513 -66
- 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
|
+
6887(__unused_rspack_module, __unused_rspack___webpack_exports__, __webpack_require__) {
|
|
6563
6563
|
// NAMESPACE OBJECT: ../../node_modules/micromark/lib/constructs.js
|
|
6564
6564
|
var constructs_namespaceObject = {};
|
|
6565
6565
|
__webpack_require__.r(constructs_namespaceObject);
|
|
@@ -26479,6 +26479,8 @@ const EMPTY_COMPLETION_RESULT = {
|
|
|
26479
26479
|
}
|
|
26480
26480
|
};
|
|
26481
26481
|
//# sourceMappingURL=mcp.js.map
|
|
26482
|
+
// EXTERNAL MODULE: external "node:fs/promises"
|
|
26483
|
+
var promises_ = __webpack_require__(1455);
|
|
26482
26484
|
// EXTERNAL MODULE: external "node:path"
|
|
26483
26485
|
var external_node_path_ = __webpack_require__(6760);
|
|
26484
26486
|
;// CONCATENATED MODULE: ../core/src/gateways/action-version-gateway.ts
|
|
@@ -26524,8 +26526,6 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
26524
26526
|
* List of known action names for convenience
|
|
26525
26527
|
*/ const KNOWN_ACTIONS = Object.keys(DEFAULT_ACTION_VERSIONS);
|
|
26526
26528
|
|
|
26527
|
-
// EXTERNAL MODULE: external "node:fs/promises"
|
|
26528
|
-
var promises_ = __webpack_require__(1455);
|
|
26529
26529
|
// EXTERNAL MODULE: external "node:fs"
|
|
26530
26530
|
var external_node_fs_ = __webpack_require__(3024);
|
|
26531
26531
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/errors.js
|
|
@@ -26579,16 +26579,24 @@ function createBoundedReadStream(opened, maxBytes) {
|
|
|
26579
26579
|
}
|
|
26580
26580
|
|
|
26581
26581
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/file-identity.js
|
|
26582
|
+
|
|
26582
26583
|
function isZero(value) {
|
|
26583
26584
|
return value === 0 || value === 0n;
|
|
26584
26585
|
}
|
|
26586
|
+
function sameStatValue(left, right) {
|
|
26587
|
+
return typeof left === typeof right ? left === right : BigInt(left) === BigInt(right);
|
|
26588
|
+
}
|
|
26589
|
+
function sha256Hex(data, encoding) {
|
|
26590
|
+
const buffer = typeof data === "string" ? Buffer.from(data, encoding ?? "utf8") : data;
|
|
26591
|
+
return (0,external_node_crypto_.createHash)("sha256").update(buffer).digest("hex");
|
|
26592
|
+
}
|
|
26585
26593
|
function file_identity_sameFileIdentity(left, right, platform = process.platform) {
|
|
26586
|
-
if (left.ino
|
|
26594
|
+
if (!sameStatValue(left.ino, right.ino)) {
|
|
26587
26595
|
return false;
|
|
26588
26596
|
}
|
|
26589
26597
|
// On Windows, path-based stat calls can report dev=0 while fd-based stat
|
|
26590
26598
|
// reports a real volume serial; treat either-side dev=0 as "unknown device".
|
|
26591
|
-
if (left.dev
|
|
26599
|
+
if (sameStatValue(left.dev, right.dev)) {
|
|
26592
26600
|
return true;
|
|
26593
26601
|
}
|
|
26594
26602
|
return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
|
|
@@ -27982,6 +27990,377 @@ async function runPinnedPathHelper(params) {
|
|
|
27982
27990
|
}
|
|
27983
27991
|
}
|
|
27984
27992
|
|
|
27993
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
|
|
27994
|
+
|
|
27995
|
+
|
|
27996
|
+
|
|
27997
|
+
|
|
27998
|
+
const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
|
|
27999
|
+
function getGlobalManagers() {
|
|
28000
|
+
const globalWithState = globalThis;
|
|
28001
|
+
if (!globalWithState[GLOBAL_STATE_KEY]) {
|
|
28002
|
+
globalWithState[GLOBAL_STATE_KEY] = new Map();
|
|
28003
|
+
}
|
|
28004
|
+
return globalWithState[GLOBAL_STATE_KEY];
|
|
28005
|
+
}
|
|
28006
|
+
function resolveManagerState(key) {
|
|
28007
|
+
const managers = getGlobalManagers();
|
|
28008
|
+
let state = managers.get(key);
|
|
28009
|
+
if (!state) {
|
|
28010
|
+
state = { cleanupRegistered: false, held: new Map() };
|
|
28011
|
+
managers.set(key, state);
|
|
28012
|
+
}
|
|
28013
|
+
return state;
|
|
28014
|
+
}
|
|
28015
|
+
async function readLockSnapshot(lockPath) {
|
|
28016
|
+
try {
|
|
28017
|
+
const stat = await promises_.lstat(lockPath);
|
|
28018
|
+
const raw = await promises_.readFile(lockPath, "utf8");
|
|
28019
|
+
try {
|
|
28020
|
+
const parsed = JSON.parse(raw);
|
|
28021
|
+
const payload = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
28022
|
+
? parsed
|
|
28023
|
+
: null;
|
|
28024
|
+
return { raw, payload, stat };
|
|
28025
|
+
}
|
|
28026
|
+
catch {
|
|
28027
|
+
return { raw, payload: null, stat };
|
|
28028
|
+
}
|
|
28029
|
+
}
|
|
28030
|
+
catch (err) {
|
|
28031
|
+
if (err.code === "ENOENT") {
|
|
28032
|
+
return null;
|
|
28033
|
+
}
|
|
28034
|
+
throw err;
|
|
28035
|
+
}
|
|
28036
|
+
}
|
|
28037
|
+
function snapshotMatches(current, observed) {
|
|
28038
|
+
if (observed.stat && current.stat && !file_identity_sameFileIdentity(observed.stat, current.stat)) {
|
|
28039
|
+
return false;
|
|
28040
|
+
}
|
|
28041
|
+
if (observed.raw !== undefined) {
|
|
28042
|
+
return current.raw === observed.raw;
|
|
28043
|
+
}
|
|
28044
|
+
return observed.stat !== undefined && current.stat !== undefined;
|
|
28045
|
+
}
|
|
28046
|
+
async function removeLockIfUnchanged(lockPath, observed) {
|
|
28047
|
+
const current = await readLockSnapshot(lockPath);
|
|
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.
|
|
28054
|
+
return false;
|
|
28055
|
+
}
|
|
28056
|
+
await promises_.rm(lockPath, { force: true }).catch(() => undefined);
|
|
28057
|
+
return true;
|
|
28058
|
+
}
|
|
28059
|
+
async function lockSnapshotStillPresent(lockPath, observed) {
|
|
28060
|
+
const current = await readLockSnapshot(lockPath);
|
|
28061
|
+
return !!current && !!observed && snapshotMatches(current, observed);
|
|
28062
|
+
}
|
|
28063
|
+
async function removeStaleLockIfAllowed(params) {
|
|
28064
|
+
if (!params.shouldRemoveStaleLock) {
|
|
28065
|
+
return "not-approved";
|
|
28066
|
+
}
|
|
28067
|
+
if (params.snapshot.raw === undefined) {
|
|
28068
|
+
return "not-approved";
|
|
28069
|
+
}
|
|
28070
|
+
if (!(await params.shouldRemoveStaleLock({
|
|
28071
|
+
lockPath: params.lockPath,
|
|
28072
|
+
normalizedTargetPath: params.normalizedTargetPath,
|
|
28073
|
+
raw: params.snapshot.raw,
|
|
28074
|
+
payload: params.snapshot.payload,
|
|
28075
|
+
}))) {
|
|
28076
|
+
return "not-approved";
|
|
28077
|
+
}
|
|
28078
|
+
const current = await readLockSnapshot(params.lockPath);
|
|
28079
|
+
if (!current || !snapshotMatches(current, params.snapshot)) {
|
|
28080
|
+
return "changed";
|
|
28081
|
+
}
|
|
28082
|
+
try {
|
|
28083
|
+
await promises_.rm(params.lockPath, { force: true });
|
|
28084
|
+
}
|
|
28085
|
+
catch (err) {
|
|
28086
|
+
if (err.code === "ENOENT") {
|
|
28087
|
+
return "changed";
|
|
28088
|
+
}
|
|
28089
|
+
return "not-approved";
|
|
28090
|
+
}
|
|
28091
|
+
return "removed";
|
|
28092
|
+
}
|
|
28093
|
+
function snapshotMatchesSync(lockPath, observed) {
|
|
28094
|
+
try {
|
|
28095
|
+
const stat = external_node_fs_.lstatSync(lockPath);
|
|
28096
|
+
if (observed.stat && !file_identity_sameFileIdentity(observed.stat, stat)) {
|
|
28097
|
+
return false;
|
|
28098
|
+
}
|
|
28099
|
+
return observed.raw === undefined || external_node_fs_.readFileSync(lockPath, "utf8") === observed.raw;
|
|
28100
|
+
}
|
|
28101
|
+
catch {
|
|
28102
|
+
return false;
|
|
28103
|
+
}
|
|
28104
|
+
}
|
|
28105
|
+
async function resolveNormalizedTargetPath(targetPath) {
|
|
28106
|
+
const resolved = external_node_path_.resolve(targetPath);
|
|
28107
|
+
const dir = external_node_path_.dirname(resolved);
|
|
28108
|
+
await promises_.mkdir(dir, { recursive: true });
|
|
28109
|
+
try {
|
|
28110
|
+
return external_node_path_.join(await promises_.realpath(dir), external_node_path_.basename(resolved));
|
|
28111
|
+
}
|
|
28112
|
+
catch {
|
|
28113
|
+
return resolved;
|
|
28114
|
+
}
|
|
28115
|
+
}
|
|
28116
|
+
function computeDelayMs(retry, attempt) {
|
|
28117
|
+
const minTimeout = retry.minTimeout ?? 50;
|
|
28118
|
+
const maxTimeout = retry.maxTimeout ?? 1000;
|
|
28119
|
+
const factor = retry.factor ?? 1;
|
|
28120
|
+
const base = Math.min(maxTimeout, Math.max(minTimeout, minTimeout * factor ** attempt));
|
|
28121
|
+
const jitter = retry.randomize ? 1 + Math.random() : 1;
|
|
28122
|
+
return Math.min(maxTimeout, Math.round(base * jitter));
|
|
28123
|
+
}
|
|
28124
|
+
async function defaultShouldReclaim(params) {
|
|
28125
|
+
const createdAt = typeof params.payload?.createdAt === "string" ? params.payload.createdAt : "";
|
|
28126
|
+
const createdAtMs = Date.parse(createdAt);
|
|
28127
|
+
if (Number.isFinite(createdAtMs) && params.nowMs - createdAtMs > params.staleMs) {
|
|
28128
|
+
return true;
|
|
28129
|
+
}
|
|
28130
|
+
try {
|
|
28131
|
+
const stat = await promises_.stat(params.lockPath);
|
|
28132
|
+
return params.nowMs - stat.mtimeMs > params.staleMs;
|
|
28133
|
+
}
|
|
28134
|
+
catch {
|
|
28135
|
+
return true;
|
|
28136
|
+
}
|
|
28137
|
+
}
|
|
28138
|
+
function releaseAllLocksSync(state) {
|
|
28139
|
+
for (const [normalizedTargetPath, held] of state.held) {
|
|
28140
|
+
void held.handle.close().catch(() => undefined);
|
|
28141
|
+
try {
|
|
28142
|
+
if (snapshotMatchesSync(held.lockPath, held.snapshot)) {
|
|
28143
|
+
external_node_fs_.rmSync(held.lockPath, { force: true });
|
|
28144
|
+
}
|
|
28145
|
+
}
|
|
28146
|
+
catch {
|
|
28147
|
+
// Best-effort process-exit cleanup.
|
|
28148
|
+
}
|
|
28149
|
+
state.held.delete(normalizedTargetPath);
|
|
28150
|
+
}
|
|
28151
|
+
}
|
|
28152
|
+
async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
|
|
28153
|
+
const current = state.held.get(normalizedTargetPath);
|
|
28154
|
+
if (current !== held) {
|
|
28155
|
+
return false;
|
|
28156
|
+
}
|
|
28157
|
+
if (opts.force) {
|
|
28158
|
+
held.count = 0;
|
|
28159
|
+
}
|
|
28160
|
+
else {
|
|
28161
|
+
held.count -= 1;
|
|
28162
|
+
if (held.count > 0) {
|
|
28163
|
+
return false;
|
|
28164
|
+
}
|
|
28165
|
+
}
|
|
28166
|
+
if (held.releasePromise) {
|
|
28167
|
+
await held.releasePromise.catch(() => undefined);
|
|
28168
|
+
return true;
|
|
28169
|
+
}
|
|
28170
|
+
state.held.delete(normalizedTargetPath);
|
|
28171
|
+
held.releasePromise = (async () => {
|
|
28172
|
+
await held.handle.close().catch(() => undefined);
|
|
28173
|
+
await removeLockIfUnchanged(held.lockPath, held.snapshot);
|
|
28174
|
+
})();
|
|
28175
|
+
try {
|
|
28176
|
+
await held.releasePromise;
|
|
28177
|
+
return true;
|
|
28178
|
+
}
|
|
28179
|
+
finally {
|
|
28180
|
+
held.releasePromise = undefined;
|
|
28181
|
+
}
|
|
28182
|
+
}
|
|
28183
|
+
function createSidecarLockManager(key) {
|
|
28184
|
+
const state = resolveManagerState(key);
|
|
28185
|
+
function ensureExitCleanupRegistered() {
|
|
28186
|
+
if (state.cleanupRegistered) {
|
|
28187
|
+
return;
|
|
28188
|
+
}
|
|
28189
|
+
state.cleanupRegistered = true;
|
|
28190
|
+
process.on("exit", () => releaseAllLocksSync(state));
|
|
28191
|
+
}
|
|
28192
|
+
async function acquire(options) {
|
|
28193
|
+
ensureExitCleanupRegistered();
|
|
28194
|
+
const normalizedTargetPath = await resolveNormalizedTargetPath(options.targetPath);
|
|
28195
|
+
const lockPath = options.lockPath ?? `${normalizedTargetPath}.lock`;
|
|
28196
|
+
const held = state.held.get(normalizedTargetPath);
|
|
28197
|
+
if (held && options.allowReentrant) {
|
|
28198
|
+
held.count += 1;
|
|
28199
|
+
const release = () => releaseHeldLock(state, normalizedTargetPath, held).then(() => undefined);
|
|
28200
|
+
return {
|
|
28201
|
+
lockPath,
|
|
28202
|
+
normalizedTargetPath,
|
|
28203
|
+
release,
|
|
28204
|
+
[Symbol.asyncDispose]: release,
|
|
28205
|
+
};
|
|
28206
|
+
}
|
|
28207
|
+
const startedAt = Date.now();
|
|
28208
|
+
const retry = options.retry ?? {};
|
|
28209
|
+
const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
|
|
28210
|
+
let attempt = 0;
|
|
28211
|
+
while (true) {
|
|
28212
|
+
let handle = null;
|
|
28213
|
+
try {
|
|
28214
|
+
handle = await promises_.open(lockPath, "wx");
|
|
28215
|
+
const payload = await options.payload();
|
|
28216
|
+
const raw = `${JSON.stringify(payload, null, 2)}\n`;
|
|
28217
|
+
await handle.writeFile(raw, "utf8");
|
|
28218
|
+
const snapshot = { raw, payload, stat: await handle.stat() };
|
|
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 {
|
|
28230
|
+
lockPath,
|
|
28231
|
+
normalizedTargetPath,
|
|
28232
|
+
release,
|
|
28233
|
+
[Symbol.asyncDispose]: release,
|
|
28234
|
+
};
|
|
28235
|
+
}
|
|
28236
|
+
catch (err) {
|
|
28237
|
+
if (handle) {
|
|
28238
|
+
const failedSnapshot = { payload: null };
|
|
28239
|
+
try {
|
|
28240
|
+
failedSnapshot.stat = await handle.stat();
|
|
28241
|
+
}
|
|
28242
|
+
catch {
|
|
28243
|
+
// Best-effort cleanup of a failed exclusive create.
|
|
28244
|
+
}
|
|
28245
|
+
const current = state.held.get(normalizedTargetPath);
|
|
28246
|
+
if (current?.handle === handle) {
|
|
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) {
|
|
28263
|
+
continue;
|
|
28264
|
+
}
|
|
28265
|
+
const shouldReclaim = options.shouldReclaim ?? defaultShouldReclaim;
|
|
28266
|
+
if (await shouldReclaim({
|
|
28267
|
+
lockPath,
|
|
28268
|
+
normalizedTargetPath,
|
|
28269
|
+
payload: snapshot?.payload ?? null,
|
|
28270
|
+
staleMs: options.staleMs,
|
|
28271
|
+
nowMs,
|
|
28272
|
+
heldByThisProcess: state.held.has(normalizedTargetPath),
|
|
28273
|
+
})) {
|
|
28274
|
+
if (!(await lockSnapshotStillPresent(lockPath, snapshot))) {
|
|
28275
|
+
continue;
|
|
28276
|
+
}
|
|
28277
|
+
const staleRecovery = options.staleRecovery ?? "fail-closed";
|
|
28278
|
+
if (staleRecovery === "remove-if-unchanged") {
|
|
28279
|
+
const removal = await removeStaleLockIfAllowed({
|
|
28280
|
+
lockPath,
|
|
28281
|
+
normalizedTargetPath,
|
|
28282
|
+
snapshot,
|
|
28283
|
+
shouldRemoveStaleLock: options.shouldRemoveStaleLock,
|
|
28284
|
+
});
|
|
28285
|
+
if (removal === "removed" || removal === "changed") {
|
|
28286
|
+
continue;
|
|
28287
|
+
}
|
|
28288
|
+
}
|
|
28289
|
+
throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
|
|
28290
|
+
code: "file_lock_stale",
|
|
28291
|
+
lockPath,
|
|
28292
|
+
normalizedTargetPath,
|
|
28293
|
+
});
|
|
28294
|
+
}
|
|
28295
|
+
const elapsed = Date.now() - startedAt;
|
|
28296
|
+
if ((options.timeoutMs !== undefined &&
|
|
28297
|
+
options.timeoutMs !== Number.POSITIVE_INFINITY &&
|
|
28298
|
+
elapsed >= options.timeoutMs) ||
|
|
28299
|
+
(maxRetries !== undefined && attempt >= maxRetries)) {
|
|
28300
|
+
throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
|
|
28301
|
+
code: "file_lock_timeout",
|
|
28302
|
+
lockPath,
|
|
28303
|
+
normalizedTargetPath,
|
|
28304
|
+
});
|
|
28305
|
+
}
|
|
28306
|
+
const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
|
|
28307
|
+
? Number.POSITIVE_INFINITY
|
|
28308
|
+
: Math.max(0, options.timeoutMs - elapsed);
|
|
28309
|
+
const delay = Math.min(computeDelayMs(retry, attempt), remaining);
|
|
28310
|
+
attempt += 1;
|
|
28311
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
28312
|
+
}
|
|
28313
|
+
}
|
|
28314
|
+
}
|
|
28315
|
+
async function withLock(options, fn) {
|
|
28316
|
+
const lock = await acquire(options);
|
|
28317
|
+
try {
|
|
28318
|
+
return await fn();
|
|
28319
|
+
}
|
|
28320
|
+
finally {
|
|
28321
|
+
await lock.release();
|
|
28322
|
+
}
|
|
28323
|
+
}
|
|
28324
|
+
async function drain() {
|
|
28325
|
+
for (const [normalizedTargetPath, held] of Array.from(state.held.entries())) {
|
|
28326
|
+
await releaseHeldLock(state, normalizedTargetPath, held, { force: true }).catch(() => undefined);
|
|
28327
|
+
}
|
|
28328
|
+
}
|
|
28329
|
+
function reset() {
|
|
28330
|
+
releaseAllLocksSync(state);
|
|
28331
|
+
}
|
|
28332
|
+
function heldEntries() {
|
|
28333
|
+
return Array.from(state.held.entries()).map(([normalizedTargetPath, held]) => ({
|
|
28334
|
+
normalizedTargetPath,
|
|
28335
|
+
lockPath: held.lockPath,
|
|
28336
|
+
acquiredAt: held.acquiredAt,
|
|
28337
|
+
metadata: held.metadata,
|
|
28338
|
+
forceRelease: () => releaseHeldLock(state, normalizedTargetPath, held, { force: true }),
|
|
28339
|
+
}));
|
|
28340
|
+
}
|
|
28341
|
+
return { acquire, withLock, drain, reset, heldEntries };
|
|
28342
|
+
}
|
|
28343
|
+
async function withSidecarLock(targetPath, options, fn) {
|
|
28344
|
+
const manager = createSidecarLockManager(options.managerKey ?? `fs-safe.sidecar-lock:${targetPath}`);
|
|
28345
|
+
const { managerKey: _managerKey, ...acquireOptions } = options;
|
|
28346
|
+
return await manager.withLock({ ...acquireOptions, targetPath }, fn);
|
|
28347
|
+
}
|
|
28348
|
+
|
|
28349
|
+
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
|
|
28350
|
+
let test_hooks_fsSafeTestHooks;
|
|
28351
|
+
function allowFsSafeTestHooks() {
|
|
28352
|
+
return false || process.env.VITEST === "true";
|
|
28353
|
+
}
|
|
28354
|
+
function getFsSafeTestHooks() {
|
|
28355
|
+
return test_hooks_fsSafeTestHooks;
|
|
28356
|
+
}
|
|
28357
|
+
function __setFsSafeTestHooksForTest(hooks) {
|
|
28358
|
+
if (hooks && !allowFsSafeTestHooks()) {
|
|
28359
|
+
throw new Error("__setFsSafeTestHooksForTest is only available in tests");
|
|
28360
|
+
}
|
|
28361
|
+
test_hooks_fsSafeTestHooks = hooks;
|
|
28362
|
+
}
|
|
28363
|
+
|
|
27985
28364
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-write.js
|
|
27986
28365
|
|
|
27987
28366
|
|
|
@@ -27995,6 +28374,8 @@ async function runPinnedPathHelper(params) {
|
|
|
27995
28374
|
|
|
27996
28375
|
|
|
27997
28376
|
|
|
28377
|
+
|
|
28378
|
+
|
|
27998
28379
|
function pinned_write_byteLength(input, encoding) {
|
|
27999
28380
|
return typeof input === "string"
|
|
28000
28381
|
? Buffer.byteLength(input, encoding ?? "utf8")
|
|
@@ -28052,6 +28433,12 @@ async function runPinnedWriteHelper(params) {
|
|
|
28052
28433
|
validatePinnedOperationPayload({
|
|
28053
28434
|
relativeParentPath: params.relativeParentPath,
|
|
28054
28435
|
});
|
|
28436
|
+
// The Python helper deliberately enforces the strict post-rename inode
|
|
28437
|
+
// contract. The explicit compatibility policy therefore uses the guarded
|
|
28438
|
+
// Node fallback, where content verification can replace that one check.
|
|
28439
|
+
if (params.onRenameIdentityMismatch === "verify-content") {
|
|
28440
|
+
return await runPinnedWriteFallback(params);
|
|
28441
|
+
}
|
|
28055
28442
|
if (getFsSafePythonConfig().mode === "off") {
|
|
28056
28443
|
return await runPinnedWriteFallback(params);
|
|
28057
28444
|
}
|
|
@@ -28066,8 +28453,11 @@ async function runPinnedWriteHelper(params) {
|
|
|
28066
28453
|
throw error;
|
|
28067
28454
|
}
|
|
28068
28455
|
}
|
|
28456
|
+
const input = params.input.kind === "stream"
|
|
28457
|
+
? { kind: "buffer", data: Buffer.from(await inputToBase64(params.input, params.maxBytes), "base64") }
|
|
28458
|
+
: params.input;
|
|
28069
28459
|
const payload = {
|
|
28070
|
-
base64: await inputToBase64(
|
|
28460
|
+
base64: await inputToBase64(input, params.maxBytes),
|
|
28071
28461
|
basename: params.basename,
|
|
28072
28462
|
maxBytes: params.maxBytes ?? -1,
|
|
28073
28463
|
mkdir: params.mkdir,
|
|
@@ -28085,11 +28475,32 @@ async function runPinnedWriteHelper(params) {
|
|
|
28085
28475
|
}
|
|
28086
28476
|
catch (error) {
|
|
28087
28477
|
if (canFallbackFromPythonError(error)) {
|
|
28088
|
-
return await runPinnedWriteFallback(params);
|
|
28478
|
+
return await runPinnedWriteFallback({ ...params, input });
|
|
28089
28479
|
}
|
|
28090
28480
|
throw error;
|
|
28091
28481
|
}
|
|
28092
28482
|
}
|
|
28483
|
+
async function runPinnedWriteWithRenamePolicy(params) {
|
|
28484
|
+
const { targetPath, renameIdentity, ...writeParams } = params;
|
|
28485
|
+
if (renameIdentity !== "verify-content-with-lock") {
|
|
28486
|
+
return await runPinnedWriteHelper(writeParams);
|
|
28487
|
+
}
|
|
28488
|
+
const relativeTargetPath = writeParams.relativeParentPath
|
|
28489
|
+
? `${writeParams.relativeParentPath}/${writeParams.basename}`
|
|
28490
|
+
: writeParams.basename;
|
|
28491
|
+
const lockPath = external_node_path_.join(writeParams.rootPath, `.fs-safe-write-${sha256Hex(relativeTargetPath)}.lock`);
|
|
28492
|
+
return await withSidecarLock(writeParams.rootPath, {
|
|
28493
|
+
managerKey: `fs-safe.write:${targetPath}`,
|
|
28494
|
+
lockPath,
|
|
28495
|
+
staleMs: 30_000,
|
|
28496
|
+
timeoutMs: 5_000,
|
|
28497
|
+
payload: () => ({ pid: process.pid, createdAt: new Date().toISOString() }),
|
|
28498
|
+
retry: { retries: 5, minTimeout: 100, maxTimeout: 2_000, factor: 2 },
|
|
28499
|
+
}, async () => await runPinnedWriteHelper({
|
|
28500
|
+
...writeParams,
|
|
28501
|
+
onRenameIdentityMismatch: "verify-content",
|
|
28502
|
+
}));
|
|
28503
|
+
}
|
|
28093
28504
|
async function runPinnedCopyHelper(params) {
|
|
28094
28505
|
assertSafeBasename(params.basename);
|
|
28095
28506
|
validatePinnedOperationPayload({
|
|
@@ -28145,7 +28556,10 @@ async function runPinnedWriteFallback(params) {
|
|
|
28145
28556
|
else {
|
|
28146
28557
|
await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
|
|
28147
28558
|
}
|
|
28559
|
+
await handle.sync();
|
|
28148
28560
|
const stat = await handle.stat();
|
|
28561
|
+
await handle.close().catch(() => undefined);
|
|
28562
|
+
await syncDirectoryBestEffort(parentPath);
|
|
28149
28563
|
created = false;
|
|
28150
28564
|
return { dev: stat.dev, ino: stat.ino };
|
|
28151
28565
|
}
|
|
@@ -28193,11 +28607,49 @@ async function runPinnedWriteFallback(params) {
|
|
|
28193
28607
|
await withAsyncDirectoryGuards([parentGuard], async () => {
|
|
28194
28608
|
await promises_.rename(tempPath, targetPath);
|
|
28195
28609
|
renamed = true;
|
|
28610
|
+
await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
|
|
28196
28611
|
await syncDirectoryBestEffort(parentPath);
|
|
28197
28612
|
targetStat = await promises_.lstat(targetPath);
|
|
28198
|
-
if (targetStat.isSymbolicLink()
|
|
28613
|
+
if (targetStat.isSymbolicLink()) {
|
|
28199
28614
|
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
28200
28615
|
}
|
|
28616
|
+
if (!file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
|
|
28617
|
+
// On filesystems like rclone FUSE, rename(2) can give the destination a
|
|
28618
|
+
// different inode from the source temp fd even with zero concurrency. The
|
|
28619
|
+
// caller must ensure mutual exclusion before passing "verify-content";
|
|
28620
|
+
// fall back to a content hash for this rename-boundary check only.
|
|
28621
|
+
if (params.onRenameIdentityMismatch !== "verify-content") {
|
|
28622
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
28623
|
+
}
|
|
28624
|
+
if (params.input.kind !== "buffer") {
|
|
28625
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
28626
|
+
}
|
|
28627
|
+
const expectedHash = sha256Hex(params.input.data, params.input.encoding);
|
|
28628
|
+
const readFlags = external_node_fs_.constants.O_RDONLY |
|
|
28629
|
+
(process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants
|
|
28630
|
+
? external_node_fs_.constants.O_NOFOLLOW
|
|
28631
|
+
: 0);
|
|
28632
|
+
const readHandle = await promises_.open(targetPath, readFlags);
|
|
28633
|
+
let actualHash;
|
|
28634
|
+
let readHandleStat;
|
|
28635
|
+
try {
|
|
28636
|
+
// Capture fd-based identity before reading — this is stable across all
|
|
28637
|
+
// subsequent lookups (on FUSE and locally), unlike the lstat-based
|
|
28638
|
+
// targetStat that triggered this fallback.
|
|
28639
|
+
readHandleStat = await readHandle.stat();
|
|
28640
|
+
actualHash = sha256Hex(await readHandle.readFile());
|
|
28641
|
+
}
|
|
28642
|
+
finally {
|
|
28643
|
+
await readHandle.close().catch(() => undefined);
|
|
28644
|
+
}
|
|
28645
|
+
if (actualHash !== expectedHash) {
|
|
28646
|
+
throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
|
|
28647
|
+
}
|
|
28648
|
+
// Replace the unreliable lstat-based targetStat with the fd-based stat so
|
|
28649
|
+
// the returned identity is consistent with what subsequent verifications
|
|
28650
|
+
// (e.g. verifyAtomicWriteResult) will obtain by opening the same file.
|
|
28651
|
+
targetStat = readHandleStat;
|
|
28652
|
+
}
|
|
28201
28653
|
});
|
|
28202
28654
|
}
|
|
28203
28655
|
catch (error) {
|
|
@@ -29293,21 +29745,6 @@ function normalizePinnedPathError(error) {
|
|
|
29293
29745
|
});
|
|
29294
29746
|
}
|
|
29295
29747
|
|
|
29296
|
-
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
|
|
29297
|
-
let test_hooks_fsSafeTestHooks;
|
|
29298
|
-
function allowFsSafeTestHooks() {
|
|
29299
|
-
return false || process.env.VITEST === "true";
|
|
29300
|
-
}
|
|
29301
|
-
function getFsSafeTestHooks() {
|
|
29302
|
-
return test_hooks_fsSafeTestHooks;
|
|
29303
|
-
}
|
|
29304
|
-
function __setFsSafeTestHooksForTest(hooks) {
|
|
29305
|
-
if (hooks && !allowFsSafeTestHooks()) {
|
|
29306
|
-
throw new Error("__setFsSafeTestHooksForTest is only available in tests");
|
|
29307
|
-
}
|
|
29308
|
-
test_hooks_fsSafeTestHooks = hooks;
|
|
29309
|
-
}
|
|
29310
|
-
|
|
29311
29748
|
;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
|
|
29312
29749
|
function stringifyJsonDocument(value, replacer, space) {
|
|
29313
29750
|
const text = JSON.stringify(value, replacer, space);
|
|
@@ -29643,6 +30080,7 @@ class RootHandle {
|
|
|
29643
30080
|
data,
|
|
29644
30081
|
mkdir: this.defaults.mkdir,
|
|
29645
30082
|
mode: this.defaults.mode,
|
|
30083
|
+
renameIdentity: this.defaults.renameIdentity,
|
|
29646
30084
|
...options,
|
|
29647
30085
|
denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
|
|
29648
30086
|
});
|
|
@@ -30077,21 +30515,23 @@ async function writeFileInRoot(root, params) {
|
|
|
30077
30515
|
async function commitPinnedWriteInRoot(root, pinned, params) {
|
|
30078
30516
|
let identity;
|
|
30079
30517
|
try {
|
|
30080
|
-
identity = await
|
|
30518
|
+
identity = await runPinnedWriteWithRenamePolicy({
|
|
30081
30519
|
rootPath: pinned.rootReal,
|
|
30082
30520
|
relativeParentPath: pinned.relativeParentPath,
|
|
30083
30521
|
basename: pinned.basename,
|
|
30522
|
+
targetPath: pinned.targetPath,
|
|
30523
|
+
renameIdentity: params.renameIdentity,
|
|
30084
30524
|
mkdir: params.mkdir !== false,
|
|
30085
30525
|
mode: params.mode ?? pinned.mode,
|
|
30086
30526
|
overwrite: params.overwrite,
|
|
30087
|
-
input: {
|
|
30088
|
-
kind: "buffer",
|
|
30089
|
-
data: params.data,
|
|
30090
|
-
encoding: params.encoding,
|
|
30091
|
-
},
|
|
30527
|
+
input: { kind: "buffer", data: params.data, encoding: params.encoding },
|
|
30092
30528
|
});
|
|
30093
30529
|
}
|
|
30094
30530
|
catch (error) {
|
|
30531
|
+
const errorCode = error?.code;
|
|
30532
|
+
if (errorCode === "file_lock_stale" || errorCode === "file_lock_timeout") {
|
|
30533
|
+
throw error;
|
|
30534
|
+
}
|
|
30095
30535
|
if (params.overwrite === false && isAlreadyExistsError(error)) {
|
|
30096
30536
|
throw new errors_FsSafeError("already-exists", "file already exists", {
|
|
30097
30537
|
cause: error instanceof Error ? error : undefined,
|
|
@@ -37113,6 +37553,7 @@ function createWorkflowGateway(cwd) {
|
|
|
37113
37553
|
|
|
37114
37554
|
|
|
37115
37555
|
|
|
37556
|
+
|
|
37116
37557
|
/**
|
|
37117
37558
|
* Parses a workflow file and extracts action references.
|
|
37118
37559
|
*/ async function parseWorkflowFile(rootDir, relativePath) {
|
|
@@ -37142,45 +37583,51 @@ function createWorkflowGateway(cwd) {
|
|
|
37142
37583
|
/**
|
|
37143
37584
|
* Analyzes GitHub Action versions used across all workflow files.
|
|
37144
37585
|
*/ const analyzeActionVersionsHandler = async (args)=>{
|
|
37145
|
-
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37151
|
-
|
|
37152
|
-
|
|
37153
|
-
|
|
37154
|
-
|
|
37155
|
-
|
|
37156
|
-
|
|
37157
|
-
// Read all workflow files
|
|
37158
|
-
const entries = await file_system_utils_listDirectoryWithinRoot(dir, workflowsRelativeDir);
|
|
37159
|
-
const files = entries.filter((entry)=>entry.isFile()).map((entry)=>entry.name);
|
|
37160
|
-
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
37161
|
-
const workflows = [];
|
|
37162
|
-
for (const file of yamlFiles){
|
|
37163
|
-
const filePath = (0,external_node_path_.join)(workflowsRelativeDir, file);
|
|
37164
|
-
const actions = await parseWorkflowFile(dir, filePath);
|
|
37165
|
-
if (actions && actions.length > 0) {
|
|
37166
|
-
workflows.push({
|
|
37167
|
-
file,
|
|
37168
|
-
actions
|
|
37586
|
+
try {
|
|
37587
|
+
const dir = args.targetDir || process.cwd();
|
|
37588
|
+
const dirStat = await (0,promises_.stat)(dir).catch(()=>null);
|
|
37589
|
+
if (dirStat === null || !dirStat.isDirectory()) {
|
|
37590
|
+
return types_errorResponse(`Target directory does not exist: ${dir}`);
|
|
37591
|
+
}
|
|
37592
|
+
const workflowsRelativeDir = (0,external_node_path_.join)(".github", "workflows");
|
|
37593
|
+
if (!await file_system_utils_pathExistsWithinRoot(dir, workflowsRelativeDir)) {
|
|
37594
|
+
return successResponse({
|
|
37595
|
+
workflows: [],
|
|
37596
|
+
uniqueActions: [],
|
|
37597
|
+
message: "No .github/workflows directory found - no workflows to analyze"
|
|
37169
37598
|
});
|
|
37170
37599
|
}
|
|
37600
|
+
// Read all workflow files
|
|
37601
|
+
const entries = await file_system_utils_listDirectoryWithinRoot(dir, workflowsRelativeDir);
|
|
37602
|
+
const files = entries.filter((entry)=>entry.isFile()).map((entry)=>entry.name);
|
|
37603
|
+
const yamlFiles = files.filter((f)=>f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
37604
|
+
const workflows = [];
|
|
37605
|
+
for (const file of yamlFiles){
|
|
37606
|
+
const filePath = (0,external_node_path_.join)(workflowsRelativeDir, file);
|
|
37607
|
+
const actions = await parseWorkflowFile(dir, filePath);
|
|
37608
|
+
if (actions && actions.length > 0) {
|
|
37609
|
+
workflows.push({
|
|
37610
|
+
file,
|
|
37611
|
+
actions
|
|
37612
|
+
});
|
|
37613
|
+
}
|
|
37614
|
+
}
|
|
37615
|
+
// Build unique actions map
|
|
37616
|
+
const actionVersionsMap = buildUniqueActionsMap(workflows);
|
|
37617
|
+
const uniqueActions = Array.from(actionVersionsMap.entries()).map(([name, versions])=>({
|
|
37618
|
+
name,
|
|
37619
|
+
versions: Array.from(versions)
|
|
37620
|
+
}));
|
|
37621
|
+
const totalActions = workflows.reduce((sum, w)=>sum + w.actions.length, 0);
|
|
37622
|
+
return successResponse({
|
|
37623
|
+
workflows,
|
|
37624
|
+
uniqueActions,
|
|
37625
|
+
message: workflows.length > 0 ? `Found ${totalActions} action reference(s) across ${workflows.length} workflow(s), ${uniqueActions.length} unique action(s)` : "No action references found in workflows"
|
|
37626
|
+
});
|
|
37627
|
+
} catch (error) {
|
|
37628
|
+
const message = error instanceof Error ? error.message : "Unknown error occurred";
|
|
37629
|
+
return types_errorResponse(`Failed to analyze action versions: ${message}`);
|
|
37171
37630
|
}
|
|
37172
|
-
// Build unique actions map
|
|
37173
|
-
const actionVersionsMap = buildUniqueActionsMap(workflows);
|
|
37174
|
-
const uniqueActions = Array.from(actionVersionsMap.entries()).map(([name, versions])=>({
|
|
37175
|
-
name,
|
|
37176
|
-
versions: Array.from(versions)
|
|
37177
|
-
}));
|
|
37178
|
-
const totalActions = workflows.reduce((sum, w)=>sum + w.actions.length, 0);
|
|
37179
|
-
return successResponse({
|
|
37180
|
-
workflows,
|
|
37181
|
-
uniqueActions,
|
|
37182
|
-
message: workflows.length > 0 ? `Found ${totalActions} action reference(s) across ${workflows.length} workflow(s), ${uniqueActions.length} unique action(s)` : "No action references found in workflows"
|
|
37183
|
-
});
|
|
37184
37631
|
};
|
|
37185
37632
|
|
|
37186
37633
|
;// CONCATENATED MODULE: ../core/src/gateways/instruction-file-discovery-gateway.ts
|
|
@@ -68590,4 +69037,4 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
|
|
|
68590
69037
|
// module factories are used so entry inlining is disabled
|
|
68591
69038
|
// startup
|
|
68592
69039
|
// Load entry module and return exports
|
|
68593
|
-
var __webpack_exports__ = __webpack_require__(
|
|
69040
|
+
var __webpack_exports__ = __webpack_require__(6887);
|