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

@agflowai/sdk

v1.0.0

Published

Official JS/Node SDK for AgentFlow

Readme

@agflowai/sdk

Official Node.js SDK for AgentFlow — programmatic access to workspaces, runs, streaming events, and artifacts.

  • Node.js ≥ 18 required (uses native fetch and ReadableStream)
  • Zero runtime dependencies
  • Full TypeScript declarations included

Installation

npm install @agflowai/sdk

Quick Start

const { AgentFlow } = require("@agflowai/sdk");

const af = new AgentFlow({
  baseUrl: "https://api.yourdomain.com",
  apiKey: "af_live_...",
  orgId: "00000000-0000-0000-0000-000000000000",
});

const { run } = await af.runs.create({
  workspace_id: "wks_...",
  input_text: "Analyse Q1 results",
});

for await (const event of af.runs.events(run.id)) {
  console.log(event.type, event.data);
}

Authentication

Generate an API key from Settings → API Keys in the AgentFlow dashboard. Keys take the form:

| Prefix | Usage | | ------------- | ---------------------- | | af_live_... | Production / live data | | af_test_... | Test / sandbox data |

Every request carries Authorization: Bearer <apiKey> and X-Org-Id: <orgId>.


Constructor

new AgentFlow(options);

| Option | Type | Required | Default | Description | | --------- | -------- | -------- | ------- | -------------------------------------------------- | | baseUrl | string | ✅ | — | API base URL, no trailing slash | | apiKey | string | ✅ | — | API key (af_live_ or af_test_) | | orgId | string | ✅ | — | UUID of the organisation to operate in | | timeout | number | | 30000 | Request timeout in ms (not applied to SSE streams) | | retries | number | | 2 | Retry count on transient 5xx GET errors |


Resources

After instantiation the SDK exposes three resource namespaces:

af.templates   — workflow templates
af.workspaces  — workspaces
af.runs        — runs, events, artifacts

af.templates

templates.list()

List all templates visible to the org (platform + org-owned).

const { templates } = await af.templates.list();

Returns: { templates: Template[] }

templates.get(slug)

Get a single template by its slug.

const { template } = await af.templates.get("investment-analyst-desk");

Returns: { template: Template }
Throws: AgentFlowError(404) if not found.

templates.update(slug, body)

Update a template (org admin only). Sends a PATCH — include only fields you want to change.

const { template } = await af.templates.update("investment-analyst-desk", {
  description: "Updated description",
});

| Field | Type | Description | | ------------------------ | -------- | --------------------------------------- | | name | string | Display name | | description | string | Short description | | category | string | Template category | | definition_json | object | Full template definition | | definition_schema_json | object | JSON schema for the template | | contract_json | object | Contract configuration for the template |

Returns: { template: Template }


af.workspaces

workspaces.list(opts?)

List workspaces in the org.

const { workspaces, total } = await af.workspaces.list({
  search: "billing", // optional workspace name filter
  template_search: "support", // optional template name filter
  limit: 20,
  offset: 0,
});

| Option | Type | Default | Description | | ----------------- | -------- | ------- | ------------------------ | | search | string | — | Filter by workspace name | | template_search | string | — | Filter by template name | | limit | number | 10 | Max results (1–200) | | offset | number | 0 | Pagination offset |

Returns: { workspaces: Workspace[], total: number, limit: number, offset: number }

workspaces.get(id)

Get a single workspace by UUID.

const { workspace } = await af.workspaces.get("wks_...");

Returns: { workspace: Workspace }

workspaces.create(body)

Create a new workspace. Accepts either template_id (UUID) or template_slug (string).

// Using a slug (SDK resolves it automatically)
const { workspace } = await af.workspaces.create({
  name: "Q1 Analysis",
  template_slug: "investment-analyst-desk",
});

// Using a UUID
const { workspace } = await af.workspaces.create({
  name: "Q1 Analysis",
  template_id: "tpl_...",
});

| Field | Type | Required | Description | | --------------- | -------- | -------- | -------------------------------------------- | | name | string | ✅ | Workspace display name | | template_id | string | ✅* | Template UUID | | template_slug | string | ✅* | Template slug (alternative to template_id) |

* One of template_id or template_slug must be provided.

Returns: { workspace: Workspace }

workspaces.update(id, body)

Update a workspace (PATCH). Include only fields to change.

const { workspace } = await af.workspaces.update("wks_...", {
  name: "Revised Q1 Analysis",
});

Returns: { workspace: Workspace }


af.runs

runs.create(body)

Create and dispatch a new run.

const { run } = await af.runs.create({
  workspace_id: "wks_...",
  input_text: "Analyse the impact of AI on software jobs in 2026.",
});

