@lousy-agents/lint 5.15.6 → 5.16.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.
Files changed (2) hide show
  1. package/dist/index.js +573 -132
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -566,7 +566,7 @@ module.exports = webpackEmptyAsyncContext;
566
566
 
567
567
 
568
568
  },
569
- 2749(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
569
+ 6179(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
570
570
 
571
571
  // EXPORTS
572
572
  __webpack_require__.d(__webpack_exports__, {
@@ -884,16 +884,24 @@ function createBoundedReadStream(opened, maxBytes) {
884
884
  }
885
885
 
886
886
  ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/file-identity.js
887
+
887
888
  function isZero(value) {
888
889
  return value === 0 || value === 0n;
889
890
  }
891
+ function sameStatValue(left, right) {
892
+ return typeof left === typeof right ? left === right : BigInt(left) === BigInt(right);
893
+ }
894
+ function sha256Hex(data, encoding) {
895
+ const buffer = typeof data === "string" ? Buffer.from(data, encoding ?? "utf8") : data;
896
+ return (0,external_node_crypto_.createHash)("sha256").update(buffer).digest("hex");
897
+ }
890
898
  function file_identity_sameFileIdentity(left, right, platform = process.platform) {
891
- if (left.ino !== right.ino) {
899
+ if (!sameStatValue(left.ino, right.ino)) {
892
900
  return false;
893
901
  }
894
902
  // On Windows, path-based stat calls can report dev=0 while fd-based stat
895
903
  // reports a real volume serial; treat either-side dev=0 as "unknown device".
896
- if (left.dev === right.dev) {
904
+ if (sameStatValue(left.dev, right.dev)) {
897
905
  return true;
898
906
  }
899
907
  return platform === "win32" && (isZero(left.dev) || isZero(right.dev));
@@ -2287,6 +2295,377 @@ async function runPinnedPathHelper(params) {
2287
2295
  }
2288
2296
  }
2289
2297
 
2298
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/sidecar-lock.js
2299
+
2300
+
2301
+
2302
+
2303
+ const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
2304
+ function getGlobalManagers() {
2305
+ const globalWithState = globalThis;
2306
+ if (!globalWithState[GLOBAL_STATE_KEY]) {
2307
+ globalWithState[GLOBAL_STATE_KEY] = new Map();
2308
+ }
2309
+ return globalWithState[GLOBAL_STATE_KEY];
2310
+ }
2311
+ function resolveManagerState(key) {
2312
+ const managers = getGlobalManagers();
2313
+ let state = managers.get(key);
2314
+ if (!state) {
2315
+ state = { cleanupRegistered: false, held: new Map() };
2316
+ managers.set(key, state);
2317
+ }
2318
+ return state;
2319
+ }
2320
+ async function readLockSnapshot(lockPath) {
2321
+ try {
2322
+ const stat = await promises_.lstat(lockPath);
2323
+ const raw = await promises_.readFile(lockPath, "utf8");
2324
+ try {
2325
+ const parsed = JSON.parse(raw);
2326
+ const payload = parsed && typeof parsed === "object" && !Array.isArray(parsed)
2327
+ ? parsed
2328
+ : null;
2329
+ return { raw, payload, stat };
2330
+ }
2331
+ catch {
2332
+ return { raw, payload: null, stat };
2333
+ }
2334
+ }
2335
+ catch (err) {
2336
+ if (err.code === "ENOENT") {
2337
+ return null;
2338
+ }
2339
+ throw err;
2340
+ }
2341
+ }
2342
+ function snapshotMatches(current, observed) {
2343
+ if (observed.stat && current.stat && !file_identity_sameFileIdentity(observed.stat, current.stat)) {
2344
+ return false;
2345
+ }
2346
+ if (observed.raw !== undefined) {
2347
+ return current.raw === observed.raw;
2348
+ }
2349
+ return observed.stat !== undefined && current.stat !== undefined;
2350
+ }
2351
+ async function removeLockIfUnchanged(lockPath, observed) {
2352
+ const current = await readLockSnapshot(lockPath);
2353
+ if (!current || !observed) {
2354
+ return false;
2355
+ }
2356
+ if (!snapshotMatches(current, observed)) {
2357
+ // The lock changed after we decided it was stale. Leave the fresh holder's
2358
+ // file alone; deleting by path here would break mutual exclusion.
2359
+ return false;
2360
+ }
2361
+ await promises_.rm(lockPath, { force: true }).catch(() => undefined);
2362
+ return true;
2363
+ }
2364
+ async function lockSnapshotStillPresent(lockPath, observed) {
2365
+ const current = await readLockSnapshot(lockPath);
2366
+ return !!current && !!observed && snapshotMatches(current, observed);
2367
+ }
2368
+ async function removeStaleLockIfAllowed(params) {
2369
+ if (!params.shouldRemoveStaleLock) {
2370
+ return "not-approved";
2371
+ }
2372
+ if (params.snapshot.raw === undefined) {
2373
+ return "not-approved";
2374
+ }
2375
+ if (!(await params.shouldRemoveStaleLock({
2376
+ lockPath: params.lockPath,
2377
+ normalizedTargetPath: params.normalizedTargetPath,
2378
+ raw: params.snapshot.raw,
2379
+ payload: params.snapshot.payload,
2380
+ }))) {
2381
+ return "not-approved";
2382
+ }
2383
+ const current = await readLockSnapshot(params.lockPath);
2384
+ if (!current || !snapshotMatches(current, params.snapshot)) {
2385
+ return "changed";
2386
+ }
2387
+ try {
2388
+ await promises_.rm(params.lockPath, { force: true });
2389
+ }
2390
+ catch (err) {
2391
+ if (err.code === "ENOENT") {
2392
+ return "changed";
2393
+ }
2394
+ return "not-approved";
2395
+ }
2396
+ return "removed";
2397
+ }
2398
+ function snapshotMatchesSync(lockPath, observed) {
2399
+ try {
2400
+ const stat = external_node_fs_.lstatSync(lockPath);
2401
+ if (observed.stat && !file_identity_sameFileIdentity(observed.stat, stat)) {
2402
+ return false;
2403
+ }
2404
+ return observed.raw === undefined || external_node_fs_.readFileSync(lockPath, "utf8") === observed.raw;
2405
+ }
2406
+ catch {
2407
+ return false;
2408
+ }
2409
+ }
2410
+ async function resolveNormalizedTargetPath(targetPath) {
2411
+ const resolved = external_node_path_.resolve(targetPath);
2412
+ const dir = external_node_path_.dirname(resolved);
2413
+ await promises_.mkdir(dir, { recursive: true });
2414
+ try {
2415
+ return external_node_path_.join(await promises_.realpath(dir), external_node_path_.basename(resolved));
2416
+ }
2417
+ catch {
2418
+ return resolved;
2419
+ }
2420
+ }
2421
+ function computeDelayMs(retry, attempt) {
2422
+ const minTimeout = retry.minTimeout ?? 50;
2423
+ const maxTimeout = retry.maxTimeout ?? 1000;
2424
+ const factor = retry.factor ?? 1;
2425
+ const base = Math.min(maxTimeout, Math.max(minTimeout, minTimeout * factor ** attempt));
2426
+ const jitter = retry.randomize ? 1 + Math.random() : 1;
2427
+ return Math.min(maxTimeout, Math.round(base * jitter));
2428
+ }
2429
+ async function defaultShouldReclaim(params) {
2430
+ const createdAt = typeof params.payload?.createdAt === "string" ? params.payload.createdAt : "";
2431
+ const createdAtMs = Date.parse(createdAt);
2432
+ if (Number.isFinite(createdAtMs) && params.nowMs - createdAtMs > params.staleMs) {
2433
+ return true;
2434
+ }
2435
+ try {
2436
+ const stat = await promises_.stat(params.lockPath);
2437
+ return params.nowMs - stat.mtimeMs > params.staleMs;
2438
+ }
2439
+ catch {
2440
+ return true;
2441
+ }
2442
+ }
2443
+ function releaseAllLocksSync(state) {
2444
+ for (const [normalizedTargetPath, held] of state.held) {
2445
+ void held.handle.close().catch(() => undefined);
2446
+ try {
2447
+ if (snapshotMatchesSync(held.lockPath, held.snapshot)) {
2448
+ external_node_fs_.rmSync(held.lockPath, { force: true });
2449
+ }
2450
+ }
2451
+ catch {
2452
+ // Best-effort process-exit cleanup.
2453
+ }
2454
+ state.held.delete(normalizedTargetPath);
2455
+ }
2456
+ }
2457
+ async function releaseHeldLock(state, normalizedTargetPath, held, opts = {}) {
2458
+ const current = state.held.get(normalizedTargetPath);
2459
+ if (current !== held) {
2460
+ return false;
2461
+ }
2462
+ if (opts.force) {
2463
+ held.count = 0;
2464
+ }
2465
+ else {
2466
+ held.count -= 1;
2467
+ if (held.count > 0) {
2468
+ return false;
2469
+ }
2470
+ }
2471
+ if (held.releasePromise) {
2472
+ await held.releasePromise.catch(() => undefined);
2473
+ return true;
2474
+ }
2475
+ state.held.delete(normalizedTargetPath);
2476
+ held.releasePromise = (async () => {
2477
+ await held.handle.close().catch(() => undefined);
2478
+ await removeLockIfUnchanged(held.lockPath, held.snapshot);
2479
+ })();
2480
+ try {
2481
+ await held.releasePromise;
2482
+ return true;
2483
+ }
2484
+ finally {
2485
+ held.releasePromise = undefined;
2486
+ }
2487
+ }
2488
+ function createSidecarLockManager(key) {
2489
+ const state = resolveManagerState(key);
2490
+ function ensureExitCleanupRegistered() {
2491
+ if (state.cleanupRegistered) {
2492
+ return;
2493
+ }
2494
+ state.cleanupRegistered = true;
2495
+ process.on("exit", () => releaseAllLocksSync(state));
2496
+ }
2497
+ async function acquire(options) {
2498
+ ensureExitCleanupRegistered();
2499
+ const normalizedTargetPath = await resolveNormalizedTargetPath(options.targetPath);
2500
+ const lockPath = options.lockPath ?? `${normalizedTargetPath}.lock`;
2501
+ const held = state.held.get(normalizedTargetPath);
2502
+ if (held && options.allowReentrant) {
2503
+ held.count += 1;
2504
+ const release = () => releaseHeldLock(state, normalizedTargetPath, held).then(() => undefined);
2505
+ return {
2506
+ lockPath,
2507
+ normalizedTargetPath,
2508
+ release,
2509
+ [Symbol.asyncDispose]: release,
2510
+ };
2511
+ }
2512
+ const startedAt = Date.now();
2513
+ const retry = options.retry ?? {};
2514
+ const maxRetries = options.timeoutMs === Number.POSITIVE_INFINITY ? undefined : retry.retries;
2515
+ let attempt = 0;
2516
+ while (true) {
2517
+ let handle = null;
2518
+ try {
2519
+ handle = await promises_.open(lockPath, "wx");
2520
+ const payload = await options.payload();
2521
+ const raw = `${JSON.stringify(payload, null, 2)}\n`;
2522
+ await handle.writeFile(raw, "utf8");
2523
+ const snapshot = { raw, payload, stat: await handle.stat() };
2524
+ const createdHeld = {
2525
+ count: 1,
2526
+ handle,
2527
+ lockPath,
2528
+ snapshot,
2529
+ acquiredAt: Date.now(),
2530
+ metadata: options.metadata ?? {},
2531
+ };
2532
+ state.held.set(normalizedTargetPath, createdHeld);
2533
+ const release = () => releaseHeldLock(state, normalizedTargetPath, createdHeld).then(() => undefined);
2534
+ return {
2535
+ lockPath,
2536
+ normalizedTargetPath,
2537
+ release,
2538
+ [Symbol.asyncDispose]: release,
2539
+ };
2540
+ }
2541
+ catch (err) {
2542
+ if (handle) {
2543
+ const failedSnapshot = { payload: null };
2544
+ try {
2545
+ failedSnapshot.stat = await handle.stat();
2546
+ }
2547
+ catch {
2548
+ // Best-effort cleanup of a failed exclusive create.
2549
+ }
2550
+ const current = state.held.get(normalizedTargetPath);
2551
+ if (current?.handle === handle) {
2552
+ state.held.delete(normalizedTargetPath);
2553
+ }
2554
+ // If payload serialization/write fails, the file may be empty or
2555
+ // partial JSON, so remove while our exclusive handle is still open.
2556
+ await promises_.rm(lockPath, { force: true }).catch(() => undefined);
2557
+ await handle.close().catch(() => undefined);
2558
+ // Windows can refuse removing an open file; retry after close but
2559
+ // only if the path still points at the file identity we created.
2560
+ await removeLockIfUnchanged(lockPath, failedSnapshot);
2561
+ }
2562
+ if (err.code !== "EEXIST") {
2563
+ throw err;
2564
+ }
2565
+ const nowMs = Date.now();
2566
+ const snapshot = await readLockSnapshot(lockPath);
2567
+ if (!snapshot) {
2568
+ continue;
2569
+ }
2570
+ const shouldReclaim = options.shouldReclaim ?? defaultShouldReclaim;
2571
+ if (await shouldReclaim({
2572
+ lockPath,
2573
+ normalizedTargetPath,
2574
+ payload: snapshot?.payload ?? null,
2575
+ staleMs: options.staleMs,
2576
+ nowMs,
2577
+ heldByThisProcess: state.held.has(normalizedTargetPath),
2578
+ })) {
2579
+ if (!(await lockSnapshotStillPresent(lockPath, snapshot))) {
2580
+ continue;
2581
+ }
2582
+ const staleRecovery = options.staleRecovery ?? "fail-closed";
2583
+ if (staleRecovery === "remove-if-unchanged") {
2584
+ const removal = await removeStaleLockIfAllowed({
2585
+ lockPath,
2586
+ normalizedTargetPath,
2587
+ snapshot,
2588
+ shouldRemoveStaleLock: options.shouldRemoveStaleLock,
2589
+ });
2590
+ if (removal === "removed" || removal === "changed") {
2591
+ continue;
2592
+ }
2593
+ }
2594
+ throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
2595
+ code: "file_lock_stale",
2596
+ lockPath,
2597
+ normalizedTargetPath,
2598
+ });
2599
+ }
2600
+ const elapsed = Date.now() - startedAt;
2601
+ if ((options.timeoutMs !== undefined &&
2602
+ options.timeoutMs !== Number.POSITIVE_INFINITY &&
2603
+ elapsed >= options.timeoutMs) ||
2604
+ (maxRetries !== undefined && attempt >= maxRetries)) {
2605
+ throw Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
2606
+ code: "file_lock_timeout",
2607
+ lockPath,
2608
+ normalizedTargetPath,
2609
+ });
2610
+ }
2611
+ const remaining = options.timeoutMs === undefined || options.timeoutMs === Number.POSITIVE_INFINITY
2612
+ ? Number.POSITIVE_INFINITY
2613
+ : Math.max(0, options.timeoutMs - elapsed);
2614
+ const delay = Math.min(computeDelayMs(retry, attempt), remaining);
2615
+ attempt += 1;
2616
+ await new Promise((resolve) => setTimeout(resolve, delay));
2617
+ }
2618
+ }
2619
+ }
2620
+ async function withLock(options, fn) {
2621
+ const lock = await acquire(options);
2622
+ try {
2623
+ return await fn();
2624
+ }
2625
+ finally {
2626
+ await lock.release();
2627
+ }
2628
+ }
2629
+ async function drain() {
2630
+ for (const [normalizedTargetPath, held] of Array.from(state.held.entries())) {
2631
+ await releaseHeldLock(state, normalizedTargetPath, held, { force: true }).catch(() => undefined);
2632
+ }
2633
+ }
2634
+ function reset() {
2635
+ releaseAllLocksSync(state);
2636
+ }
2637
+ function heldEntries() {
2638
+ return Array.from(state.held.entries()).map(([normalizedTargetPath, held]) => ({
2639
+ normalizedTargetPath,
2640
+ lockPath: held.lockPath,
2641
+ acquiredAt: held.acquiredAt,
2642
+ metadata: held.metadata,
2643
+ forceRelease: () => releaseHeldLock(state, normalizedTargetPath, held, { force: true }),
2644
+ }));
2645
+ }
2646
+ return { acquire, withLock, drain, reset, heldEntries };
2647
+ }
2648
+ async function withSidecarLock(targetPath, options, fn) {
2649
+ const manager = createSidecarLockManager(options.managerKey ?? `fs-safe.sidecar-lock:${targetPath}`);
2650
+ const { managerKey: _managerKey, ...acquireOptions } = options;
2651
+ return await manager.withLock({ ...acquireOptions, targetPath }, fn);
2652
+ }
2653
+
2654
+ ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
2655
+ let test_hooks_fsSafeTestHooks;
2656
+ function allowFsSafeTestHooks() {
2657
+ return false || process.env.VITEST === "true";
2658
+ }
2659
+ function getFsSafeTestHooks() {
2660
+ return test_hooks_fsSafeTestHooks;
2661
+ }
2662
+ function __setFsSafeTestHooksForTest(hooks) {
2663
+ if (hooks && !allowFsSafeTestHooks()) {
2664
+ throw new Error("__setFsSafeTestHooksForTest is only available in tests");
2665
+ }
2666
+ test_hooks_fsSafeTestHooks = hooks;
2667
+ }
2668
+
2290
2669
  ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/pinned-write.js
2291
2670
 
2292
2671
 
@@ -2300,6 +2679,8 @@ async function runPinnedPathHelper(params) {
2300
2679
 
2301
2680
 
2302
2681
 
2682
+
2683
+
2303
2684
  function byteLength(input, encoding) {
2304
2685
  return typeof input === "string"
2305
2686
  ? Buffer.byteLength(input, encoding ?? "utf8")
@@ -2357,6 +2738,12 @@ async function runPinnedWriteHelper(params) {
2357
2738
  validatePinnedOperationPayload({
2358
2739
  relativeParentPath: params.relativeParentPath,
2359
2740
  });
2741
+ // The Python helper deliberately enforces the strict post-rename inode
2742
+ // contract. The explicit compatibility policy therefore uses the guarded
2743
+ // Node fallback, where content verification can replace that one check.
2744
+ if (params.onRenameIdentityMismatch === "verify-content") {
2745
+ return await runPinnedWriteFallback(params);
2746
+ }
2360
2747
  if (getFsSafePythonConfig().mode === "off") {
2361
2748
  return await runPinnedWriteFallback(params);
2362
2749
  }
@@ -2371,8 +2758,11 @@ async function runPinnedWriteHelper(params) {
2371
2758
  throw error;
2372
2759
  }
2373
2760
  }
2761
+ const input = params.input.kind === "stream"
2762
+ ? { kind: "buffer", data: Buffer.from(await inputToBase64(params.input, params.maxBytes), "base64") }
2763
+ : params.input;
2374
2764
  const payload = {
2375
- base64: await inputToBase64(params.input, params.maxBytes),
2765
+ base64: await inputToBase64(input, params.maxBytes),
2376
2766
  basename: params.basename,
2377
2767
  maxBytes: params.maxBytes ?? -1,
2378
2768
  mkdir: params.mkdir,
@@ -2390,11 +2780,32 @@ async function runPinnedWriteHelper(params) {
2390
2780
  }
2391
2781
  catch (error) {
2392
2782
  if (canFallbackFromPythonError(error)) {
2393
- return await runPinnedWriteFallback(params);
2783
+ return await runPinnedWriteFallback({ ...params, input });
2394
2784
  }
2395
2785
  throw error;
2396
2786
  }
2397
2787
  }
2788
+ async function runPinnedWriteWithRenamePolicy(params) {
2789
+ const { targetPath, renameIdentity, ...writeParams } = params;
2790
+ if (renameIdentity !== "verify-content-with-lock") {
2791
+ return await runPinnedWriteHelper(writeParams);
2792
+ }
2793
+ const relativeTargetPath = writeParams.relativeParentPath
2794
+ ? `${writeParams.relativeParentPath}/${writeParams.basename}`
2795
+ : writeParams.basename;
2796
+ const lockPath = external_node_path_.join(writeParams.rootPath, `.fs-safe-write-${sha256Hex(relativeTargetPath)}.lock`);
2797
+ return await withSidecarLock(writeParams.rootPath, {
2798
+ managerKey: `fs-safe.write:${targetPath}`,
2799
+ lockPath,
2800
+ staleMs: 30_000,
2801
+ timeoutMs: 5_000,
2802
+ payload: () => ({ pid: process.pid, createdAt: new Date().toISOString() }),
2803
+ retry: { retries: 5, minTimeout: 100, maxTimeout: 2_000, factor: 2 },
2804
+ }, async () => await runPinnedWriteHelper({
2805
+ ...writeParams,
2806
+ onRenameIdentityMismatch: "verify-content",
2807
+ }));
2808
+ }
2398
2809
  async function runPinnedCopyHelper(params) {
2399
2810
  assertSafeBasename(params.basename);
2400
2811
  validatePinnedOperationPayload({
@@ -2450,7 +2861,10 @@ async function runPinnedWriteFallback(params) {
2450
2861
  else {
2451
2862
  await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
2452
2863
  }
2864
+ await handle.sync();
2453
2865
  const stat = await handle.stat();
2866
+ await handle.close().catch(() => undefined);
2867
+ await syncDirectoryBestEffort(parentPath);
2454
2868
  created = false;
2455
2869
  return { dev: stat.dev, ino: stat.ino };
2456
2870
  }
@@ -2498,11 +2912,49 @@ async function runPinnedWriteFallback(params) {
2498
2912
  await withAsyncDirectoryGuards([parentGuard], async () => {
2499
2913
  await promises_.rename(tempPath, targetPath);
2500
2914
  renamed = true;
2915
+ await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
2501
2916
  await syncDirectoryBestEffort(parentPath);
2502
2917
  targetStat = await promises_.lstat(targetPath);
2503
- if (targetStat.isSymbolicLink() || !file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
2918
+ if (targetStat.isSymbolicLink()) {
2504
2919
  throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
2505
2920
  }
2921
+ if (!file_identity_sameFileIdentity(targetStat, expectedTempStat)) {
2922
+ // On filesystems like rclone FUSE, rename(2) can give the destination a
2923
+ // different inode from the source temp fd even with zero concurrency. The
2924
+ // caller must ensure mutual exclusion before passing "verify-content";
2925
+ // fall back to a content hash for this rename-boundary check only.
2926
+ if (params.onRenameIdentityMismatch !== "verify-content") {
2927
+ throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
2928
+ }
2929
+ if (params.input.kind !== "buffer") {
2930
+ throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
2931
+ }
2932
+ const expectedHash = sha256Hex(params.input.data, params.input.encoding);
2933
+ const readFlags = external_node_fs_.constants.O_RDONLY |
2934
+ (process.platform !== "win32" && "O_NOFOLLOW" in external_node_fs_.constants
2935
+ ? external_node_fs_.constants.O_NOFOLLOW
2936
+ : 0);
2937
+ const readHandle = await promises_.open(targetPath, readFlags);
2938
+ let actualHash;
2939
+ let readHandleStat;
2940
+ try {
2941
+ // Capture fd-based identity before reading — this is stable across all
2942
+ // subsequent lookups (on FUSE and locally), unlike the lstat-based
2943
+ // targetStat that triggered this fallback.
2944
+ readHandleStat = await readHandle.stat();
2945
+ actualHash = sha256Hex(await readHandle.readFile());
2946
+ }
2947
+ finally {
2948
+ await readHandle.close().catch(() => undefined);
2949
+ }
2950
+ if (actualHash !== expectedHash) {
2951
+ throw new errors_FsSafeError("path-mismatch", "fallback target changed during write");
2952
+ }
2953
+ // Replace the unreliable lstat-based targetStat with the fd-based stat so
2954
+ // the returned identity is consistent with what subsequent verifications
2955
+ // (e.g. verifyAtomicWriteResult) will obtain by opening the same file.
2956
+ targetStat = readHandleStat;
2957
+ }
2506
2958
  });
2507
2959
  }
2508
2960
  catch (error) {
@@ -3598,21 +4050,6 @@ function normalizePinnedPathError(error) {
3598
4050
  });
3599
4051
  }
3600
4052
 
3601
- ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/test-hooks.js
3602
- let test_hooks_fsSafeTestHooks;
3603
- function allowFsSafeTestHooks() {
3604
- return false || process.env.VITEST === "true";
3605
- }
3606
- function getFsSafeTestHooks() {
3607
- return test_hooks_fsSafeTestHooks;
3608
- }
3609
- function __setFsSafeTestHooksForTest(hooks) {
3610
- if (hooks && !allowFsSafeTestHooks()) {
3611
- throw new Error("__setFsSafeTestHooksForTest is only available in tests");
3612
- }
3613
- test_hooks_fsSafeTestHooks = hooks;
3614
- }
3615
-
3616
4053
  ;// CONCATENATED MODULE: ../../node_modules/@openclaw/fs-safe/dist/json-stringify.js
3617
4054
  function stringifyJsonDocument(value, replacer, space) {
3618
4055
  const text = JSON.stringify(value, replacer, space);
@@ -3948,6 +4385,7 @@ class RootHandle {
3948
4385
  data,
3949
4386
  mkdir: this.defaults.mkdir,
3950
4387
  mode: this.defaults.mode,
4388
+ renameIdentity: this.defaults.renameIdentity,
3951
4389
  ...options,
3952
4390
  denyMutations: mergeDenyMutationPolicies(this.defaults.denyMutations, options.denyMutations),
3953
4391
  });
@@ -4382,21 +4820,23 @@ async function writeFileInRoot(root, params) {
4382
4820
  async function commitPinnedWriteInRoot(root, pinned, params) {
4383
4821
  let identity;
4384
4822
  try {
4385
- identity = await runPinnedWriteHelper({
4823
+ identity = await runPinnedWriteWithRenamePolicy({
4386
4824
  rootPath: pinned.rootReal,
4387
4825
  relativeParentPath: pinned.relativeParentPath,
4388
4826
  basename: pinned.basename,
4827
+ targetPath: pinned.targetPath,
4828
+ renameIdentity: params.renameIdentity,
4389
4829
  mkdir: params.mkdir !== false,
4390
4830
  mode: params.mode ?? pinned.mode,
4391
4831
  overwrite: params.overwrite,
4392
- input: {
4393
- kind: "buffer",
4394
- data: params.data,
4395
- encoding: params.encoding,
4396
- },
4832
+ input: { kind: "buffer", data: params.data, encoding: params.encoding },
4397
4833
  });
4398
4834
  }
4399
4835
  catch (error) {
4836
+ const errorCode = error?.code;
4837
+ if (errorCode === "file_lock_stale" || errorCode === "file_lock_timeout") {
4838
+ throw error;
4839
+ }
4400
4840
  if (params.overwrite === false && isAlreadyExistsError(error)) {
4401
4841
  throw new errors_FsSafeError("already-exists", "file already exists", {
4402
4842
  cause: error instanceof Error ? error : undefined,
@@ -35399,77 +35839,6 @@ function safeDestr(value, options = {}) {
35399
35839
 
35400
35840
 
35401
35841
 
35402
- ;// CONCATENATED MODULE: ../../node_modules/defu/dist/defu.mjs
35403
- function defu_isPlainObject(value) {
35404
- if (value === null || typeof value !== "object") {
35405
- return false;
35406
- }
35407
- const prototype = Object.getPrototypeOf(value);
35408
- if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
35409
- return false;
35410
- }
35411
- if (Symbol.iterator in value) {
35412
- return false;
35413
- }
35414
- if (Symbol.toStringTag in value) {
35415
- return Object.prototype.toString.call(value) === "[object Module]";
35416
- }
35417
- return true;
35418
- }
35419
-
35420
- function defu_defu(baseObject, defaults, namespace = ".", merger) {
35421
- if (!defu_isPlainObject(defaults)) {
35422
- return defu_defu(baseObject, {}, namespace, merger);
35423
- }
35424
- const object = { ...defaults };
35425
- for (const key of Object.keys(baseObject)) {
35426
- if (key === "__proto__" || key === "constructor") {
35427
- continue;
35428
- }
35429
- const value = baseObject[key];
35430
- if (value === null || value === void 0) {
35431
- continue;
35432
- }
35433
- if (merger && merger(object, key, value, namespace)) {
35434
- continue;
35435
- }
35436
- if (Array.isArray(value) && Array.isArray(object[key])) {
35437
- object[key] = [...value, ...object[key]];
35438
- } else if (defu_isPlainObject(value) && defu_isPlainObject(object[key])) {
35439
- object[key] = defu_defu(
35440
- value,
35441
- object[key],
35442
- (namespace ? `${namespace}.` : "") + key.toString(),
35443
- merger
35444
- );
35445
- } else {
35446
- object[key] = value;
35447
- }
35448
- }
35449
- return object;
35450
- }
35451
- function defu_createDefu(merger) {
35452
- return (...arguments_) => (
35453
- // eslint-disable-next-line unicorn/no-array-reduce
35454
- arguments_.reduce((p, c) => defu_defu(p, c, "", merger), {})
35455
- );
35456
- }
35457
- const dist_defu_defu = defu_createDefu();
35458
- const defuFn = defu_createDefu((object, key, currentValue) => {
35459
- if (object[key] !== void 0 && typeof currentValue === "function") {
35460
- object[key] = currentValue(object[key]);
35461
- return true;
35462
- }
35463
- });
35464
- const defuArrayFn = defu_createDefu((object, key, currentValue) => {
35465
- if (Array.isArray(object[key]) && typeof currentValue === "function") {
35466
- object[key] = currentValue(object[key]);
35467
- return true;
35468
- }
35469
- });
35470
-
35471
-
35472
-
35473
35842
  ;// CONCATENATED MODULE: ../../node_modules/rc9/dist/index.mjs
35474
35843
 
35475
35844
 
@@ -35630,6 +35999,77 @@ function updateUser(config, options) {
35630
35999
  //#endregion
35631
36000
 
35632
36001
 
36002
+ ;// CONCATENATED MODULE: ../../node_modules/defu/dist/defu.mjs
36003
+ function defu_isPlainObject(value) {
36004
+ if (value === null || typeof value !== "object") {
36005
+ return false;
36006
+ }
36007
+ const prototype = Object.getPrototypeOf(value);
36008
+ if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
36009
+ return false;
36010
+ }
36011
+ if (Symbol.iterator in value) {
36012
+ return false;
36013
+ }
36014
+ if (Symbol.toStringTag in value) {
36015
+ return Object.prototype.toString.call(value) === "[object Module]";
36016
+ }
36017
+ return true;
36018
+ }
36019
+
36020
+ function defu_defu(baseObject, defaults, namespace = ".", merger) {
36021
+ if (!defu_isPlainObject(defaults)) {
36022
+ return defu_defu(baseObject, {}, namespace, merger);
36023
+ }
36024
+ const object = { ...defaults };
36025
+ for (const key of Object.keys(baseObject)) {
36026
+ if (key === "__proto__" || key === "constructor") {
36027
+ continue;
36028
+ }
36029
+ const value = baseObject[key];
36030
+ if (value === null || value === void 0) {
36031
+ continue;
36032
+ }
36033
+ if (merger && merger(object, key, value, namespace)) {
36034
+ continue;
36035
+ }
36036
+ if (Array.isArray(value) && Array.isArray(object[key])) {
36037
+ object[key] = [...value, ...object[key]];
36038
+ } else if (defu_isPlainObject(value) && defu_isPlainObject(object[key])) {
36039
+ object[key] = defu_defu(
36040
+ value,
36041
+ object[key],
36042
+ (namespace ? `${namespace}.` : "") + key.toString(),
36043
+ merger
36044
+ );
36045
+ } else {
36046
+ object[key] = value;
36047
+ }
36048
+ }
36049
+ return object;
36050
+ }
36051
+ function defu_createDefu(merger) {
36052
+ return (...arguments_) => (
36053
+ // eslint-disable-next-line unicorn/no-array-reduce
36054
+ arguments_.reduce((p, c) => defu_defu(p, c, "", merger), {})
36055
+ );
36056
+ }
36057
+ const dist_defu_defu = defu_createDefu();
36058
+ const defuFn = (/* unused pure expression or super */ null && (defu_createDefu((object, key, currentValue) => {
36059
+ if (object[key] !== void 0 && typeof currentValue === "function") {
36060
+ object[key] = currentValue(object[key]);
36061
+ return true;
36062
+ }
36063
+ })));
36064
+ const defuArrayFn = (/* unused pure expression or super */ null && (defu_createDefu((object, key, currentValue) => {
36065
+ if (Array.isArray(object[key]) && typeof currentValue === "function") {
36066
+ object[key] = currentValue(object[key]);
36067
+ return true;
36068
+ }
36069
+ })));
36070
+
36071
+
36072
+
35633
36073
  // EXTERNAL MODULE: ../../node_modules/confbox/dist/_chunks/_format.mjs
35634
36074
  var _format = __webpack_require__(5492);
35635
36075
  ;// CONCATENATED MODULE: ../../node_modules/confbox/dist/_chunks/json.mjs
@@ -40535,7 +40975,7 @@ exports.prettifyError = prettifyError;
40535
40975
 
40536
40976
  },
40537
40977
  3519(__unused_rspack_module, exports, __webpack_require__) {
40538
- var __webpack_unused_export__;
40978
+ var __rspack_unused_export;
40539
40979
 
40540
40980
 
40541
40981
  var composer = __webpack_require__(8480);
@@ -40557,35 +40997,35 @@ var visit = __webpack_require__(4364);
40557
40997
 
40558
40998
 
40559
40999
 
40560
- __webpack_unused_export__ = composer.Composer;
40561
- __webpack_unused_export__ = Document.Document;
40562
- __webpack_unused_export__ = Schema.Schema;
40563
- __webpack_unused_export__ = errors.YAMLError;
40564
- __webpack_unused_export__ = errors.YAMLParseError;
40565
- __webpack_unused_export__ = errors.YAMLWarning;
40566
- __webpack_unused_export__ = Alias.Alias;
40567
- __webpack_unused_export__ = identity.isAlias;
40568
- __webpack_unused_export__ = identity.isCollection;
40569
- __webpack_unused_export__ = identity.isDocument;
40570
- __webpack_unused_export__ = identity.isMap;
40571
- __webpack_unused_export__ = identity.isNode;
40572
- __webpack_unused_export__ = identity.isPair;
40573
- __webpack_unused_export__ = identity.isScalar;
40574
- __webpack_unused_export__ = identity.isSeq;
40575
- __webpack_unused_export__ = Pair.Pair;
40576
- __webpack_unused_export__ = Scalar.Scalar;
40577
- __webpack_unused_export__ = YAMLMap.YAMLMap;
40578
- __webpack_unused_export__ = YAMLSeq.YAMLSeq;
40579
- __webpack_unused_export__ = cst;
40580
- __webpack_unused_export__ = lexer.Lexer;
40581
- __webpack_unused_export__ = lineCounter.LineCounter;
40582
- __webpack_unused_export__ = parser.Parser;
41000
+ __rspack_unused_export = composer.Composer;
41001
+ __rspack_unused_export = Document.Document;
41002
+ __rspack_unused_export = Schema.Schema;
41003
+ __rspack_unused_export = errors.YAMLError;
41004
+ __rspack_unused_export = errors.YAMLParseError;
41005
+ __rspack_unused_export = errors.YAMLWarning;
41006
+ __rspack_unused_export = Alias.Alias;
41007
+ __rspack_unused_export = identity.isAlias;
41008
+ __rspack_unused_export = identity.isCollection;
41009
+ __rspack_unused_export = identity.isDocument;
41010
+ __rspack_unused_export = identity.isMap;
41011
+ __rspack_unused_export = identity.isNode;
41012
+ __rspack_unused_export = identity.isPair;
41013
+ __rspack_unused_export = identity.isScalar;
41014
+ __rspack_unused_export = identity.isSeq;
41015
+ __rspack_unused_export = Pair.Pair;
41016
+ __rspack_unused_export = Scalar.Scalar;
41017
+ __rspack_unused_export = YAMLMap.YAMLMap;
41018
+ __rspack_unused_export = YAMLSeq.YAMLSeq;
41019
+ __rspack_unused_export = cst;
41020
+ __rspack_unused_export = lexer.Lexer;
41021
+ __rspack_unused_export = lineCounter.LineCounter;
41022
+ __rspack_unused_export = parser.Parser;
40583
41023
  exports.qg = publicApi.parse;
40584
- __webpack_unused_export__ = publicApi.parseAllDocuments;
40585
- __webpack_unused_export__ = publicApi.parseDocument;
40586
- __webpack_unused_export__ = publicApi.stringify;
40587
- __webpack_unused_export__ = visit.visit;
40588
- __webpack_unused_export__ = visit.visitAsync;
41024
+ __rspack_unused_export = publicApi.parseAllDocuments;
41025
+ __rspack_unused_export = publicApi.parseDocument;
41026
+ __rspack_unused_export = publicApi.stringify;
41027
+ __rspack_unused_export = visit.visit;
41028
+ __rspack_unused_export = visit.visitAsync;
40589
41029
 
40590
41030
 
40591
41031
  },
@@ -46497,14 +46937,14 @@ __webpack_require__.r = (exports) => {
46497
46937
  var installedChunks = {410: 0,};
46498
46938
  var installChunk = (data) => {
46499
46939
  var __rspack_esm_ids = data.__rspack_esm_ids;
46500
- var __webpack_modules__ = data.__webpack_modules__;
46940
+ var moreModules = data.__webpack_modules__;
46501
46941
  var __rspack_esm_runtime = data.__rspack_esm_runtime;
46502
46942
  // add "modules" to the modules object,
46503
46943
  // then flag all "ids" as loaded and fire callback
46504
46944
  var moduleId, chunkId, i = 0;
46505
- for (moduleId in __webpack_modules__) {
46506
- if (__webpack_require__.o(__webpack_modules__, moduleId)) {
46507
- __webpack_require__.m[moduleId] = __webpack_modules__[moduleId];
46945
+ for (moduleId in moreModules) {
46946
+ if (__webpack_require__.o(moreModules, moduleId)) {
46947
+ __webpack_require__.m[moduleId] = moreModules[moduleId];
46508
46948
  }
46509
46949
  }
46510
46950
  if (__rspack_esm_runtime) __rspack_esm_runtime(__webpack_require__);
@@ -46517,6 +46957,7 @@ __webpack_require__.r = (exports) => {
46517
46957
  }
46518
46958
 
46519
46959
  };
46960
+
46520
46961
  __webpack_require__.f.j = function (chunkId, promises) {
46521
46962
  // import() chunk loading for javascript
46522
46963
  var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
@@ -46549,7 +46990,7 @@ if (installedChunkData !== 0) { // 0 means "already installed".'
46549
46990
  // module factories are used so entry inlining is disabled
46550
46991
  // startup
46551
46992
  // Load entry module and return exports
46552
- var __webpack_exports__ = __webpack_require__(2749);
46993
+ var __webpack_exports__ = __webpack_require__(6179);
46553
46994
  var __webpack_exports__DEFAULT_LINT_RULES = __webpack_exports__.Kd;
46554
46995
  var __webpack_exports__LintValidationError = __webpack_exports__.F5;
46555
46996
  var __webpack_exports__createFormatter = __webpack_exports__.xi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lousy-agents/lint",
3
- "version": "5.15.6",
3
+ "version": "5.16.0",
4
4
  "description": "Programmatic lint API for validating AI coding assistant configurations — skills, agents, hooks, and instructions",
5
5
  "type": "module",
6
6
  "repository": {