@floomhq/floom 1.0.43 → 1.0.45

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/cli.js CHANGED
@@ -105,7 +105,7 @@ function commandUsage() {
105
105
  ${c.dim("Advanced")}
106
106
  ${c.cyan("library")} Create, browse, and subscribe to libraries
107
107
  ${c.dim("Alias: lib")}
108
- ${c.cyan("move")} ${c.dim("<slug> --folder <path>")} Place a saved skill in a local folder
108
+ ${c.cyan("move")} ${c.dim("<slug> --folder <path>")} Place a saved skill in a relative folder
109
109
  ${c.cyan("mcp")} Print optional MCP setup guidance
110
110
  ${c.cyan("sync")} Preview pull of published, saved, and library skills
111
111
  ${c.dim(`Flags: --target ${TARGET_HINT} (default: claude)`)}
@@ -163,13 +163,13 @@ function moveUsage() {
163
163
  process.stdout.write(`
164
164
  ${c.bold("Usage:")} ${c.cyan(`${CLI_COMMAND} move`)} ${c.dim("<slug> --folder <path> [--tag <tag>]")}
165
165
 
166
- ${c.bold("Place a saved or subscribed skill in a local folder.")}
166
+ ${c.bold("Place a saved or subscribed skill in a portable library folder.")}
167
167
  ${c.cyan(`${CLI_COMMAND} move support-tone --folder support/tone`)}
168
168
  ${c.cyan(`${CLI_COMMAND} move support-tone --root`)}
169
169
  ${c.cyan(`${CLI_COMMAND} move support-tone --folder support --tags support,tone`)}
170
170
 
171
171
  ${c.bold("Flags")}
172
- ${c.cyan("--folder <path>")} Folder path for synced installs
172
+ ${c.cyan("--folder <path>")} Relative folder path for synced installs
173
173
  ${c.cyan("--root")} Put the skill at the root
174
174
  ${c.cyan("--tag <tag>")} Add one tag, repeatable
175
175
  ${c.cyan("--tags a,b")} Add comma-separated tags
@@ -589,7 +589,20 @@ function parseDoctorFlags(argv) {
589
589
  return out;
590
590
  }
591
591
  function normalizeFolder(value) {
592
- return value === "root" || value === "/" || value === "." ? null : value;
592
+ const normalized = value.trim().replace(/\\/g, "/").replace(/\/+/g, "/");
593
+ if (normalized === "root" || normalized === "/" || normalized === ".")
594
+ return null;
595
+ if (normalized.startsWith("/")) {
596
+ throw new FloomError("Invalid --folder: use a relative sync folder.", "Floom folders are portable library paths like `support/tone`, not absolute filesystem paths like `/tmp/floom-move-target`.");
597
+ }
598
+ if (normalized === ".." ||
599
+ normalized.startsWith("../") ||
600
+ normalized.includes("/../") ||
601
+ normalized.endsWith("/..")) {
602
+ throw new FloomError("Invalid --folder: path traversal is not allowed.", "Use a relative sync folder like `support/tone`, or use `--root`.");
603
+ }
604
+ const relative = normalized.startsWith("./") ? normalized.slice(2) : normalized;
605
+ return relative || null;
593
606
  }
594
607
  function parseFolderTagFlags(argv) {
595
608
  const out = { tags: [], rest: [] };
package/dist/login.js CHANGED
@@ -107,6 +107,31 @@ function waitForCallback(port) {
107
107
  res.end();
108
108
  return;
109
109
  }
110
+ if (req.method === "GET" && req.url?.startsWith("/cli-callback?")) {
111
+ try {
112
+ const data = parseCallbackQuery(req.url);
113
+ if (!data.access_token || !data.refresh_token) {
114
+ res.writeHead(400, {
115
+ "content-type": "text/html; charset=utf-8",
116
+ });
117
+ res.end(localCallbackPage("Missing tokens from OAuth response."));
118
+ return;
119
+ }
120
+ res.writeHead(200, {
121
+ "content-type": "text/html; charset=utf-8",
122
+ "referrer-policy": "no-referrer",
123
+ });
124
+ res.end(localCallbackPage("Signed in. You can close this tab."));
125
+ settled = true;
126
+ cleanup();
127
+ resolve(data);
128
+ }
129
+ catch {
130
+ res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
131
+ res.end(localCallbackPage("Invalid OAuth response."));
132
+ }
133
+ return;
134
+ }
110
135
  if (req.method === "POST" && req.url === "/cli-callback") {
111
136
  let body = "";
112
137
  req.on("data", (chunk) => { body += chunk.toString("utf8"); });
@@ -170,6 +195,16 @@ function waitForCallback(port) {
170
195
  });
171
196
  });
172
197
  }
198
+ function parseCallbackQuery(url) {
199
+ const parsed = new URL(url, "http://127.0.0.1");
200
+ const result = {};
201
+ for (const key of ["access_token", "refresh_token", "expires_in", "token_type"]) {
202
+ const value = parsed.searchParams.get(key);
203
+ if (value)
204
+ result[key] = value;
205
+ }
206
+ return result;
207
+ }
173
208
  function parseCallbackBody(body, contentType) {
174
209
  const type = Array.isArray(contentType) ? contentType.join(";") : contentType ?? "";
175
210
  if (type.includes("application/x-www-form-urlencoded")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "description": "Sync AI skills across agents and machines.",
5
5
  "license": "MIT",
6
6
  "type": "module",