@floomhq/floom 1.0.39 → 1.0.41
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/push-watch.js +1 -24
- package/dist/sync-manifest.js +36 -14
- package/package.json +1 -1
package/dist/push-watch.js
CHANGED
|
@@ -156,30 +156,7 @@ export async function pushWatchOnce(opts) {
|
|
|
156
156
|
const pushed = pushManifest.files[pushKey];
|
|
157
157
|
if (pushed?.hash === hash) {
|
|
158
158
|
if (!isUnchangedSyncedPackage(root, skillPackage, syncManifest)) {
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
await publishSkillPath({ file: packagePath, update: true, updateSlug: pushed.slug, quiet: true });
|
|
162
|
-
updated += 1;
|
|
163
|
-
}
|
|
164
|
-
catch (err) {
|
|
165
|
-
if (err instanceof Error && /Skill not found/i.test(err.message)) {
|
|
166
|
-
const result = await publishSkillPath({ file: packagePath, visibility: "unlisted", quiet: true });
|
|
167
|
-
published += 1;
|
|
168
|
-
pushManifest.files[pushKey] = { hash, slug: result.data.slug, path: key, pushedAt: new Date().toISOString() };
|
|
169
|
-
markPackageSynced(root, skillPackage, syncManifest, result.data.slug);
|
|
170
|
-
await writeSyncManifest(syncManifest);
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
skipped += 1;
|
|
174
|
-
if (!opts.quiet) {
|
|
175
|
-
process.stderr.write(`[floom] skipped ${packagePath}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
176
|
-
}
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
adopted += 1;
|
|
182
|
-
}
|
|
159
|
+
adopted += 1;
|
|
183
160
|
markPackageSynced(root, skillPackage, syncManifest, pushed.slug);
|
|
184
161
|
await writeSyncManifest(syncManifest);
|
|
185
162
|
}
|
package/dist/sync-manifest.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { lstat, mkdir, open, rename, rm, stat } from "node:fs/promises";
|
|
3
|
-
import {
|
|
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}$/;
|
|
@@ -55,6 +54,24 @@ const ROOT_SUPPORT_FILES = new Set([
|
|
|
55
54
|
function emptyManifest() {
|
|
56
55
|
return { version: MANIFEST_VERSION, files: {} };
|
|
57
56
|
}
|
|
57
|
+
function expandHome(path) {
|
|
58
|
+
if (path === "~")
|
|
59
|
+
return homedir();
|
|
60
|
+
if (path.startsWith("~/"))
|
|
61
|
+
return join(homedir(), path.slice(2));
|
|
62
|
+
return path;
|
|
63
|
+
}
|
|
64
|
+
export function syncManifestPath() {
|
|
65
|
+
if (process.env.FLOOM_SYNC_MANIFEST_PATH)
|
|
66
|
+
return expandHome(process.env.FLOOM_SYNC_MANIFEST_PATH);
|
|
67
|
+
return join(CONFIG_DIR, "sync-manifest.json");
|
|
68
|
+
}
|
|
69
|
+
function syncManifestDir() {
|
|
70
|
+
return dirname(syncManifestPath());
|
|
71
|
+
}
|
|
72
|
+
function syncLockPath() {
|
|
73
|
+
return join(syncManifestDir(), "sync.lock");
|
|
74
|
+
}
|
|
58
75
|
function isEntryForKey(key, value) {
|
|
59
76
|
if (!value || typeof value !== "object")
|
|
60
77
|
return false;
|
|
@@ -88,7 +105,7 @@ function isPackageFilePath(packagePath) {
|
|
|
88
105
|
export async function readSyncManifest() {
|
|
89
106
|
try {
|
|
90
107
|
await ensureSyncManifestDir();
|
|
91
|
-
const handle = await open(
|
|
108
|
+
const handle = await open(syncManifestPath(), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
92
109
|
let body;
|
|
93
110
|
try {
|
|
94
111
|
body = await handle.readFile("utf8");
|
|
@@ -117,20 +134,23 @@ export async function readSyncManifest() {
|
|
|
117
134
|
}
|
|
118
135
|
export async function writeSyncManifest(manifest) {
|
|
119
136
|
await ensureSyncManifestDir();
|
|
120
|
-
const
|
|
121
|
-
const
|
|
137
|
+
const path = syncManifestPath();
|
|
138
|
+
const dirPath = dirname(path);
|
|
139
|
+
const targetName = basename(path);
|
|
140
|
+
const dir = await open(dirPath, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
141
|
+
const tmpBase = `${targetName}.${process.pid}.${Date.now()}`;
|
|
122
142
|
const body = JSON.stringify(manifest, null, 2);
|
|
123
143
|
try {
|
|
124
144
|
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
125
145
|
const tmpName = attempt === 0 ? `${tmpBase}.tmp` : `${tmpBase}.${attempt}.tmp`;
|
|
126
|
-
const tmpPath = childPath(dir,
|
|
146
|
+
const tmpPath = childPath(dir, dirPath, tmpName);
|
|
127
147
|
let handle = null;
|
|
128
148
|
try {
|
|
129
149
|
handle = await open(tmpPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
130
150
|
await handle.writeFile(body, "utf8");
|
|
131
151
|
await handle.close();
|
|
132
152
|
handle = null;
|
|
133
|
-
await rename(tmpPath, childPath(dir,
|
|
153
|
+
await rename(tmpPath, childPath(dir, dirPath, targetName));
|
|
134
154
|
return;
|
|
135
155
|
}
|
|
136
156
|
catch (err) {
|
|
@@ -154,8 +174,9 @@ function childPath(parent, fallbackParent, name) {
|
|
|
154
174
|
return join(resolve(fallbackParent), name);
|
|
155
175
|
}
|
|
156
176
|
export async function ensureSyncManifestDir() {
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
const dir = syncManifestDir();
|
|
178
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
179
|
+
const stat = await lstat(dir);
|
|
159
180
|
if (stat.isSymbolicLink()) {
|
|
160
181
|
const err = new Error("sync manifest directory is a symbolic link");
|
|
161
182
|
err.code = "ELOOP";
|
|
@@ -169,19 +190,20 @@ export async function ensureSyncManifestDir() {
|
|
|
169
190
|
}
|
|
170
191
|
export async function withSyncLock(fn) {
|
|
171
192
|
await ensureSyncManifestDir();
|
|
193
|
+
const lockPath = syncLockPath();
|
|
172
194
|
const startedAt = Date.now();
|
|
173
195
|
for (;;) {
|
|
174
196
|
try {
|
|
175
|
-
await mkdir(
|
|
197
|
+
await mkdir(lockPath, { mode: 0o700 });
|
|
176
198
|
break;
|
|
177
199
|
}
|
|
178
200
|
catch (err) {
|
|
179
201
|
if (err.code !== "EEXIST")
|
|
180
202
|
throw err;
|
|
181
203
|
try {
|
|
182
|
-
const lockStat = await stat(
|
|
204
|
+
const lockStat = await stat(lockPath);
|
|
183
205
|
if (Date.now() - lockStat.mtimeMs > LOCK_STALE_MS) {
|
|
184
|
-
await rm(
|
|
206
|
+
await rm(lockPath, { recursive: true, force: true });
|
|
185
207
|
continue;
|
|
186
208
|
}
|
|
187
209
|
}
|
|
@@ -200,7 +222,7 @@ export async function withSyncLock(fn) {
|
|
|
200
222
|
return await fn();
|
|
201
223
|
}
|
|
202
224
|
finally {
|
|
203
|
-
await rm(
|
|
225
|
+
await rm(lockPath, { recursive: true, force: true }).catch(() => { });
|
|
204
226
|
}
|
|
205
227
|
}
|
|
206
228
|
export function manifestKey(root, target) {
|