| Field | Type | Required | Description | | ------------------ | -------- | -------- | ------------------------------------------------------ | | workspace_id | string | ✅ | Workspace UUID | | input_text | string | | Free-text prompt | | input_json | object | | Structured input (validated against template contract) | | provider_id | string | | Override workspace default provider | | title | string | | Run title (max 160 chars) | | replay_of_run_id | string | | Provenance reference |

Returns: { run: Run, runtime: object }

runs.get(id)

Get a run by UUID, including tasks, artifacts, and template metadata.

const { run, tasks, artifacts, template } = await af.runs.get("run_...");

Returns: { run: Run, tasks: Task[], artifacts: Artifact[], template: Template | null }

runs.list(opts?)

List runs in the org.

const { runs, total } = await af.runs.list({
  workspace_id: "wks_...",
  status: "done",
  sort: "created_at",
  dir: "desc",
  limit: 20,
});

| Option | Type | Default | Description | | -------------- | -------- | ------------ | -------------------------------------------------- | | workspace_id | string | — | Filter by workspace | | runner_id | string | — | Filter by runner | | status | string | — | done | failed | running | planning | … | | search | string | — | Substring match on title or input_text | | sort | string | created_at | Sort field | | dir | string | desc | asc | desc | | limit | number | 10 | Max results (1–200) | | offset | number | 0 | Pagination offset |

Returns: { runs: Run[], total: number, limit: number, offset: number }

runs.cancel(id)

Cancel an active run.

await af.runs.cancel("run_...");

Returns: { ok: true }
Throws: AgentFlowError(409) if the run is already in a terminal state.

runs.rerun(id, opts?)

Rerun a completed run using the same or overridden input.

const run = await af.runs.rerun("run_...", {
  mode: "snapshot", // 'snapshot' | 'latest' (default: 'snapshot')
  input_override: {
    // shallow-merged onto source run's input_json
    ticker: "NVDA",
  },
});

| Option | Type | Default | Description | | ---------------- | -------- | ---------- | --------------------------------------------------------------------------- | | mode | string | snapshot | snapshot reuses the source run's input (both modes use the current template in v1); latest is reserved for future template-version pinning | | input_override | object | — | Partial input to override on the source run's input_json |

Returns: Run (the newly created run)
Throws: AgentFlowError(409) if the source run is still active.

runs.events(runId, opts?)

Stream live events from a run as an async iterable. Automatically terminates when a terminal event (run.finished, run.failed, run.cancelled) is received.

for await (const event of af.runs.events("run_...")) {
  console.log(event.type, event.data);
}

With abort support:

const controller = new AbortController();
setTimeout(() => controller.abort(), 60_000); // 60-second timeout

for await (const event of af.runs.events("run_...", {
  signal: controller.signal,
})) {
  console.log(event.type, event.data);
}

Each yielded event:

{
  id: 'evt_...',       // SSE event ID (or null)
  type: 'task.done',   // event type string
  data: { ... }        // parsed JSON payload (or raw string if not JSON)
}

Common event types:

| Type | Description | | --------------- | ---------------------------------------------- | | run.started | Run has been accepted and is starting | | run.planning | Plan is being generated | | task.started | A task has started executing | | task.stream | Incremental LLM output chunk for a task | | task.done | A task completed successfully | | task.failed | A task failed | | run.finished | All tasks complete — terminal, stream ends | | run.failed | Run failed — terminal, stream ends | | run.cancelled | Run was cancelled — terminal, stream ends |

| Option | Type | Default | Description | | ------------- | ------------- | ------- | ---------------------------------------- | | history | boolean | true | Replay persisted events before live tail | | signal | AbortSignal | — | Cancellation signal | | lastEventId | string | — | Resume SSE stream from this event ID |

runs.downloadArtifact(runId, artifactId, opts?)

Download a run artifact as a Buffer.

const { buffer, filename, contentType } = await af.runs.downloadArtifact(
  "run_...",
  "art_...",
);
fs.writeFileSync(filename ?? "output.bin", buffer);

| Option | Type | Description | | -------- | ------------- | ------------------- | | signal | AbortSignal | Cancellation signal |

Returns: { buffer: Buffer, filename: string | null, contentType: string | null }


Error Handling

All errors thrown by the SDK are instances of AgentFlowError (or its subclass AgentFlowQuotaError).

const {
  AgentFlow,
  AgentFlowError,
  AgentFlowQuotaError,
} = require("@agflowai/sdk");

