@floomhq/floom 1.0.40 → 1.0.42

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.
@@ -1,60 +1,119 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
3
- import { join, relative, resolve, sep } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
5
  import { CONFIG_DIR } from "./config.js";
5
6
  const MANIFEST_VERSION = 1;
6
- const MANIFEST_PATH = join(CONFIG_DIR, "sync-manifest.json");
7
- const LOCK_PATH = join(CONFIG_DIR, "sync.lock");
8
7
  const LOCK_TIMEOUT_MS = 60_000;
9
8
  const LOCK_STALE_MS = 5 * 60_000;
10
9
  const SLUG_RE = /^[A-Za-z0-9_-]{1,128}$/;
11
10
  const FD_PATH_ROOT = "/proc/self/fd";
12
11
  const SUPPORT_DIRS = new Set([
12
+ ".github",
13
13
  "agents",
14
14
  "assets",
15
+ "bin",
15
16
  "canvas-fonts",
16
17
  "checks",
18
+ "claude",
17
19
  "codex",
20
+ "contrib",
18
21
  "core",
19
22
  "cursor",
23
+ "design",
24
+ "docs",
20
25
  "evidence",
21
26
  "examples",
27
+ "extension",
22
28
  "helpers",
29
+ "hosts",
23
30
  "kimi",
31
+ "lib",
32
+ "migrations",
33
+ "model-overlays",
34
+ "openclaw",
24
35
  "opencode",
25
36
  "reference",
26
37
  "references",
38
+ "remotion-starter",
27
39
  "scripts",
28
40
  "sdk",
29
41
  "schemas",
42
+ "src",
43
+ "specialists",
44
+ "supabase",
30
45
  "templates",
46
+ "test",
31
47
  "tests",
32
48
  "themes",
49
+ "vendor",
33
50
  ]);
34
51
  const ROOT_SUPPORT_FILES = new Set([
35
52
  ".env.example",
36
53
  "ACKNOWLEDGEMENTS.md",
54
+ "AGENTS.md",
55
+ "ARCHITECTURE.md",
56
+ "BROWSER.md",
37
57
  "CHANGELOG.md",
58
+ "CLAUDE.md",
59
+ "CONTRIBUTING.md",
60
+ "DESIGN.md",
61
+ "ETHOS.md",
38
62
  "LICENSE",
39
63
  "LICENSE.md",
40
64
  "LICENSE.txt",
65
+ "PLAN-snapshot-dropdown-interactive.md",
41
66
  "README.md",
67
+ "REMOTION_PROTOCOL.md",
42
68
  "SKILL.md.tmpl",
69
+ "TODOS.md",
70
+ "TODOS-format.md",
71
+ "USING_GBRAIN_WITH_GSTACK.md",
72
+ "VERSION",
73
+ ".gitlab-ci.yml",
74
+ "actionlint.yaml",
75
+ "bun.lock",
76
+ "checklist.md",
77
+ "conductor.json",
78
+ "design-checklist.md",
79
+ "dx-hall-of-fame.md",
43
80
  "editing.md",
44
81
  "forms.md",
82
+ "greptile-triage.md",
45
83
  "instructions.md",
46
84
  "nanobanana.py",
85
+ "package.json",
47
86
  "plan.md",
48
87
  "pptxgenjs.md",
49
88
  "reference.md",
50
89
  "requirements.txt",
90
+ "setup",
51
91
  "skill.md",
92
+ "slop-scan.config.json",
52
93
  "style_guidelines.md",
53
94
  "theme-showcase.pdf",
54
95
  ]);
55
96
  function emptyManifest() {
56
97
  return { version: MANIFEST_VERSION, files: {} };
57
98
  }
