@floomhq/floom 1.0.63 → 1.0.64
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/README.md +3 -2
- package/dist/launch.js +31 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,7 +83,8 @@ version: 0.1.0
|
|
|
83
83
|
|
|
84
84
|
Override the API host with `FLOOM_API_URL` (defaults to `https://floom.dev`).
|
|
85
85
|
|
|
86
|
-
`floom sync`, `floom watch`, and `floom daemon` handle published, saved, and subscribed library skills.
|
|
87
|
-
|
|
86
|
+
`floom sync`, `floom watch`, and `floom daemon` handle published, saved, and subscribed library skills. Direct `floom sync --target <agent>` writes to that target's native skill root for compatibility. The always-on daemon writes pulled cloud packages into `~/.floom/skill-cache/<target>/` and keeps native harness roots as the user-editable projection/push surface.
|
|
87
|
+
|
|
88
|
+
Sync manifests are target-scoped. Native projections are tracked under `~/.floom/native-sync-manifests/`; daemon cache roots keep their own `.floom-cli-sync-manifest.json`. The manifests record hashes for files Floom previously wrote. Sync writes missing files and applies cloud updates when the local file still matches the last Floom-tracked hash.
|
|
88
89
|
Existing untracked files and locally edited tracked files are preserved as conflicts.
|
|
89
90
|
Symlinks are never followed. To replace a local skill manually, run `floom add <url-or-slug> --force`.
|
package/dist/launch.js
CHANGED
|
@@ -38,11 +38,14 @@ export async function launchGate(opts) {
|
|
|
38
38
|
]);
|
|
39
39
|
const targetResults = daemon?.last_run ?? {};
|
|
40
40
|
const targets = daemon?.targets ?? [];
|
|
41
|
+
const daemonLive = daemonIsLive(daemon);
|
|
41
42
|
const daemonTargetsOk = targets.length > 0 && targets.every((target) => targetResults[target]?.ok === true);
|
|
43
|
+
const conflictTargets = targets.filter((target) => (targetResults[target]?.conflicts ?? 0) > 0);
|
|
44
|
+
const daemonTargetsClean = daemonTargetsOk && conflictTargets.length === 0;
|
|
42
45
|
const releaseAligned = health?.version === CLI_VERSION && cliVersion?.latest === CLI_VERSION;
|
|
43
|
-
const daemonAligned =
|
|
46
|
+
const daemonAligned = daemonLive && daemon?.version === CLI_VERSION;
|
|
44
47
|
const payload = {
|
|
45
|
-
ok: Boolean(health?.ok && releaseAligned && daemonAligned &&
|
|
48
|
+
ok: Boolean(health?.ok && releaseAligned && daemonAligned && daemonTargetsClean),
|
|
46
49
|
release: {
|
|
47
50
|
cli: CLI_VERSION,
|
|
48
51
|
web: health?.version ?? null,
|
|
@@ -52,18 +55,23 @@ export async function launchGate(opts) {
|
|
|
52
55
|
},
|
|
53
56
|
health: health ?? null,
|
|
54
57
|
daemon: daemon ? {
|
|
55
|
-
running:
|
|
58
|
+
running: daemonLive,
|
|
56
59
|
version: daemon.version ?? null,
|
|
57
60
|
hostname: daemon.hostname ?? null,
|
|
58
61
|
targets,
|
|
59
62
|
all_targets_ok: daemonTargetsOk,
|
|
63
|
+
all_targets_clean: daemonTargetsClean,
|
|
64
|
+
conflict_targets: conflictTargets,
|
|
60
65
|
last_completed_at: daemon.last_completed_at ?? null,
|
|
66
|
+
next_run_at: daemon.next_run_at ?? null,
|
|
61
67
|
last_run: targetResults,
|
|
62
68
|
} : null,
|
|
63
69
|
escalations: [
|
|
64
70
|
...(releaseAligned ? [] : ["CLI, web health, and server latest versions are not aligned."]),
|
|
65
|
-
...(
|
|
71
|
+
...(daemonLive ? [] : ["Daemon status is stale or the recorded daemon process is not running."]),
|
|
72
|
+
...(daemon?.version === CLI_VERSION ? [] : ["Daemon is not running on the pinned CLI version."]),
|
|
66
73
|
...(targets.length === 0 || daemonTargetsOk ? [] : ["Latest daemon cycle has not yet passed for every configured target."]),
|
|
74
|
+
...(conflictTargets.length === 0 ? [] : [`Latest daemon cycle has unresolved conflicts for: ${conflictTargets.join(", ")}.`]),
|
|
67
75
|
],
|
|
68
76
|
};
|
|
69
77
|
if (opts.json) {
|
|
@@ -81,3 +89,22 @@ export async function launchGate(opts) {
|
|
|
81
89
|
}
|
|
82
90
|
process.stdout.write(`\n${symbols.ok} Launch gate passed for the pinned local release identity.\n\n`);
|
|
83
91
|
}
|
|
92
|
+
function daemonIsLive(status) {
|
|
93
|
+
if (!status?.running)
|
|
94
|
+
return false;
|
|
95
|
+
if (!status.pid || status.pid < 1)
|
|
96
|
+
return false;
|
|
97
|
+
try {
|
|
98
|
+
process.kill(status.pid, 0);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
if (!status.next_run_at || !status.interval_seconds)
|
|
104
|
+
return true;
|
|
105
|
+
const nextRun = Date.parse(status.next_run_at);
|
|
106
|
+
if (!Number.isFinite(nextRun))
|
|
107
|
+
return false;
|
|
108
|
+
const graceMs = Math.max(status.interval_seconds * 2000, 120_000);
|
|
109
|
+
return nextRun + graceMs >= Date.now();
|
|
110
|
+
}
|