try {
  const { run } = await af.runs.create({ workspace_id: "wks_..." });
} catch (err) {
  if (err instanceof AgentFlowQuotaError) {
    console.error(`Quota exceeded: ${err.code}`); // e.g. QUOTA_RUNS_MONTHLY
    console.error(`Limit: ${err.limit}`);
    console.error(`Upgrade: ${err.upgradeUrl}`);
  } else if (err instanceof AgentFlowError) {
    console.error(`API error ${err.status}: ${err.message}`); // e.g. 404 Not found
    console.error(`Code: ${err.code}`); // optional machine-readable code
  } else {
    throw err; // network error etc.
  }
}

AgentFlowError

| Property | Type | Description | | --------- | --------------------- | ------------------------------------ | | message | string | Human-readable error message | | status | number | HTTP status code | | code | string \| undefined | Machine-readable error code from API |

AgentFlowQuotaError extends AgentFlowError

Thrown on 429 responses with a QUOTA_* code.

| Property | Type | Description | | ------------ | --------------------- | --------------------------------- | | limit | number | The quota limit that was exceeded | | upgradeUrl | string \| undefined | URL to the upgrade/billing page |


TypeScript

TypeScript declarations are bundled. No @types/ package needed.

import { AgentFlow, AgentFlowError, AgentFlowQuotaError } from '@agflowai/sdk';
import type { Run, Task, Artifact, RunEvent } from '@agflowai/sdk';

const af = new AgentFlow({
  baseUrl: process.env.AGFLOW_API_URL!,
  apiKey: process.env.AGFLOW_API_KEY!,
  orgId: process.env.AGFLOW_ORG_ID!,
});

const { run }: { run: Run } = await af.runs.create({
  workspace_id: 'wks_...',
  input_text: 'Hello',
});

for await (const event: RunEvent of af.runs.events(run.id)) {
  console.log(event.type);
}

Examples

All examples live in examples/. Each file is self-contained and runnable with node. Set the environment variables described in each file's header before running.

Common setup

Every example reads these env vars:

export AGFLOW_API_URL=https://api.yourdomain.com
export AGFLOW_API_KEY=af_live_...
export AGFLOW_ORG_ID=00000000-0000-0000-0000-000000000000

start-run.js — Full end-to-end run

Use case: Kick off a single run from scratch and wait for it to finish.

Covers:

  • Creating a workspace from a template_slug
  • Dispatching a run with input_text
  • Polling with runs.get() until the run reaches a terminal status
  • Printing task results and downloading artifacts
  • Displaying estimated cost
TEMPLATE_SLUG=investment-analyst-desk node examples/start-run.js

stream-events.js — Live event streaming

Use case: Watch a run's progress in real time, event by event.

Covers:

  • Starting a new run or rerunning an existing one (set RERUN_ID)
  • Consuming the SSE event stream with for await ... of af.runs.events()
  • Graceful Ctrl-C cancellation via AbortController
  • Handling AbortError cleanly
WORKSPACE_ID=<uuid> node examples/stream-events.js

# or rerun an existing run:
RERUN_ID=<run-uuid> node examples/stream-events.js

list-runs.js — Run history and cost report

Use case: Audit or report on runs in a workspace — useful for dashboards, billing checks, or validating a batch.

Covers:

  • Paginating through runs.list() with limit / offset
  • Filtering by status (e.g. STATUS=done)
  • Printing a summary table with status, cost, and title
  • Aggregating total cost and token usage across all runs
WORKSPACE_ID=<uuid> node examples/list-runs.js

# filter to only completed runs:
STATUS=done WORKSPACE_ID=<uuid> node examples/list-runs.js

batch-runs.js — Parallel fan-out

Use case: Process a list of inputs concurrently — one run per input — and collect all results.
Common pattern for per-ticker analysis, per-client reports, or bulk document processing.

Covers:

  • Dispatching N runs simultaneously with Promise.allSettled
  • Polling all in-flight runs in parallel until every one settles
  • Handling partial failures (some succeed, some fail)
  • Reporting a final cost and pass/fail summary
# Edit the INPUTS array in the file to match your workload, then:
WORKSPACE_ID=<uuid> node examples/batch-runs.js

rerun-failed.js — Batch retry failed runs

Use case: After fixing a provider outage, bad config, or transient error — find every failed run in a workspace and requeue them all in one command.

Covers:

  • Listing runs filtered to status: 'failed'
  • DRY_RUN=1 mode to preview what would be rerun before committing
  • Fan-out of runs.rerun() calls with Promise.allSettled
  • Reporting which reruns succeeded and which errored
# Preview what would be rerun (no mutations):
DRY_RUN=1 WORKSPACE_ID=<uuid> node examples/rerun-failed.js

# Execute:
WORKSPACE_ID=<uuid> node examples/rerun-failed.js

Running Tests

cd packages/sdk
npm test

Uses the Node.js built-in test runner (node:test). No extra dependencies.


License

MIT