@floomhq/skills 0.2.4 → 0.2.5
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 +78 -8
- package/dist/index.js.map +2 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2362,10 +2362,15 @@ var CONFIG_DIR = join3(homedir2(), ".floom");
|
|
|
2362
2362
|
var AUTH_FILE = join3(CONFIG_DIR, "auth.json");
|
|
2363
2363
|
var DEFAULT_APP_URL = "https://skills.floom.dev";
|
|
2364
2364
|
var DEFAULT_API_URL = "https://skills.floom.dev/api/v1";
|
|
2365
|
+
var LEGACY_API_HOSTS = /* @__PURE__ */ new Set(["floom-v0.vercel.app"]);
|
|
2365
2366
|
async function ensureDir() {
|
|
2366
2367
|
await mkdir2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
2367
2368
|
}
|
|
2368
2369
|
async function readAuth() {
|
|
2370
|
+
const parsed = await readRawAuth();
|
|
2371
|
+
return parsed ? { ...parsed, apiUrl: normalizeApiUrl(parsed.apiUrl) } : null;
|
|
2372
|
+
}
|
|
2373
|
+
async function readRawAuth() {
|
|
2369
2374
|
try {
|
|
2370
2375
|
const raw = await readFile3(AUTH_FILE, "utf8");
|
|
2371
2376
|
return JSON.parse(raw);
|
|
@@ -2376,7 +2381,7 @@ async function readAuth() {
|
|
|
2376
2381
|
}
|
|
2377
2382
|
async function writeAuth(state) {
|
|
2378
2383
|
await ensureDir();
|
|
2379
|
-
await writeFile(AUTH_FILE, JSON.stringify(state, null, 2), { mode: 384 });
|
|
2384
|
+
await writeFile(AUTH_FILE, JSON.stringify({ ...state, apiUrl: normalizeApiUrl(state.apiUrl) }, null, 2), { mode: 384 });
|
|
2380
2385
|
try {
|
|
2381
2386
|
await chmod(AUTH_FILE, 384);
|
|
2382
2387
|
} catch {
|
|
@@ -2399,15 +2404,33 @@ function getAppUrl() {
|
|
|
2399
2404
|
}
|
|
2400
2405
|
function getApiBaseUrls(preferred) {
|
|
2401
2406
|
const explicitApiUrl = process.env.FLOOM_API_URL?.trim();
|
|
2402
|
-
if (explicitApiUrl) return [explicitApiUrl
|
|
2403
|
-
const primary = (preferred ?? getApiUrl())
|
|
2407
|
+
if (explicitApiUrl) return [normalizeApiUrl(explicitApiUrl)];
|
|
2408
|
+
const primary = normalizeApiUrl(preferred ?? getApiUrl());
|
|
2404
2409
|
const bases = [primary];
|
|
2405
2410
|
if (preferred && !process.env.FLOOM_APP_URL) bases.push(DEFAULT_API_URL);
|
|
2406
2411
|
return Array.from(new Set(bases));
|
|
2407
2412
|
}
|
|
2413
|
+
function normalizeApiUrl(apiUrl) {
|
|
2414
|
+
const trimmed = apiUrl.replace(/\/$/, "");
|
|
2415
|
+
try {
|
|
2416
|
+
const url = new URL(trimmed);
|
|
2417
|
+
if (LEGACY_API_HOSTS.has(url.hostname)) return DEFAULT_API_URL;
|
|
2418
|
+
} catch {
|
|
2419
|
+
return DEFAULT_API_URL;
|
|
2420
|
+
}
|
|
2421
|
+
return trimmed;
|
|
2422
|
+
}
|
|
2423
|
+
function isLegacyApiUrl(apiUrl) {
|
|
2424
|
+
if (!apiUrl) return false;
|
|
2425
|
+
try {
|
|
2426
|
+
return LEGACY_API_HOSTS.has(new URL(apiUrl).hostname);
|
|
2427
|
+
} catch {
|
|
2428
|
+
return false;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2408
2431
|
|
|
2409
2432
|
// src/version.ts
|
|
2410
|
-
var VERSION = "0.2.
|
|
2433
|
+
var VERSION = "0.2.5";
|
|
2411
2434
|
|
|
2412
2435
|
// src/api-client.ts
|
|
2413
2436
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
@@ -3520,6 +3543,22 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
|
|
3520
3543
|
function textOf(result) {
|
|
3521
3544
|
return String(result?.content?.[0]?.text ?? "");
|
|
3522
3545
|
}
|
|
3546
|
+
function toolError(result) {
|
|
3547
|
+
const text = textOf(result);
|
|
3548
|
+
if (result?.isError) return text || "tool returned an MCP error";
|
|
3549
|
+
if (/authentication required|forbidden|not found|invalid token/i.test(text)) return text;
|
|
3550
|
+
return null;
|
|
3551
|
+
}
|
|
3552
|
+
function jsonArrayLength(result, key) {
|
|
3553
|
+
if (toolError(result)) return null;
|
|
3554
|
+
try {
|
|
3555
|
+
const parsed = JSON.parse(textOf(result));
|
|
3556
|
+
const value = parsed?.[key];
|
|
3557
|
+
return Array.isArray(value) ? value.length : null;
|
|
3558
|
+
} catch {
|
|
3559
|
+
return null;
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3523
3562
|
function pass(name, detail) {
|
|
3524
3563
|
return { name, ok: true, detail };
|
|
3525
3564
|
}
|
|
@@ -3532,7 +3571,25 @@ function fail(name, detail) {
|
|
|
3532
3571
|
async function doctorCommand(opts = {}) {
|
|
3533
3572
|
if (!opts.freshAgent) {
|
|
3534
3573
|
const auth2 = await readAuth();
|
|
3535
|
-
const
|
|
3574
|
+
const rawAuth = await readRawAuth();
|
|
3575
|
+
let authCheck;
|
|
3576
|
+
const envToken = process.env.FLOOM_API_TOKEN?.trim();
|
|
3577
|
+
if (!auth2?.token && !envToken) {
|
|
3578
|
+
authCheck = fail("auth", "not logged in");
|
|
3579
|
+
} else {
|
|
3580
|
+
try {
|
|
3581
|
+
const me = await api("/me", { authRequired: true });
|
|
3582
|
+
const authSource = envToken ? "FLOOM_API_TOKEN" : "~/.floom/auth.json";
|
|
3583
|
+
authCheck = pass("auth", `@${me.user.handle} via ${authSource}`);
|
|
3584
|
+
} catch (e) {
|
|
3585
|
+
authCheck = fail("auth", `saved login rejected by API: ${e.message}`);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
const checks2 = [
|
|
3589
|
+
pass("cli_version", VERSION),
|
|
3590
|
+
authCheck,
|
|
3591
|
+
process.env.FLOOM_API_URL ? isLegacyApiUrl(process.env.FLOOM_API_URL) ? warn("api_url", `legacy FLOOM_API_URL ${process.env.FLOOM_API_URL}; using ${DEFAULT_API_URL}`) : pass("api_url", process.env.FLOOM_API_URL) : isLegacyApiUrl(rawAuth?.apiUrl) ? warn("auth_api_url", `legacy URL in ~/.floom/auth.json; using ${DEFAULT_API_URL}`) : pass("api_url", auth2?.apiUrl ?? DEFAULT_API_URL)
|
|
3592
|
+
];
|
|
3536
3593
|
emitDoctor(checks2, opts.json);
|
|
3537
3594
|
if (checks2.some((check) => !check.ok)) process.exit(1);
|
|
3538
3595
|
return;
|
|
@@ -3561,7 +3618,7 @@ async function doctorCommand(opts = {}) {
|
|
|
3561
3618
|
HOME: tmpHome,
|
|
3562
3619
|
FLOOM_SKILLS_DIR: tmpSkills,
|
|
3563
3620
|
...token ? { FLOOM_API_TOKEN: token } : {},
|
|
3564
|
-
...auth?.apiUrl ? { FLOOM_API_URL: auth.apiUrl } : {}
|
|
3621
|
+
...process.env.FLOOM_API_URL ? { FLOOM_API_URL: normalizeApiUrl(process.env.FLOOM_API_URL) } : auth?.apiUrl ? { FLOOM_API_URL: auth.apiUrl } : {}
|
|
3565
3622
|
},
|
|
3566
3623
|
stderr: "pipe"
|
|
3567
3624
|
});
|
|
@@ -3579,10 +3636,23 @@ async function doctorCommand(opts = {}) {
|
|
|
3579
3636
|
return;
|
|
3580
3637
|
}
|
|
3581
3638
|
const workspaces = await client.callTool({ name: "list_workspaces", arguments: {} });
|
|
3582
|
-
|
|
3639
|
+
const workspaceError = toolError(workspaces);
|
|
3640
|
+
const workspaceCount = jsonArrayLength(workspaces, "libraries");
|
|
3641
|
+
const workspacesOk = !workspaceError && workspaceCount !== null;
|
|
3642
|
+
checks.push(workspacesOk ? pass("mcp_list_workspaces", `${workspaceCount} workspaces`) : fail("mcp_list_workspaces", workspaceError ?? "unexpected response shape"));
|
|
3583
3643
|
const search = await client.callTool({ name: "search_skills", arguments: { query: opts.query ?? "pdf" } });
|
|
3584
|
-
|
|
3644
|
+
const searchError = toolError(search);
|
|
3645
|
+
const skillCount = jsonArrayLength(search, "skills");
|
|
3646
|
+
const searchOk = !searchError && skillCount !== null;
|
|
3647
|
+
checks.push(searchOk ? pass("mcp_search_skills", `${skillCount} skills`) : fail("mcp_search_skills", searchError ?? "unexpected response shape"));
|
|
3585
3648
|
if (opts.ref) {
|
|
3649
|
+
if (!workspacesOk || !searchOk) {
|
|
3650
|
+
checks.push(warn("mcp_get_skill", "skipped because authenticated MCP API prechecks failed"));
|
|
3651
|
+
checks.push(warn("mcp_install_skill", "skipped because authenticated MCP API prechecks failed"));
|
|
3652
|
+
emitDoctor(checks, opts.json);
|
|
3653
|
+
if (checks.some((check) => !check.ok)) process.exit(1);
|
|
3654
|
+
return;
|
|
3655
|
+
}
|
|
3586
3656
|
const skill = await client.callTool({ name: "get_skill", arguments: { ref: opts.ref } });
|
|
3587
3657
|
checks.push(/SKILL\.md/.test(textOf(skill)) ? pass("mcp_get_skill", opts.ref) : fail("mcp_get_skill", "bundle did not include SKILL.md"));
|
|
3588
3658
|
const installed = await client.callTool({ name: "install_skill", arguments: { ref: opts.ref, target: opts.target ?? "codex" } });
|