@floomhq/skills 1.1.0 → 1.1.1
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/index.js +73 -12
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4377,7 +4377,7 @@ async function getMachineIdentity() {
|
|
|
4377
4377
|
}
|
|
4378
4378
|
|
|
4379
4379
|
// src/version.ts
|
|
4380
|
-
var VERSION = "1.1.
|
|
4380
|
+
var VERSION = "1.1.1";
|
|
4381
4381
|
|
|
4382
4382
|
// src/api-client.ts
|
|
4383
4383
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
@@ -7702,19 +7702,24 @@ async function renameDeviceCommand(newLabel) {
|
|
|
7702
7702
|
|
|
7703
7703
|
// src/commands/install.ts
|
|
7704
7704
|
init_src();
|
|
7705
|
-
import { mkdir as mkdir6, rm as rm3, writeFile as writeFile5, stat as stat7, readFile as readFile11 } from "node:fs/promises";
|
|
7705
|
+
import { mkdir as mkdir6, rm as rm3, writeFile as writeFile5, stat as stat7, lstat as lstat4, readFile as readFile11 } from "node:fs/promises";
|
|
7706
7706
|
import { dirname as dirname2, join as join12, resolve as resolve8 } from "node:path";
|
|
7707
|
+
import { z as z7 } from "zod";
|
|
7707
7708
|
init_runtime();
|
|
7708
7709
|
init_prompt();
|
|
7709
7710
|
var TOKEN_RE = /^fls_[A-Za-z0-9_-]{32,}$/;
|
|
7710
7711
|
var SAFE_SLUG_RE2 = /^(?=.{1,80}$)[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
7711
7712
|
var MAX_DECODED_FILE_BYTES2 = 2 * 1024 * 1024;
|
|
7713
|
+
var TRUSTED_CATALOG_HOSTS = /* @__PURE__ */ new Set(["skills.floom.dev", "cyborg.floom.dev"]);
|
|
7714
|
+
var CATALOG_LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost"]);
|
|
7712
7715
|
function trustedCatalogOrigin(url) {
|
|
7713
|
-
if (url.protocol === "https:"
|
|
7716
|
+
if (TRUSTED_CATALOG_HOSTS.has(url.hostname)) return url.protocol === "https:";
|
|
7714
7717
|
const dev = process.env.FLOOM_DEV;
|
|
7715
7718
|
const devMode = dev === "1" || dev === "true";
|
|
7716
|
-
|
|
7719
|
+
if (!devMode || !CATALOG_LOOPBACK_HOSTS.has(url.hostname)) return false;
|
|
7720
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
7717
7721
|
}
|
|
7722
|
+
var trustedCatalogHosts = () => [...TRUSTED_CATALOG_HOSTS];
|
|
7718
7723
|
function parseInstallSource(input) {
|
|
7719
7724
|
const trimmed = input.trim();
|
|
7720
7725
|
if (TOKEN_RE.test(trimmed)) return { kind: "share", token: trimmed };
|
|
@@ -7734,6 +7739,34 @@ function parseInstallSource(input) {
|
|
|
7734
7739
|
}
|
|
7735
7740
|
return null;
|
|
7736
7741
|
}
|
|
7742
|
+
function untrustedCatalogHost(input) {
|
|
7743
|
+
let url;
|
|
7744
|
+
try {
|
|
7745
|
+
url = new URL(input.trim());
|
|
7746
|
+
} catch {
|
|
7747
|
+
return null;
|
|
7748
|
+
}
|
|
7749
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
7750
|
+
const last = parts[parts.length - 1];
|
|
7751
|
+
const parent = parts.length >= 2 ? parts[parts.length - 2] : null;
|
|
7752
|
+
if (parent !== "skills" || !last || !SAFE_SLUG_RE2.test(last)) return null;
|
|
7753
|
+
return trustedCatalogOrigin(url) ? null : url.host;
|
|
7754
|
+
}
|
|
7755
|
+
var ShareFileSchema = z7.object({
|
|
7756
|
+
path: z7.string().min(1).max(1024),
|
|
7757
|
+
mode: z7.enum(["text", "non_text"]),
|
|
7758
|
+
content_text: z7.string().nullable().optional().default(null),
|
|
7759
|
+
binary: z7.object({ download_url: z7.string().nullable().optional().default(null) }).passthrough().nullable().optional().default(null)
|
|
7760
|
+
}).passthrough();
|
|
7761
|
+
var PublicShareResponseSchema = z7.object({
|
|
7762
|
+
skill: z7.object({
|
|
7763
|
+
title: z7.string().min(1).max(500),
|
|
7764
|
+
description: z7.string().max(2e4).optional().default(""),
|
|
7765
|
+
owner: z7.object({ name: z7.string().min(1).max(500) }).passthrough(),
|
|
7766
|
+
version: z7.object({ version_seq: z7.number().int().nonnegative(), display: z7.string().min(1).max(100) }).passthrough()
|
|
7767
|
+
}).passthrough(),
|
|
7768
|
+
file_contents: z7.array(ShareFileSchema).max(5e3)
|
|
7769
|
+
}).passthrough();
|
|
7737
7770
|
function safeSkillSlug2(slug) {
|
|
7738
7771
|
if (!slug || !SAFE_SLUG_RE2.test(slug)) throw new Error(`Unsafe shared skill name: ${slug}`);
|
|
7739
7772
|
return slug;
|
|
@@ -7789,21 +7822,43 @@ async function fetchInstallSource(source) {
|
|
|
7789
7822
|
} catch {
|
|
7790
7823
|
throw new Error("Unexpected response from Floom API. Try again in a moment.");
|
|
7791
7824
|
}
|
|
7792
|
-
const
|
|
7793
|
-
if (!
|
|
7825
|
+
const parsed = PublicShareResponseSchema.safeParse(json);
|
|
7826
|
+
if (!parsed.success) {
|
|
7794
7827
|
throw new Error("Unexpected response shape from Floom API.");
|
|
7795
7828
|
}
|
|
7796
|
-
return
|
|
7829
|
+
return parsed.data;
|
|
7797
7830
|
}
|
|
7798
7831
|
var nativePathOperations = { dirname: dirname2 };
|
|
7799
7832
|
function installParentDirectory(destination, pathOperations = nativePathOperations) {
|
|
7800
7833
|
return pathOperations.dirname(destination);
|
|
7801
7834
|
}
|
|
7835
|
+
async function stagingRoot(skillsDir) {
|
|
7836
|
+
let current = skillsDir;
|
|
7837
|
+
for (const segment of [".floom", "tmp"]) {
|
|
7838
|
+
current = join12(current, segment);
|
|
7839
|
+
let entry;
|
|
7840
|
+
try {
|
|
7841
|
+
entry = await lstat4(current);
|
|
7842
|
+
} catch (error) {
|
|
7843
|
+
if (error.code !== "ENOENT") throw error;
|
|
7844
|
+
await mkdir6(current, { recursive: true });
|
|
7845
|
+
continue;
|
|
7846
|
+
}
|
|
7847
|
+
if (entry.isSymbolicLink()) {
|
|
7848
|
+
throw new Error(`Refusing to stage an install through a symlinked ${tildePath(current)}.`);
|
|
7849
|
+
}
|
|
7850
|
+
if (!entry.isDirectory()) {
|
|
7851
|
+
throw new Error(`Refusing to stage an install: ${tildePath(current)} is not a directory.`);
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7854
|
+
return current;
|
|
7855
|
+
}
|
|
7802
7856
|
async function installToDir(target, slug, files) {
|
|
7803
7857
|
const finalDir = join12(target.skillsDir, slug);
|
|
7804
|
-
const
|
|
7805
|
-
const
|
|
7806
|
-
|
|
7858
|
+
const staging = await stagingRoot(target.skillsDir);
|
|
7859
|
+
const tempDir = join12(staging, `${slug}-install-${Date.now()}`);
|
|
7860
|
+
const replacedDir = join12(staging, `${slug}-previous-${Date.now()}`);
|
|
7861
|
+
await mkdir6(tempDir);
|
|
7807
7862
|
try {
|
|
7808
7863
|
for (const file of files) {
|
|
7809
7864
|
const safePath = safeRemotePath2(file.path);
|
|
@@ -7864,9 +7919,15 @@ async function installCommand(input, rawOpts = {}) {
|
|
|
7864
7919
|
const json = flags.json;
|
|
7865
7920
|
const source = parseInstallSource(input);
|
|
7866
7921
|
if (!source) {
|
|
7922
|
+
const untrustedHost = untrustedCatalogHost(input);
|
|
7867
7923
|
if (!json) {
|
|
7868
|
-
|
|
7869
|
-
|
|
7924
|
+
if (untrustedHost) {
|
|
7925
|
+
log.err(`Refusing to install from ${untrustedHost}: it is not a Floom catalog host.`);
|
|
7926
|
+
log.info(`Catalog installs are accepted only from: ${trustedCatalogHosts().join(", ")}`);
|
|
7927
|
+
} else {
|
|
7928
|
+
log.err("That doesn't look like a Floom share link. It must look like: https://skills.floom.dev/s/fls_xxx");
|
|
7929
|
+
log.info(`A public catalog Skill page also works: https://${trustedCatalogHosts()[0]}/skills/<slug>`);
|
|
7930
|
+
}
|
|
7870
7931
|
}
|
|
7871
7932
|
process.exitCode = 2;
|
|
7872
7933
|
return;
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "1.1.
|
|
1
|
+
export const VERSION = "1.1.1";
|