@fylun/sdk
v0.1.0
Published
Typed client for the Fylun v1 tools API: upload, run, poll, download, retry.
Downloads
180
Maintainers
Readme
@fylun/sdk
Typed client for Fylun's v1 tools API. Upload a file, run a tool, wait for an
async job, download the result, and retry transient failures without ever
paying twice. Zero runtime dependencies; uses the global fetch, FormData
and Blob on Node 20+ and in browsers.
Quickstart
import { Fylun } from "@fylun/sdk";
const fylun = new Fylun({ apiKey: process.env.FYLUN_API_KEY! });
const run = await fylun.tools.run("web_search", { query: "site reliability postmortems", count: 5 });
console.log(run.output);Keys are created at https://fylun.ai/settings and look like fyl_....
Masked edit
import { readFileSync, writeFileSync } from "node:fs";
import { Fylun } from "@fylun/sdk";
const fylun = new Fylun({ apiKey: process.env.FYLUN_API_KEY! });
const frame = await fylun.files.upload(readFileSync("tortoise.png"), "tortoise.png", "image/png");
const mask = await fylun.files.upload(readFileSync("eyes-mask.png"), "eyes-mask.png", "image/png");
const run = await fylun.tools.run("edit_image", {
prompt: "eyes closed, everything else identical",
referenceImages: [frame.url],
model: "ideogram-v3-turbo",
mask: mask.url,
maskConvention: "white_edits",
keepOutsideMask: true,
});
const image = (run.output as { images: Array<{ id: string; url: string; window: { rawOutsideDrift: number } }> }).images[0]!;
if (image.window.rawOutsideDrift > 0.15) {
throw new Error(`model re-dreamed the frame (rawOutsideDrift ${image.window.rawOutsideDrift})`);
}
writeFileSync("tortoise-blink.png", await fylun.download(image.url));rawOutsideDrift is the share of unmasked pixels the model changed before
Fylun folded the output back onto your reference. Above ~0.15 the model
re-rendered the picture and the masked region no longer lines up; gate on the
number, not on how the result looks.
API
new Fylun({ apiKey, baseUrl = "https://fylun.ai", fetch? })files.upload(bytes | Blob, name, mimeType)->{ id, url, ... }tools.run(slug, input, { wait = true, timeoutMs, pollIntervalMs, signal, idempotencyKey })-> the run envelope. For an async tool the SDK pollsget_run_statuswith the returnedhandleand resolves withoutputreplaced by the finished job's status object. ThrowsFylunRunErrorwhen the run or the job fails.tools.submit(slug, input, { idempotencyKey, signal })-> the envelope as the server returned it, no waiting.runs.get(runId)-> the audit record for a tool call (cost, inputs, timing). It never reports an async job's progress; the server writes it once when the call returns.runs.wait(handleOrEnvelope, { timeoutMs, pollIntervalMs, signal })->{ status, output, error }for an async job. Pass the envelope fromsubmitor ahandlestring.download(url)->Uint8Array. The API key is sent only to Fylun's own origin.
Input types come from the tool manifest: tools.run("edit_image", {...}) is
checked against ToolInput<"edit_image">, and ToolSlug is the union of every
slug. Regenerate them with pnpm --filter @fylun/sdk gen after the manifest
changes; a test fails while the committed file is stale.
Errors and retries
FylunApiError— a non-2xx response.status,code(the server'serror.type),requestIdwhen the server sent one,body.FylunRunError— the run was accepted and finishedFAILEDorBLOCKED, or the async job failed.runis the final envelope.FylunTimeoutError—timeoutMselapsed while polling.
HTTP 429, 500, 502, 503, 504, 520, 522, 524 and network errors are retried up
to 4 attempts with exponential backoff. Every attempt carries the same
Idempotency-Key, so a retry replays the original run instead of billing a
second one. A 500 or 402 whose body is a run envelope is the run's own verdict
and is not retried. 4xx responses are never retried.
Development
This package is developed inside the Fylun monorepo and exported to
usefylun/fylun-sdk read-only. Issues
and feature requests are welcome there; code changes land in the monorepo and
are exported on release. src/generated/tools.ts is produced from the API's
tool manifest by pnpm gen, which only runs inside the monorepo; a test fails
if the committed file ever drifts from the manifest.
