@floomhq/floom-mcp-sync 1.0.37 → 1.0.38

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/auto-sync.js CHANGED
@@ -346,7 +346,7 @@ async function manifestHasMissingTrackedFile(manifest, root) {
346
346
  }
347
347
  return false;
348
348
  }
349
- export async function autoSync(log = (message) => process.stderr.write(`${message}\n`), signal) {
349
+ export async function autoSync(log = (message) => process.stderr.write(`${message}\n`), signal, opts = {}) {
350
350
  const initialCfg = await readConfig();
351
351
  if (!initialCfg) {
352
352
  maybeAuthWarning(log, "[floom] not signed in; skipping sync (run `npx -y @floomhq/floom login`)");
@@ -354,7 +354,7 @@ export async function autoSync(log = (message) => process.stderr.write(`${messag
354
354
  }
355
355
  let cfg = initialCfg;
356
356
  await ensureSyncManifestDir();
357
- return await withSyncLock(async () => {
357
+ const result = await withSyncLock(async () => {
358
358
  const manifest = await readSyncManifest();
359
359
  const root = skillsDir();
360
360
  const apiUrl = apiUrlFromConfig(cfg);
@@ -527,7 +527,11 @@ export async function autoSync(log = (message) => process.stderr.write(`${messag
527
527
  maybeHeartbeat(log, () => `[floom] heartbeat: ${total} skills tracked, all up-to-date`);
528
528
  }
529
529
  return { synced: total, unchanged, updated, conflicts };
530
- });
530
+ }, { wait: !opts.skipIfLocked });
531
+ if (result)
532
+ return result;
533
+ log("[floom] sync already running; skipping background poll");
534
+ return { synced: 0, unchanged: 0, updated: 0, conflicts: 0 };
531
535
  }
532
536
  async function loadSyncPayload(apiUrl, token, signal) {
533
537
  const all = [];
@@ -225,10 +225,11 @@ export async function ensureSyncManifestDir() {
225
225
  throw err;
226
226
  }
227
227
  }
228
- export async function withSyncLock(fn) {
228
+ export async function withSyncLock(fn, opts = {}) {
229
229
  await ensureSyncManifestDir();
230
230
  const lockPath = join(dirname(syncManifestPath()), "sync.lock");
231
231
  const startedAt = Date.now();
232
+ const wait = opts.wait !== false;
232
233
  for (;;) {
233
234
  try {
234
235
  await mkdir(lockPath, { mode: 0o700 });
@@ -249,6 +250,8 @@ export async function withSyncLock(fn) {
249
250
  continue;
250
251
  throw statErr;
251
252
  }
253
+ if (!wait)
254
+ return null;
252
255
  if (Date.now() - startedAt > LOCK_TIMEOUT_MS) {
253
256
  throw new Error("Timed out waiting for Floom sync lock.");
254
257
  }
package/dist/server.js CHANGED
@@ -6,19 +6,19 @@ import { clearSyncLock } from "./lib/manifest.js";
6
6
  import { getSkill } from "./tools/get.js";
7
7
  import { searchSkills } from "./tools/search.js";
8
8
  import { syncStatus } from "./tools/status.js";
9
- const SERVER_VERSION = "1.0.37";
9
+ const SERVER_VERSION = "1.0.38";
10
10
  const DEFAULT_INTERVAL_MS = 60_000;
11
11
  const MIN_INTERVAL_MS = 10_000;
12
12
  const SEARCH_TYPES = new Set(["knowledge", "instruction", "workflow", "skill"]);
13
13
  let syncInFlight = null;
14
14
  let syncAbortController = null;
15
15
  let shuttingDown = false;
16
- function runAutoSync() {
16
+ function runAutoSync(opts = {}) {
17
17
  if (syncInFlight)
18
18
  return syncInFlight;
19
19
  const controller = new AbortController();
20
20
  syncAbortController = controller;
21
- syncInFlight = autoSync(undefined, controller.signal).finally(() => {
21
+ syncInFlight = autoSync(undefined, controller.signal, opts).finally(() => {
22
22
  if (syncAbortController === controller)
23
23
  syncAbortController = null;
24
24
  syncInFlight = null;
@@ -114,7 +114,7 @@ function startPolling(intervalMs, state) {
114
114
  return;
115
115
  }
116
116
  state.inFlight = true;
117
- runAutoSync().catch((err) => {
117
+ runAutoSync({ skipIfLocked: true }).catch((err) => {
118
118
  process.stderr.write(`[floom] poll failed: ${err instanceof Error ? err.message : String(err)}\n`);
119
119
  }).finally(() => {
120
120
  state.inFlight = false;
@@ -293,7 +293,7 @@ async function main() {
293
293
  const intervalMs = resolvePollIntervalMs();
294
294
  process.stderr.write(`[floom] starting sync poller (interval ${intervalMs}ms)\n`);
295
295
  const syncState = { inFlight: true };
296
- void runAutoSync().catch((err) => {
296
+ void runAutoSync({ skipIfLocked: true }).catch((err) => {
297
297
  process.stderr.write(`[floom] initial sync failed: ${err instanceof Error ? err.message : String(err)}\n`);
298
298
  }).finally(() => {
299
299
  syncState.inFlight = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom-mcp-sync",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "Lightweight Floom MCP server for installing, publishing, and startup-syncing skills.",
5
5
  "license": "MIT",
6
6
  "type": "module",