@floomhq/floom 1.0.51 → 1.0.52

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.
Files changed (2) hide show
  1. package/dist/status.js +53 -38
  2. package/package.json +1 -1
package/dist/status.js CHANGED
@@ -2,10 +2,10 @@ import { constants } from "node:fs";
2
2
  import { open, readdir } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { CONFIG_DIR, readConfig, resolveApiUrl } from "./config.js";
5
- import { getJson } from "./lib/api.js";
5
+ import { floomFetch } from "./lib/api.js";
6
6
  import { AGENT_TARGETS, targetSkillsDir } from "./targets.js";
7
7
  import { c, symbols } from "./ui.js";
8
- const MAX_PACKAGE_SCAN_DEPTH = 8;
8
+ const STATUS_CLOUD_TIMEOUT_MS = 8_000;
9
9
  async function readJsonFile(path) {
10
10
  try {
11
11
  const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
@@ -23,30 +23,32 @@ async function readJsonFile(path) {
23
23
  throw err;
24
24
  }
25
25
  }
26
- async function countSkillPackages(root) {
26
+ async function countDirectSkillPackages(root) {
27
27
  let count = 0;
28
- async function walk(dir, depth) {
29
- if (depth > MAX_PACKAGE_SCAN_DEPTH)
30
- return;
31
- let entries;
28
+ let entries;
29
+ try {
30
+ entries = await readdir(root, { withFileTypes: true });
31
+ }
32
+ catch (err) {
33
+ if (err.code === "ENOENT")
34
+ return 0;
35
+ throw err;
36
+ }
37
+ if (entries.some((entry) => entry.isFile() && entry.name === "SKILL.md"))
38
+ count += 1;
39
+ for (const entry of entries) {
40
+ if (!entry.isDirectory() || entry.name.startsWith("."))
41
+ continue;
32
42
  try {
33
- entries = await readdir(dir, { withFileTypes: true });
43
+ const child = await readdir(join(root, entry.name), { withFileTypes: true });
44
+ if (child.some((item) => item.isFile() && item.name === "SKILL.md"))
45
+ count += 1;
34
46
  }
35
47
  catch (err) {
36
- if (err.code === "ENOENT")
37
- return;
38
- throw err;
39
- }
40
- if (entries.some((entry) => entry.isFile() && entry.name === "SKILL.md")) {
41
- count += 1;
42
- }
43
- for (const entry of entries) {
44
- if (!entry.isDirectory() || entry.name.startsWith("."))
45
- continue;
46
- await walk(join(dir, entry.name), depth + 1);
48
+ if (err.code !== "ENOENT")
49
+ throw err;
47
50
  }
48
51
  }
49
- await walk(root, 0);
50
52
  return count;
51
53
  }
52
54
  function manifestCounts(manifest) {
@@ -61,24 +63,31 @@ async function countCloudVisible() {
61
63
  if (!cfg)
62
64
  return { signedIn: false, total: null };
63
65
  const apiUrl = resolveApiUrl(cfg);
64
- let cursor;
65
- const seenCursors = new Set();
66
- let total = 0;
67
- for (let page = 0; page < 1000; page += 1) {
66
+ try {
68
67
  const url = new URL(`${apiUrl}/api/v1/me/skills`);
69
68
  url.searchParams.set("limit", "100");
70
- if (cursor)
71
- url.searchParams.set("cursor", cursor);
72
- const payload = await getJson(url.toString(), "load your skills", cfg.accessToken);
73
- total += Array.isArray(payload.skills) ? payload.skills.length : 0;
74
- if (!payload.next_cursor)
75
- return { signedIn: true, total };
76
- if (seenCursors.has(payload.next_cursor))
77
- return { signedIn: true, total };
78
- seenCursors.add(payload.next_cursor);
79
- cursor = payload.next_cursor;
69
+ const res = await floomFetch(url.toString(), "load your skills", {
70
+ token: cfg.accessToken,
71
+ timeoutMs: STATUS_CLOUD_TIMEOUT_MS,
72
+ rateLimitRetries: 0,
73
+ });
74
+ const payload = (await res.json());
75
+ const sampleCount = Array.isArray(payload.skills) ? payload.skills.length : 0;
76
+ const hasMore = Boolean(payload.next_cursor);
77
+ return {
78
+ signedIn: true,
79
+ total: hasMore ? null : sampleCount,
80
+ sampleCount,
81
+ hasMore,
82
+ };
83
+ }
84
+ catch (err) {
85
+ return {
86
+ signedIn: true,
87
+ total: null,
88
+ error: err instanceof Error ? err.message : String(err),
89
+ };
80
90
  }
81
- return { signedIn: true, total };
82
91
  }
83
92
  async function buildStatus() {
84
93
  const cloud = await countCloudVisible();
@@ -91,11 +100,11 @@ async function buildStatus() {
91
100
  const cacheManifest = manifestCounts(await readJsonFile(join(cacheRoot, ".floom-cli-sync-manifest.json")));
92
101
  const nativeManifest = manifestCounts(await readJsonFile(join(CONFIG_DIR, "native-sync-manifests", `${target}.json`)));
93
102
  targets[target] = {
94
- cache_packages: await countSkillPackages(cacheRoot),
103
+ cache_packages: cacheManifest.skills || await countDirectSkillPackages(cacheRoot),
95
104
  cache_manifest_files: cacheManifest.files,
96
105
  cache_manifest_skills: cacheManifest.skills,
97
106
  native_root: nativeRoot,
98
- native_packages: await countSkillPackages(nativeRoot),
107
+ native_packages: nativeManifest.skills || await countDirectSkillPackages(nativeRoot),
99
108
  native_manifest_files: nativeManifest.files,
100
109
  native_manifest_skills: nativeManifest.skills,
101
110
  ...(daemonRaw?.last_run?.[target] ? { daemon: daemonRaw.last_run[target] } : {}),
@@ -105,6 +114,9 @@ async function buildStatus() {
105
114
  cloud: {
106
115
  signed_in: cloud.signedIn,
107
116
  visible_total: cloud.total,
117
+ ...(cloud.sampleCount !== undefined ? { visible_sample_count: cloud.sampleCount } : {}),
118
+ ...(cloud.hasMore !== undefined ? { visible_has_more: cloud.hasMore } : {}),
119
+ ...(cloud.error ? { error: cloud.error } : {}),
108
120
  },
109
121
  daemon: {
110
122
  running: daemonRunning,
@@ -149,7 +161,10 @@ export async function status(opts) {
149
161
  }
150
162
  process.stdout.write(`\n${symbols.dot} ${c.bold("Floom status")}\n\n`);
151
163
  process.stdout.write(` ${c.dim("cloud signed in:")} ${payload.cloud.signed_in ? "yes" : "no"}\n`);
152
- process.stdout.write(` ${c.dim("cloud visible skills:")} ${payload.cloud.visible_total ?? "unknown"}\n`);
164
+ const cloudVisible = payload.cloud.visible_total ?? (payload.cloud.visible_has_more ? `${payload.cloud.visible_sample_count ?? "?"}+` : "unknown");
165
+ process.stdout.write(` ${c.dim("cloud visible skills:")} ${cloudVisible}\n`);
166
+ if (payload.cloud.error)
167
+ process.stdout.write(` ${c.dim("cloud status:")} ${payload.cloud.error}\n`);
153
168
  process.stdout.write(` ${c.dim("daemon:")} ${payload.daemon.running ? "running" : "not running"}${payload.daemon.version ? ` (${payload.daemon.version})` : ""}\n`);
154
169
  if (payload.daemon.last_completed_at)
155
170
  process.stdout.write(` ${c.dim("last completed:")} ${payload.daemon.last_completed_at}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom",
3
- "version": "1.0.51",
3
+ "version": "1.0.52",
4
4
  "description": "Sync AI skills across agents and machines.",
5
5
  "license": "MIT",
6
6
  "type": "module",