99
+ function expandHome(path) {
100
+ if (path === "~")
101
+ return homedir();
102
+ if (path.startsWith("~/"))
103
+ return join(homedir(), path.slice(2));
104
+ return path;
105
+ }
106
+ export function syncManifestPath() {
107
+ if (process.env.FLOOM_SYNC_MANIFEST_PATH)
108
+ return expandHome(process.env.FLOOM_SYNC_MANIFEST_PATH);
109
+ return join(CONFIG_DIR, "sync-manifest.json");
110
+ }
111
+ function syncManifestDir() {
112
+ return dirname(syncManifestPath());
113
+ }
114
+ function syncLockPath() {
115
+ return join(syncManifestDir(), "sync.lock");
116
+ }
58
117
  function isEntryForKey(key, value) {
59
118
  if (!value || typeof value !== "object")
60
119
  return false;
@@ -88,7 +147,7 @@ function isPackageFilePath(packagePath) {
88
147
  export async function readSyncManifest() {
89
148
  try {
90
149
  await ensureSyncManifestDir();
91
- const handle = await open(MANIFEST_PATH, constants.O_RDONLY | constants.O_NOFOLLOW);
150
+ const handle = await open(syncManifestPath(), constants.O_RDONLY | constants.O_NOFOLLOW);
92
151
  let body;
93
152
  try {
94
153
  body = await handle.readFile("utf8");
@@ -117,20 +176,23 @@ export async function readSyncManifest() {
117
176
  }
118
177
  export async function writeSyncManifest(manifest) {
119
178
  await ensureSyncManifestDir();
120
- const dir = await open(CONFIG_DIR, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
121
- const tmpBase = `sync-manifest.json.${process.pid}.${Date.now()}`;
179
+ const path = syncManifestPath();
180
+ const dirPath = dirname(path);
181
+ const targetName = basename(path);
182
+ const dir = await open(dirPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
183
+ const tmpBase = `${targetName}.${process.pid}.${Date.now()}`;
122
184
  const body = JSON.stringify(manifest, null, 2);
123
185
  try {
124
186
  for (let attempt = 0; attempt < 10; attempt += 1) {
125
187
  const tmpName = attempt === 0 ? `${tmpBase}.tmp` : `${tmpBase}.${attempt}.tmp`;
126
- const tmpPath = childPath(dir, CONFIG_DIR, tmpName);
188
+ const tmpPath = childPath(dir, dirPath, tmpName);
127
189
  let handle = null;
128
190
  try {
129
191
  handle = await open(tmpPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
130
192
  await handle.writeFile(body, "utf8");
131
193
  await handle.close();
132
194
  handle = null;
133
- await rename(tmpPath, childPath(dir, CONFIG_DIR, "sync-manifest.json"));
195
+ await rename(tmpPath, childPath(dir, dirPath, targetName));
134
196
  return;
135
197
  }
136
198
  catch (err) {
@@ -154,8 +216,9 @@ function childPath(parent, fallbackParent, name) {
154
216
  return join(resolve(fallbackParent), name);
155
217
  }
156
218
  export async function ensureSyncManifestDir() {
157
- await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
158
- const stat = await lstat(CONFIG_DIR);
219
+ const dir = syncManifestDir();
220
+ await mkdir(dir, { recursive: true, mode: 0o700 });
221
+ const stat = await lstat(dir);
159
222
  if (stat.isSymbolicLink()) {
160
223
  const err = new Error("sync manifest directory is a symbolic link");
161
224
  err.code = "ELOOP";
@@ -169,19 +232,20 @@ export async function ensureSyncManifestDir() {
169
232
  }
170
233
  export async function withSyncLock(fn) {
171
234
  await ensureSyncManifestDir();
235
+ const lockPath = syncLockPath();
172
236
  const startedAt = Date.now();
173
237
  for (;;) {
174
238
  try {
175
- await mkdir(LOCK_PATH, { mode: 0o700 });
239
+ await mkdir(lockPath, { mode: 0o700 });
176
240
  break;
177
241
  }
178
242
  catch (err) {
179
243
  if (err.code !== "EEXIST")
180
244
  throw err;
181
245
  try {
182
- const lockStat = await stat(LOCK_PATH);
246
+ const lockStat = await stat(lockPath);
183
247
  if (Date.now() - lockStat.mtimeMs > LOCK_STALE_MS) {
184
- await rm(LOCK_PATH, { recursive: true, force: true });
248
+ await rm(lockPath, { recursive: true, force: true });
185
249
  continue;
186
250
  }
187
251
  }
@@ -200,7 +264,7 @@ export async function withSyncLock(fn) {
200
264
  return await fn();
201
265
  }
202
266
  finally {
203
- await rm(LOCK_PATH, { recursive: true, force: true }).catch(() => { });
267
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
204
268
  }
205
269
  }
206
270
  export function manifestKey(root, target) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "description": "Sync AI skills across agents and machines.",
5
5
  "license": "MIT",
6
6
  "type": "module",