@devfellowship/sdk
v0.2.0
Published
DevFellowship SDK — Supabase-compatible client with workflow primitives
Maintainers
Readme
@devfellowship/sdk
DevFellowship SDK v0 — Supabase-compatible client with workflow primitives.
Install
npm install @devfellowship/sdkUsage
import { createDflClient } from "@devfellowship/sdk";
const dfl = createDflClient({
supabaseUrl: "https://your-project.supabase.co",
supabaseAnonKey: "your-anon-key",
natsUrl: "nats://localhost:4222",
engineUrl: "http://localhost:3335",
});
// Raw Supabase client (same API as @supabase/supabase-js)
const { data } = await dfl.client.from("users").select("*");
// Fire an intent (publishes to NATS DFL_INTENTS stream)
await dfl.wf.fireIntent("onboarding.new_member", {
intentId: "unique-id-123",
payload: { userId: "abc", email: "[email protected]" },
});
// Subscribe to workflow instance progress events
const unsub = await dfl.wf.subscribeInstance("instance-uuid", (event) => {
console.log("Workflow event:", event);
});
// Call engine proxy endpoint
const result = await dfl.wf.proxy("my-service-node", { key: "value" });LLM proxy
All LLM calls in the DFL fleet should go through dfl-flows-engine so we get
unified OTEL spans, cost attribution, and provider-agnostic adoption.
wf.chat() is a thin typed wrapper around wf.proxy() for chat-completion
workflows.
Quickstart
import { createDflClient, type LlmChatResponse } from "@devfellowship/sdk";
const dfl = createDflClient({
supabaseUrl: process.env.SUPABASE_URL!,
supabaseAnonKey: process.env.SUPABASE_ANON_KEY!,
engineUrl: process.env.DFL_ENGINE_URL!, // required for wf.chat / wf.proxy
});
const res = await dfl.wf.chat(
"openrouter-chat",
{
model: "gpt-4.1-mini",
messages: [
{ role: "system", content: "You are concise." },
{ role: "user", content: "Title for a 3min video about RLS?" },
],
temperature: 0.4,
},
{ appName: "lesson-studio" }
);
console.log(res.choices[0].message.content);The appName is propagated as the X-DFL-App header so the engine can attribute
cost back to the right product. If omitted, process.env.DFL_APP_NAME is used as
a fallback (Node only — guarded for browser-safe usage).
Available workflow keys
These are the canonical LLM workflow definitions in dfl-flows-definitions
(see dfl-flows-definitions#13). Pass any of them as the first arg to
wf.chat() / wf.proxy():
| Key | Provider | Shape |
|---|---|---|
| openrouter-chat | OpenRouter (preferred default per DFL standard) | LlmChatRequest → LlmChatResponse |
| openai-chat | OpenAI direct | LlmChatRequest → LlmChatResponse |
| anthropic-chat | Anthropic direct | LlmChatRequest → LlmChatResponse (normalized server-side) |
| groq-chat | Groq | LlmChatRequest → LlmChatResponse |
| groq-whisper-transcribe | Groq Whisper | WhisperTranscribeRequest → WhisperTranscribeResponse (use wf.proxy() not wf.chat()) |
Cancellation
Pass an AbortSignal to cancel an in-flight call:
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5_000);
await dfl.wf.chat(
"openrouter-chat",
{ model: "gpt-4.1-mini", messages: [...] },
{ appName: "dfl-seo", signal: ctrl.signal }
);Environment variables
When using createDflClientFromEnv():
| Variable | Required | Description |
|---|---|---|
| SUPABASE_URL | Yes | Supabase project URL |
| SUPABASE_ANON_KEY | Yes | Supabase anonymous key |
| DFL_NATS_URL | No | NATS server URL for workflow ops |
| DFL_ENGINE_URL | No | Flows engine URL — required for wf.proxy() / wf.chat() |
| DFL_APP_NAME | No | Default X-DFL-App header value (overridden by opts.appName) |
