@daeda/mcp-pro 0.1.25 → 0.1.26

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 +99 -51
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -2329,77 +2329,105 @@ var buildCappedSelectSql = (sql, maxRows) => {
2329
2329
  };
2330
2330
 
2331
2331
  // src/layers/MasterLockLive.ts
2332
- import { createRequire } from "module";
2333
2332
  import path2 from "path";
2334
- import { mkdir, open } from "fs/promises";
2335
2333
  import { Effect as Effect24, Layer as Layer2, Ref, pipe as pipe13 } from "effect";
2336
- var PROMOTION_POLL_MS = 3e3;
2337
- var lockFilePath = () => path2.join(PORTALS_DIR(), "master.lock");
2338
- var loadOsLockModule = () => {
2334
+
2335
+ // src/layers/MasterLockDependencies.ts
2336
+ import { createRequire } from "module";
2337
+ import { mkdir } from "fs/promises";
2338
+ var loadProperLockfileModule = () => {
2339
2339
  const requireFromHere = createRequire(import.meta.url);
2340
- return requireFromHere("os-lock");
2340
+ return requireFromHere("proper-lockfile");
2341
2341
  };
2342
- var resolveDependencies = (config = {}) => ({
2342
+ var resolveMasterLockDependencies = (config, defaults) => ({
2343
2343
  ensurePortalsDir: config.ensurePortalsDir ?? (async (dirPath) => {
2344
2344
  await mkdir(dirPath, { recursive: true });
2345
2345
  }),
2346
- openLockFile: config.openLockFile ?? ((filePath) => open(filePath, "a+")),
2347
- lock: config.lock ?? loadOsLockModule().lock,
2348
- unlock: config.unlock ?? loadOsLockModule().unlock,
2346
+ acquireLock: config.acquireLock ?? (async (dirPath, options2) => {
2347
+ const release = await loadProperLockfileModule().lock(dirPath, options2);
2348
+ return { release };
2349
+ }),
2349
2350
  setInterval: config.setInterval ?? ((callback, ms) => setInterval(callback, ms)),
2350
2351
  clearInterval: config.clearInterval ?? ((timer) => clearInterval(timer)),
2351
2352
  log: config.log ?? ((message) => console.error(message)),
2352
- promotionPollMs: config.promotionPollMs ?? PROMOTION_POLL_MS
2353
+ promotionPollMs: config.promotionPollMs ?? defaults.promotionPollMs
2353
2354
  });
2355
+
2356
+ // src/layers/MasterLockLive.ts
2357
+ var PROMOTION_POLL_MS = 3e3;
2358
+ var LOCK_STALE_MS = 5e3;
2359
+ var LOCK_UPDATE_MS = 2500;
2360
+ var lockFilePath = () => path2.join(PORTALS_DIR(), "master.lock");
2354
2361
  var logMessage = (runtime, message) => Effect24.sync(() => runtime.deps.log(message));
2355
2362
  var isLockConflictError = (error) => {
2356
2363
  const code = error?.code;
2357
- return code === "EACCES" || code === "EAGAIN" || code === "EBUSY";
2364
+ return code === "ELOCKED";
2358
2365
  };
2359
2366
  var ensurePortalsDir = (runtime) => Effect24.tryPromise({
2360
2367
  try: () => runtime.deps.ensurePortalsDir(PORTALS_DIR()),
2361
2368
  catch: (cause) => new Error(`Failed to create portals directory: ${String(cause)}`)
2362
2369
  });
2363
- var closeHandle = (runtime, handle) => Effect24.tryPromise({
2370
+ var releaseLease = (runtime, lease) => Effect24.tryPromise({
2364
2371
  try: async () => {
2365
- try {
2366
- await runtime.deps.unlock(handle.fd);
2367
- } catch {
2368
- }
2369
- try {
2370
- await handle.close();
2371
- } catch {
2372
- }
2372
+ await lease.release();
2373
2373
  },
2374
- catch: (cause) => new Error(`Failed to close master lock handle: ${String(cause)}`)
2374
+ catch: (cause) => new Error(`Failed to release master lock lease: ${String(cause)}`)
2375
+ });
2376
+ var acquireLockOptions = (runtime) => ({
2377
+ lockfilePath: lockFilePath(),
2378
+ retries: 0,
2379
+ stale: LOCK_STALE_MS,
2380
+ update: LOCK_UPDATE_MS,
2381
+ realpath: true,
2382
+ onCompromised: (error) => {
2383
+ Effect24.runFork(
2384
+ pipe13(
2385
+ handleCompromisedLease(runtime, error),
2386
+ Effect24.catchAll(() => Effect24.void)
2387
+ )
2388
+ );
2389
+ }
2375
2390
  });
2391
+ var handleCompromisedLease = (runtime, error) => pipe13(
2392
+ Ref.get(runtime.releasedRef),
2393
+ Effect24.flatMap((released) => {
2394
+ if (released) return Effect24.void;
2395
+ return pipe13(
2396
+ Ref.getAndSet(runtime.lockLeaseRef, null),
2397
+ Effect24.flatMap((lease) => {
2398
+ if (lease === null) return Effect24.void;
2399
+ return pipe13(
2400
+ logMessage(
2401
+ runtime,
2402
+ `[master-lock] master lease compromised: ${error.message}`
2403
+ ),
2404
+ Effect24.flatMap(() => Ref.set(runtime.connectionTypeRef, "READ_ONLY")),
2405
+ Effect24.flatMap(() => startPromotionPolling(runtime))
2406
+ );
2407
+ })
2408
+ );
2409
+ })
2410
+ );
2376
2411
  var tryAcquireMasterLock = (runtime) => pipe13(
2377
- Ref.get(runtime.lockHandleRef),
2378
- Effect24.flatMap((existingHandle) => {
2379
- if (existingHandle !== null) {
2412
+ Ref.get(runtime.lockLeaseRef),
2413
+ Effect24.flatMap((existingLease) => {
2414
+ if (existingLease !== null) {
2380
2415
  return Ref.get(runtime.connectionTypeRef);
2381
2416
  }
2382
2417
  return pipe13(
2383
2418
  ensurePortalsDir(runtime),
2384
2419
  Effect24.flatMap(
2385
2420
  () => Effect24.tryPromise({
2386
- try: () => runtime.deps.openLockFile(lockFilePath()),
2387
- catch: (cause) => new Error(`Failed to open master lock file: ${String(cause)}`)
2388
- })
2389
- ),
2390
- Effect24.flatMap(
2391
- (handle) => Effect24.tryPromise({
2392
2421
  try: async () => {
2393
2422
  try {
2394
- await runtime.deps.lock(handle.fd, {
2395
- exclusive: true,
2396
- immediate: true
2397
- });
2398
- return { connectionType: "MASTER", handle };
2423
+ const lease = await runtime.deps.acquireLock(
2424
+ PORTALS_DIR(),
2425
+ acquireLockOptions(runtime)
2426
+ );
2427
+ return { connectionType: "MASTER", lease };
2399
2428
  } catch (error) {
2400
- await handle.close();
2401
2429
  if (isLockConflictError(error)) {
2402
- return { connectionType: "READ_ONLY", handle: null };
2430
+ return { connectionType: "READ_ONLY", lease: null };
2403
2431
  }
2404
2432
  throw error;
2405
2433
  }
@@ -2408,10 +2436,21 @@ var tryAcquireMasterLock = (runtime) => pipe13(
2408
2436
  })
2409
2437
  ),
2410
2438
  Effect24.flatMap(
2411
- ({ connectionType, handle }) => pipe13(
2412
- Ref.set(runtime.connectionTypeRef, connectionType),
2413
- Effect24.flatMap(() => Ref.set(runtime.lockHandleRef, handle)),
2414
- Effect24.as(connectionType)
2439
+ ({ connectionType, lease }) => pipe13(
2440
+ Ref.get(runtime.releasedRef),
2441
+ Effect24.flatMap((released) => {
2442
+ if (!released || lease === null) {
2443
+ return pipe13(
2444
+ Ref.set(runtime.connectionTypeRef, connectionType),
2445
+ Effect24.flatMap(() => Ref.set(runtime.lockLeaseRef, lease)),
2446
+ Effect24.as(connectionType)
2447
+ );
2448
+ }
2449
+ return pipe13(
2450
+ releaseLease(runtime, lease),
2451
+ Effect24.flatMap(() => Effect24.succeed("READ_ONLY"))
2452
+ );
2453
+ })
2415
2454
  )
2416
2455
  )
2417
2456
  );
@@ -2426,9 +2465,12 @@ var stopPromotionPolling = (runtime) => pipe13(
2426
2465
  )
2427
2466
  );
2428
2467
  var tryPromoteToMaster = (runtime) => pipe13(
2429
- Ref.get(runtime.promotingRef),
2430
- Effect24.flatMap((promoting) => {
2431
- if (promoting) return Effect24.void;
2468
+ Effect24.all({
2469
+ promoting: Ref.get(runtime.promotingRef),
2470
+ released: Ref.get(runtime.releasedRef)
2471
+ }),
2472
+ Effect24.flatMap(({ promoting, released }) => {
2473
+ if (promoting || released) return Effect24.void;
2432
2474
  return pipe13(
2433
2475
  Ref.set(runtime.promotingRef, true),
2434
2476
  Effect24.flatMap(
@@ -2478,20 +2520,26 @@ var startPromotionPolling = (runtime) => pipe13(
2478
2520
  })
2479
2521
  );
2480
2522
  var releaseRuntime = (runtime) => pipe13(
2481
- stopPromotionPolling(runtime),
2482
- Effect24.flatMap(() => Ref.getAndSet(runtime.lockHandleRef, null)),
2523
+ Ref.set(runtime.releasedRef, true),
2524
+ Effect24.flatMap(() => stopPromotionPolling(runtime)),
2525
+ Effect24.flatMap(() => Ref.getAndSet(runtime.lockLeaseRef, null)),
2483
2526
  Effect24.flatMap(
2484
- (handle) => handle === null ? Effect24.void : closeHandle(runtime, handle)
2527
+ (lease) => lease === null ? Effect24.void : releaseLease(runtime, lease)
2485
2528
  ),
2486
2529
  Effect24.flatMap(() => Ref.set(runtime.connectionTypeRef, "READ_ONLY"))
2487
2530
  );
2488
2531
  var makeRuntime = (config = {}) => pipe13(
2489
2532
  Effect24.all({
2490
- deps: Effect24.sync(() => resolveDependencies(config)),
2533
+ deps: Effect24.sync(
2534
+ () => resolveMasterLockDependencies(config, {
2535
+ promotionPollMs: PROMOTION_POLL_MS
2536
+ })
2537
+ ),
2491
2538
  connectionTypeRef: Ref.make("READ_ONLY"),
2492
- lockHandleRef: Ref.make(null),
2539
+ lockLeaseRef: Ref.make(null),
2493
2540
  promotionTimerRef: Ref.make(null),
2494
- promotingRef: Ref.make(false)
2541
+ promotingRef: Ref.make(false),
2542
+ releasedRef: Ref.make(false)
2495
2543
  }),
2496
2544
  Effect24.tap(
2497
2545
  (runtime) => pipe13(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daeda/mcp-pro",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "MCP server for HubSpot CRM — sync, query, and manage your portal data",
5
5
  "type": "module",
6
6
  "bin": {
@@ -35,8 +35,8 @@
35
35
  "@modelcontextprotocol/sdk": "^1.25.3",
36
36
  "effect": "3.19.15",
37
37
  "fflate": "^0.8.2",
38
- "os-lock": "^2.0.0",
39
38
  "papaparse": "^5.5.3",
39
+ "proper-lockfile": "^4.1.2",
40
40
  "zod": "^3.23.8"
41
41
  },
42
42
  "devDependencies": {
@@ -49,6 +49,6 @@
49
49
  },
50
50
  "trustedDependencies": [
51
51
  "fs-ext",
52
- "os-lock"
52
+ "proper-lockfile"
53
53
  ]
54
54
  }