npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@flowget/ai-chat

v0.1.1

Published

Builder-agnostic AI workflow-authoring chat for Flowget — an assistant-ui chat panel (./react) + an SSE BFF helper (./server) over @flowget/ai's streaming author(). Bring your own builder via the currentGraph/applyGraph seam.

Readme

@flowget/ai-chat

Builder-agnostic AI workflow-authoring chat for Flowget.

A user describes a workflow in natural language; the chat streams a proposal, shows an approval card, and — once approved — applies the laid-out graph to your canvas. Editing works the same way (the current graph is the context).

Built on assistant-ui primitives (streaming, approval gate, thread) and @flowget/ai's streaming author() — with no lock-in: the LLM stays behind @flowget/ai's BYO adapter, and the chat never imports your builder.

Two entry points

| Import | Runs | What | | --- | --- | --- | | @flowget/ai-chat/react | browser | <WorkflowChat> — the chat panel + streaming runtime + proposal card | | @flowget/ai-chat/server | your BFF | createChatStreamResponse() — SSE over @flowget/ai's authorStream() | | @flowget/ai-chat/styles.css | — | the stylesheet (import once) |

Install

npm install @flowget/ai-chat @flowget/ai @assistant-ui/react

react, react-dom, @assistant-ui/react are peer deps (you provide them). @flowget/ai is an optional peer — install it only if you use the /server helper (the authoring engine runs there). A react-only consumer with its own backend transport can skip it.

Client — mount the chat

Drop <WorkflowChat> inside your builder. It's builder-agnostic: pass the live currentGraph and an applyGraph callback (e.g. your store's setGraph). The chat proposes; you apply.

import "@flowget/ai-chat/styles.css";
import { WorkflowChat } from "@flowget/ai-chat/react";
import { useFlowgetWorkflow } from "@flowget/builder"; // your builder

function BuilderChat() {
  const { graph, setGraph } = useFlowgetWorkflow();
  return <WorkflowChat currentGraph={graph} applyGraph={setGraph} />;
}

// inside your builder's children slot:
// <FlowgetBuilder ...><BuilderChat /></FlowgetBuilder>

WorkflowChat props:

| Prop | Type | | | --- | --- | --- | | currentGraph | WorkflowGraph | live canvas graph (edit context + layout baseline) | | applyGraph | (g: WorkflowGraph) => void | commit a laid-out graph to the canvas | | transport? | ChatStreamTransport | fully custom backend transport (overrides endpoint) | | endpoint? | string | endpoint for the default SSE transport (/api/chat) | | heading? | string | panel heading (default "Workflow AI") | | subtitle? | string | panel subtitle (default "Draft & edit with a prompt") | | theme? | string | stamp data-theme on the body-portaled panel so a scoped [data-theme] token override reaches it (see Theming) | | examples? | readonly string[] | empty-state suggestion chips; [] hides them | | layout? | WorkflowLayout | lay a proposal out before apply (default layoutProposal) — see below | | actor? | ChatActor | caller-identity hint — untrusted on the wire (see Security) | | context? | unknown | opaque continuity payload forwarded to the model |

Layout — delegate to your builder

By default the chat lays each proposal out with the built-in layoutProposal (a zero-dependency BFS layered layout that preserves the positions of nodes already on the canvas). A builder-host with a better layout — e.g. a dagre autoLayout / merge — injects it via the layout prop, keeping ai-chat builder-agnostic (the host supplies the function; the chat never imports your builder):

import { WorkflowChat, type WorkflowLayout } from "@flowget/ai-chat/react";

const layout: WorkflowLayout = (proposed, current) => mergeGraph(current, proposed);

<WorkflowChat currentGraph={graph} applyGraph={setGraph} layout={layout} />;

layout: (proposed: ProposedGraph, current: WorkflowGraph) => WorkflowGraph. layoutProposal stays exported as the default / fallback.

Headless mode

<WorkflowChat> is a turnkey shell built from smaller pieces. To bring your own shell — or render assistant-ui primitives yourself — compose them directly:

import {
  ChatRuntimeProvider,
  httpChatStreamTransport,
  layoutProposal,
} from "@flowget/ai-chat/react";

// getCurrentGraph must be referentially stable (a ref getter, not an inline fn)
<ChatRuntimeProvider getCurrentGraph={getCurrentGraph} transport={httpChatStreamTransport("/api/chat")}>
  {/* your own assistant-ui <Thread>, composer, and proposal rendering */}
</ChatRuntimeProvider>
  • ChatRuntimeProvider — wires assistant-ui's LocalRuntime to the authoring adapter and provides it to your subtree.
  • createWorkflowChatAdapter(...) — the raw ChatModelAdapter, if you drive the runtime yourself.
  • httpChatStreamTransport(endpoint?) — the default SSE transport; swap it for any ChatStreamTransport (an async generator of ChatStreamEvents) to reach a custom backend.
  • layoutProposal(proposed, current?) — the pure layout helper (BFS layered layout; preserves the positions of nodes that already exist on the canvas).

Server — the BFF route

Wire your @flowget/ai config (your node catalog + LLM adapter) into the SSE helper. The engine runs entirely server-side.

import { createChatStreamResponse } from "@flowget/ai-chat/server";
import { finalizeConfig, resolveCatalog, openaiAdapter } from "@flowget/ai";

