@floomhq/floom-mcp-sync 1.0.35 → 1.0.37

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.
@@ -3,7 +3,7 @@ import { dirname } from "node:path";
3
3
  import { configPath } from "./paths.js";
4
4
  export const DEFAULT_API_URL = "https://floom.dev";
5
5
  export function apiUrlFromConfig(cfg) {
6
- return (cfg.apiUrl ?? process.env.FLOOM_API_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
6
+ return (process.env.FLOOM_API_URL ?? cfg.apiUrl ?? DEFAULT_API_URL).replace(/\/$/, "");
7
7
  }
8
8
  export async function readConfig() {
9
9
  try {
@@ -1,8 +1,7 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
3
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
- import { configPath } from "./paths.js";
4
+ import { configPath, hasStaleNativeCodexEnv, homeDir, skillsDir } from "./paths.js";
6
5
  const MANIFEST_VERSION = 1;
7
6
  const SLUG_RE = /^[A-Za-z0-9_-]{1,128}$/;
8
7
  const FD_PATH_ROOT = "/proc/self/fd";
@@ -96,13 +95,15 @@ const ROOT_SUPPORT_FILES = new Set([
96
95
  export function syncManifestPath() {
97
96
  if (process.env.FLOOM_SYNC_MANIFEST_PATH)
98
97
  return expandHome(process.env.FLOOM_SYNC_MANIFEST_PATH);
98
+ if (hasStaleNativeCodexEnv())
99
+ return join(skillsDir(), ".floom-sync-manifest.json");
99
100
  return join(dirname(configPath()), "sync-manifest.json");
100
101
  }
101
102
  function expandHome(path) {
102
103
  if (path === "~")
103
- return homedir();
104
+ return homeDir();
104
105
  if (path.startsWith("~/"))
105
- return join(homedir(), path.slice(2));
106
+ return join(homeDir(), path.slice(2));
106
107
  return path;
107
108
  }
108
109
  function emptyManifest() {
@@ -261,6 +262,9 @@ export async function withSyncLock(fn) {
261
262
  await rm(lockPath, { recursive: true, force: true }).catch(() => { });
262
263
  }
263
264
  }
265
+ export async function clearSyncLock() {
266
+ await rm(join(dirname(syncManifestPath()), "sync.lock"), { recursive: true, force: true }).catch(() => { });
267
+ }
264
268
  export function manifestKey(root, target) {
265
269
  const relativeTarget = relative(resolve(root), resolve(target));
266
270
  if (relativeTarget === ".." || relativeTarget.startsWith(`..${sep}`)) {
package/dist/lib/paths.js CHANGED
@@ -2,14 +2,20 @@ import { homedir } from "node:os";
2
2
  import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
3
  import { assertValidSlug } from "./slug.js";
4
4
  export function configPath() {
5
- return process.env.FLOOM_CONFIG_PATH ?? join(homedir(), ".floom", "config.json");
5
+ return process.env.FLOOM_CONFIG_PATH ?? join(homeDir(), ".floom", "config.json");
6
6
  }
7
- export function skillsDir() {
8
- if (!process.env.FLOOM_SKILLS_DIR &&
7
+ export function homeDir() {
8
+ return process.env.HOME ? resolve(process.env.HOME) : homedir();
9
+ }
10
+ export function hasStaleNativeCodexEnv() {
11
+ return Boolean(!process.env.FLOOM_SKILLS_DIR &&
9
12
  !process.env.FLOOM_ALLOW_NATIVE_CODEX_SYNC &&
10
13
  process.env.CODEX_SKILLS_DIR &&
11
- resolve(expandHome(process.env.CODEX_SKILLS_DIR)) === resolve(join(homedir(), ".codex", "skills"))) {
12
- return join(homedir(), ".floom", "skill-cache", "codex");
14
+ resolve(expandHome(process.env.CODEX_SKILLS_DIR)) === resolve(join(homeDir(), ".codex", "skills")));
15
+ }
16
+ export function skillsDir() {
17
+ if (hasStaleNativeCodexEnv()) {
18
+ return join(homeDir(), ".floom", "skill-cache", "codex");
13
19
  }
14
20
  return expandHome(process.env.FLOOM_SKILLS_DIR
15
21
  ?? process.env.CLAUDE_SKILLS_DIR
@@ -21,9 +27,9 @@ export function skillsDir() {
21
27
  }
22
28
  function expandHome(path) {
23
29
  if (path === "~")
24
- return homedir();
30
+ return homeDir();
25
31
  if (path.startsWith("~/"))
26
- return join(homedir(), path.slice(2));
32
+ return join(homeDir(), path.slice(2));
27
33
  return path;
28
34
  }
29
35
  export function skillPath(slug) {
package/dist/server.js CHANGED
@@ -2,15 +2,17 @@
2
2
  import { createInterface } from "node:readline";
3
3
  import { stdin, stdout } from "node:process";
4
4
  import { autoSync } from "./auto-sync.js";
5
+ import { clearSyncLock } from "./lib/manifest.js";
5
6
  import { getSkill } from "./tools/get.js";
6
7
  import { searchSkills } from "./tools/search.js";
7
8
  import { syncStatus } from "./tools/status.js";
8
- const SERVER_VERSION = "1.0.35";
9
+ const SERVER_VERSION = "1.0.37";
9
10
  const DEFAULT_INTERVAL_MS = 60_000;
10
11
  const MIN_INTERVAL_MS = 10_000;
11
12
  const SEARCH_TYPES = new Set(["knowledge", "instruction", "workflow", "skill"]);
12
13
  let syncInFlight = null;
13
14
  let syncAbortController = null;
15
+ let shuttingDown = false;
14
16
  function runAutoSync() {
15
17
  if (syncInFlight)
16
18
  return syncInFlight;
@@ -27,6 +29,24 @@ function abortAutoSync() {
27
29
  syncAbortController?.abort();
28
30
  syncAbortController = null;
29
31
  }
32
+ async function stopAutoSync() {
33
+ const inFlight = syncInFlight;
34
+ abortAutoSync();
35
+ if (!inFlight)
36
+ return;
37
+ try {
38
+ await Promise.race([
39
+ inFlight,
40
+ new Promise((resolve) => setTimeout(resolve, 750)),
41
+ ]);
42
+ }
43
+ catch {
44
+ // Shutdown is best-effort; autoSync already logged actionable failures.
45
+ }
46
+ finally {
47
+ await clearSyncLock();
48
+ }
49
+ }
30
50
  function usage() {
31
51
  return `
32
52
  floom-mcp-sync v${SERVER_VERSION}
@@ -280,10 +300,12 @@ async function main() {
280
300
  });
281
301
  const pollHandle = startPolling(intervalMs, syncState);
282
302
  const shutdown = (signal) => {
303
+ if (shuttingDown)
304
+ return;
305
+ shuttingDown = true;
283
306
  clearInterval(pollHandle);
284
- abortAutoSync();
285
307
  process.stderr.write(`[floom] received ${signal}, stopping sync poller\n`);
286
- process.exit(0);
308
+ void stopAutoSync().finally(() => process.exit(0));
287
309
  };
288
310
  process.once("SIGINT", () => shutdown("SIGINT"));
289
311
  process.once("SIGTERM", () => shutdown("SIGTERM"));
@@ -307,7 +329,7 @@ async function main() {
307
329
  }
308
330
  finally {
309
331
  clearInterval(pollHandle);
310
- abortAutoSync();
332
+ await stopAutoSync();
311
333
  process.stderr.write("[floom] stdin closed, stopping sync poller\n");
312
334
  }
313
335
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom-mcp-sync",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "Lightweight Floom MCP server for installing, publishing, and startup-syncing skills.",
5
5
  "license": "MIT",
6
6
  "type": "module",