const catalog = await resolveCatalog({ registryDir: process.env.REGISTRY_DIR! });
const config = finalizeConfig(
  { adapter: openaiAdapter({ apiKey: process.env.OPENROUTER_API_KEY! }), catalog },
  catalog,
);

// e.g. a Bun / Node / edge handler for POST /api/chat
export async function POST(req: Request): Promise<Response> {
  // ⚠️ Your auth + rate-limiting run FIRST — see Security below.
  const session = await getSession(req);
  if (!session) return new Response("Unauthorized", { status: 401 });

  return createChatStreamResponse(req, config, {
    // Inject a SERVER-DERIVED actor over the untrusted client value:
    buildRequest: (parsed) => ({ ...parsed, actor: session.actor }),
  });
}

createChatStreamResponse(req, config, options?) — parses + validates the request, then streams the authoring result as SSE. Options:

| Option | | | | --- | --- | --- | | buildRequest? | (parsed, req) => AuthorRequest | rebuild the request — inject a server-derived actor / context | | onError? | (err) => string | map an error to a client-safe message (default "Authoring failed") | | maxBodyBytes? | number | request-body cap (default 1 MiB) | | maxCommandLength? | number | command length cap (default 8000) |

Need to build the AuthorRequest yourself? Use the lower-level streamAuthorSSE(request, config, { signal?, onError? }).

The wire format between /server and /react is SSE: one data:-framed event per line — text-delta* then a terminal proposal | message, or error.

Security

The /server helper is unauthenticated by design and does no rate-limiting. It is streaming plumbing, not a security boundary.

You MUST:

  • Put your own authentication and authorization in front of the route (reject unauthenticated requests before calling the helper).
  • Add rate-limiting / abuse protection — each request drives an LLM call.
  • Treat the client-supplied actor as untrusted. It is plain JSON on the request body; a browser can send anything. If any @flowget/ai toolset or authorizer gates data access on the actor (e.g. by tenantId), derive the actor server-side (from your session/JWT) and inject it with buildRequest — and/or verify it with a @flowget/ai authorizer. Otherwise a client can spoof it.

Server-side errors are logged on the server and never forwarded verbatim to the browser (the client only sees a generic message, or your onError mapping).

Theming

Import the stylesheet once — it is self-contained and renders correctly with no host theme. Every value reads a --flowget-* design token with a built-in fallback, so where your app defines those tokens (light/dark), the chat inherits them automatically; where it doesn't, the fallback applies.

Overridable design tokens (set them on :root or any ancestor):

| Token | Fallback | Used for | | --- | --- | --- | | --flowget-color-accent | #6366f1 | primary / brand accent | | --flowget-color-text | #0f172a | body text | | --flowget-color-text-subtle | #64748b | secondary text | | --flowget-color-text-inverse | #ffffff | text on the accent | | --flowget-color-bg | #ffffff | input / chip background | | --flowget-color-bg-elevated | #ffffff | panel background | | --flowget-color-surface-strong | #f8fafc | proposal-card surface | | --flowget-color-border | #e2e8f0 | borders | | --flowget-color-border-strong | #cbd5e1 | hover borders | | --flowget-color-danger | #dc2626 | error state | | --flowget-color-success | #16a34a | applied confirmation | | --flowget-radius-lg / -md | 14px / 9px | corner radii | | --flowget-shadow-lg | 0 12px 32px -8px rgba(15,23,42,.28) | panel / launcher shadow | | --flowget-font-sans / -mono | system stacks | typography | | --flowget-font-size-sm / -xs / -md | 13px / 11px / 15px | type scale | | --flowget-motion-fast | 140ms | transition duration |

Two package-owned knobs control placement (class prefix: fg-aichat-):

| Token | Fallback | | | --- | --- | --- | | --fg-aichat-z | 200 | stacking order of the launcher + panel | | --fg-aichat-launcher-offset | 24px | gap from the bottom-right corner |

⚠️ Scoped theme overrides and the body portal

The chat panel portals into document.body — outside your builder wrapper. (The launcher is inline and already inherits your theme; only the panel portals.) So a token override you scope to a selector on that wrapper doesn't cascade into the panel. If you theme the builder with a scoped block like

[data-theme="midnight"] { --flowget-color-accent: #22d3ee; /* … */ }

(the same block <FlowgetBuilder theme="midnight"> stamps data-theme for), those tokens fall back to :root inside the portaled panel unless you pass the matching theme:

<WorkflowChat currentGraph={graph} applyGraph={setGraph} theme="midnight" />

theme stamps data-theme="midnight" on the panel root (.fg-aichat-panel), so the scoped block now matches and the tokens cascade into the panel. It's a plain string (builder-agnostic — not tied to any named-theme enum) and is omitted entirely when unset, so unscoped (:root-defined) tokens keep working with no change.

How it fits together

browser                          your BFF                         @flowget/ai
────────                         ────────                         ───────────
<WorkflowChat>                   createChatStreamResponse(req,cfg)
  LocalRuntime ── POST /api/chat ──►  authorStream(cfg, request) ──► BYO adapter (LLM)
  (streams text) ◄──── SSE ────────   (text-delta* → proposal|message)
  proposal card → Apply → applyGraph(graph)   (your canvas)

License

FSL-1.1-ALv2