{"_id":"@agflowai/sdk","name":"@agflowai/sdk","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@agflowai/sdk","version":"1.0.0","description":"Official JS/Node SDK for AgentFlow","main":"src/index.js","types":"typings/index.d.ts","scripts":{"test":"node --test test/*.test.js"},"engines":{"node":">=18"},"license":"MIT","keywords":["agentflow","sdk","ai","agents","llm"],"gitHead":"44f11412dfe3829376e3d066df00ce70296d755e","_id":"@agflowai/sdk@1.0.0","_nodeVersion":"24.11.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-M00RY0sqdQyv+Y/I6O3E5pvnRTQ08mqkbp05T0CJFB8qBw5AFUBYZt3NKrwF+CawNam4Irct3jFiA8MAIXfrvA==","shasum":"45f0ab6688c5407729756e60eda0ce124b70e176","tarball":"https://registry.npmjs.org/@agflowai/sdk/-/sdk-1.0.0.tgz","fileCount":11,"unpackedSize":50193,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDT/yZolNxs20lt+TvpM7S/10GEx7Y+cURiRo2LXtxlWgIgELAENxNNNglX8dPUBkNrRfBclpT4A9Zoy69NXW7uHo4="}]},"_npmUser":{"name":"khanh245","email":"khanh.nguyen@agflow.io"},"directories":{},"maintainers":[{"name":"khanh245","email":"khanh.nguyen@agflow.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_1.0.0_1776399947061_0.7391422185788667"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-17T04:25:46.906Z","1.0.0":"2026-04-17T04:25:47.225Z","modified":"2026-04-17T04:25:47.466Z"},"maintainers":[{"name":"khanh245","email":"khanh.nguyen@agflow.io"}],"description":"Official JS/Node SDK for AgentFlow","keywords":["agentflow","sdk","ai","agents","llm"],"license":"MIT","readme":"# @agflowai/sdk\r\n\r\nOfficial Node.js SDK for [AgentFlow](https://agentflow.ai) — programmatic access to workspaces, runs, streaming events, and artifacts.\r\n\r\n- Node.js ≥ 18 required (uses native `fetch` and `ReadableStream`)\r\n- Zero runtime dependencies\r\n- Full TypeScript declarations included\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @agflowai/sdk\r\n```\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n```js\r\nconst { AgentFlow } = require(\"@agflowai/sdk\");\r\n\r\nconst af = new AgentFlow({\r\n  baseUrl: \"https://api.yourdomain.com\",\r\n  apiKey: \"af_live_...\",\r\n  orgId: \"00000000-0000-0000-0000-000000000000\",\r\n});\r\n\r\nconst { run } = await af.runs.create({\r\n  workspace_id: \"wks_...\",\r\n  input_text: \"Analyse Q1 results\",\r\n});\r\n\r\nfor await (const event of af.runs.events(run.id)) {\r\n  console.log(event.type, event.data);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Authentication\r\n\r\nGenerate an API key from **Settings → API Keys** in the AgentFlow dashboard. Keys take the form:\r\n\r\n| Prefix        | Usage                  |\r\n| ------------- | ---------------------- |\r\n| `af_live_...` | Production / live data |\r\n| `af_test_...` | Test / sandbox data    |\r\n\r\nEvery request carries `Authorization: Bearer <apiKey>` and `X-Org-Id: <orgId>`.\r\n\r\n---\r\n\r\n## Constructor\r\n\r\n```js\r\nnew AgentFlow(options);\r\n```\r\n\r\n| Option    | Type     | Required | Default | Description                                        |\r\n| --------- | -------- | -------- | ------- | -------------------------------------------------- |\r\n| `baseUrl` | `string` | ✅       | —       | API base URL, no trailing slash                    |\r\n| `apiKey`  | `string` | ✅       | —       | API key (`af_live_` or `af_test_`)                 |\r\n| `orgId`   | `string` | ✅       | —       | UUID of the organisation to operate in             |\r\n| `timeout` | `number` |          | `30000` | Request timeout in ms (not applied to SSE streams) |\r\n| `retries` | `number` |          | `2`     | Retry count on transient 5xx GET errors            |\r\n\r\n---\r\n\r\n## Resources\r\n\r\nAfter instantiation the SDK exposes three resource namespaces:\r\n\r\n```\r\naf.templates   — workflow templates\r\naf.workspaces  — workspaces\r\naf.runs        — runs, events, artifacts\r\n```\r\n\r\n---\r\n\r\n### `af.templates`\r\n\r\n#### `templates.list()`\r\n\r\nList all templates visible to the org (platform + org-owned).\r\n\r\n```js\r\nconst { templates } = await af.templates.list();\r\n```\r\n\r\nReturns: `{ templates: Template[] }`\r\n\r\n#### `templates.get(slug)`\r\n\r\nGet a single template by its slug.\r\n\r\n```js\r\nconst { template } = await af.templates.get(\"investment-analyst-desk\");\r\n```\r\n\r\nReturns: `{ template: Template }`  \r\nThrows: `AgentFlowError(404)` if not found.\r\n\r\n#### `templates.update(slug, body)`\r\n\r\nUpdate a template (org admin only). Sends a `PATCH` — include only fields you want to change.\r\n\r\n```js\r\nconst { template } = await af.templates.update(\"investment-analyst-desk\", {\r\n  description: \"Updated description\",\r\n});\r\n```\r\n\r\n| Field                    | Type     | Description                             |\r\n| ------------------------ | -------- | --------------------------------------- |\r\n| `name`                   | `string` | Display name                            |\r\n| `description`            | `string` | Short description                       |\r\n| `category`               | `string` | Template category                       |\r\n| `definition_json`        | `object` | Full template definition                |\r\n| `definition_schema_json` | `object` | JSON schema for the template            |\r\n| `contract_json`          | `object` | Contract configuration for the template |\r\n\r\nReturns: `{ template: Template }`\r\n\r\n---\r\n\r\n### `af.workspaces`\r\n\r\n#### `workspaces.list(opts?)`\r\n\r\nList workspaces in the org.\r\n\r\n```js\r\nconst { workspaces, total } = await af.workspaces.list({\r\n  search: \"billing\", // optional workspace name filter\r\n  template_search: \"support\", // optional template name filter\r\n  limit: 20,\r\n  offset: 0,\r\n});\r\n```\r\n\r\n| Option            | Type     | Default | Description              |\r\n| ----------------- | -------- | ------- | ------------------------ |\r\n| `search`          | `string` | —       | Filter by workspace name |\r\n| `template_search` | `string` | —       | Filter by template name  |\r\n| `limit`           | `number` | `10`    | Max results (1–200)      |\r\n| `offset`          | `number` | `0`     | Pagination offset        |\r\n\r\nReturns: `{ workspaces: Workspace[], total: number, limit: number, offset: number }`\r\n\r\n#### `workspaces.get(id)`\r\n\r\nGet a single workspace by UUID.\r\n\r\n```js\r\nconst { workspace } = await af.workspaces.get(\"wks_...\");\r\n```\r\n\r\nReturns: `{ workspace: Workspace }`\r\n\r\n#### `workspaces.create(body)`\r\n\r\nCreate a new workspace. Accepts either `template_id` (UUID) or `template_slug` (string).\r\n\r\n```js\r\n// Using a slug (SDK resolves it automatically)\r\nconst { workspace } = await af.workspaces.create({\r\n  name: \"Q1 Analysis\",\r\n  template_slug: \"investment-analyst-desk\",\r\n});\r\n\r\n// Using a UUID\r\nconst { workspace } = await af.workspaces.create({\r\n  name: \"Q1 Analysis\",\r\n  template_id: \"tpl_...\",\r\n});\r\n```\r\n\r\n| Field           | Type     | Required | Description                                  |\r\n| --------------- | -------- | -------- | -------------------------------------------- |\r\n| `name`          | `string` | ✅       | Workspace display name                       |\r\n| `template_id`   | `string` | ✅\\*     | Template UUID                                |\r\n| `template_slug` | `string` | ✅\\*     | Template slug (alternative to `template_id`) |\r\n\r\n\\* One of `template_id` or `template_slug` must be provided.\r\n\r\nReturns: `{ workspace: Workspace }`\r\n\r\n#### `workspaces.update(id, body)`\r\n\r\nUpdate a workspace (PATCH). Include only fields to change.\r\n\r\n```js\r\nconst { workspace } = await af.workspaces.update(\"wks_...\", {\r\n  name: \"Revised Q1 Analysis\",\r\n});\r\n```\r\n\r\nReturns: `{ workspace: Workspace }`\r\n\r\n---\r\n\r\n### `af.runs`\r\n\r\n#### `runs.create(body)`\r\n\r\nCreate and dispatch a new run.\r\n\r\n```js\r\nconst { run } = await af.runs.create({\r\n  workspace_id: \"wks_...\",\r\n  input_text: \"Analyse the impact of AI on software jobs in 2026.\",\r\n});\r\n```\r\n\r\n| Field              | Type     | Required | Description                                            |\r\n| ------------------ | -------- | -------- | ------------------------------------------------------ |\r\n| `workspace_id`     | `string` | ✅       | Workspace UUID                                         |\r\n| `input_text`       | `string` |          | Free-text prompt                                       |\r\n| `input_json`       | `object` |          | Structured input (validated against template contract) |\r\n| `provider_id`      | `string` |          | Override workspace default provider                    |\r\n| `title`            | `string` |          | Run title (max 160 chars)                              |\r\n| `replay_of_run_id` | `string` |          | Provenance reference                                   |\r\n\r\nReturns: `{ run: Run, runtime: object }`\r\n\r\n#### `runs.get(id)`\r\n\r\nGet a run by UUID, including tasks, artifacts, and template metadata.\r\n\r\n```js\r\nconst { run, tasks, artifacts, template } = await af.runs.get(\"run_...\");\r\n```\r\n\r\nReturns: `{ run: Run, tasks: Task[], artifacts: Artifact[], template: Template | null }`\r\n\r\n#### `runs.list(opts?)`\r\n\r\nList runs in the org.\r\n\r\n```js\r\nconst { runs, total } = await af.runs.list({\r\n  workspace_id: \"wks_...\",\r\n  status: \"done\",\r\n  sort: \"created_at\",\r\n  dir: \"desc\",\r\n  limit: 20,\r\n});\r\n```\r\n\r\n| Option         | Type     | Default      | Description                                        |\r\n| -------------- | -------- | ------------ | -------------------------------------------------- |\r\n| `workspace_id` | `string` | —            | Filter by workspace                                |\r\n| `runner_id`    | `string` | —            | Filter by runner                                   |\r\n| `status`       | `string` | —            | `done` \\| `failed` \\| `running` \\| `planning` \\| … |\r\n| `search`       | `string` | —            | Substring match on title or input_text             |\r\n| `sort`         | `string` | `created_at` | Sort field                                         |\r\n| `dir`          | `string` | `desc`       | `asc` \\| `desc`                                    |\r\n| `limit`        | `number` | `10`         | Max results (1–200)                                |\r\n| `offset`       | `number` | `0`          | Pagination offset                                  |\r\n\r\nReturns: `{ runs: Run[], total: number, limit: number, offset: number }`\r\n\r\n#### `runs.cancel(id)`\r\n\r\nCancel an active run.\r\n\r\n```js\r\nawait af.runs.cancel(\"run_...\");\r\n```\r\n\r\nReturns: `{ ok: true }`  \r\nThrows: `AgentFlowError(409)` if the run is already in a terminal state.\r\n\r\n#### `runs.rerun(id, opts?)`\r\n\r\nRerun a completed run using the same or overridden input.\r\n\r\n```js\r\nconst run = await af.runs.rerun(\"run_...\", {\r\n  mode: \"snapshot\", // 'snapshot' | 'latest' (default: 'snapshot')\r\n  input_override: {\r\n    // shallow-merged onto source run's input_json\r\n    ticker: \"NVDA\",\r\n  },\r\n});\r\n```\r\n\r\n| Option           | Type     | Default    | Description                                                                 |\r\n| ---------------- | -------- | ---------- | --------------------------------------------------------------------------- |\r\n| `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 |\r\n| `input_override` | `object` | —          | Partial input to override on the source run's `input_json`                  |\r\n\r\nReturns: `Run` (the newly created run)  \r\nThrows: `AgentFlowError(409)` if the source run is still active.\r\n\r\n#### `runs.events(runId, opts?)`\r\n\r\nStream live events from a run as an async iterable. Automatically terminates when a terminal event (`run.finished`, `run.failed`, `run.cancelled`) is received.\r\n\r\n```js\r\nfor await (const event of af.runs.events(\"run_...\")) {\r\n  console.log(event.type, event.data);\r\n}\r\n```\r\n\r\nWith abort support:\r\n\r\n```js\r\nconst controller = new AbortController();\r\nsetTimeout(() => controller.abort(), 60_000); // 60-second timeout\r\n\r\nfor await (const event of af.runs.events(\"run_...\", {\r\n  signal: controller.signal,\r\n})) {\r\n  console.log(event.type, event.data);\r\n}\r\n```\r\n\r\nEach yielded event:\r\n\r\n```js\r\n{\r\n  id: 'evt_...',       // SSE event ID (or null)\r\n  type: 'task.done',   // event type string\r\n  data: { ... }        // parsed JSON payload (or raw string if not JSON)\r\n}\r\n```\r\n\r\nCommon event types:\r\n\r\n| Type            | Description                                    |\r\n| --------------- | ---------------------------------------------- |\r\n| `run.started`   | Run has been accepted and is starting          |\r\n| `run.planning`  | Plan is being generated                        |\r\n| `task.started`  | A task has started executing                   |\r\n| `task.stream`   | Incremental LLM output chunk for a task        |\r\n| `task.done`     | A task completed successfully                  |\r\n| `task.failed`   | A task failed                                  |\r\n| `run.finished`  | All tasks complete — **terminal, stream ends** |\r\n| `run.failed`    | Run failed — **terminal, stream ends**         |\r\n| `run.cancelled` | Run was cancelled — **terminal, stream ends**  |\r\n\r\n| Option        | Type          | Default | Description                              |\r\n| ------------- | ------------- | ------- | ---------------------------------------- |\r\n| `history`     | `boolean`     | `true`  | Replay persisted events before live tail |\r\n| `signal`      | `AbortSignal` | —       | Cancellation signal                      |\r\n| `lastEventId` | `string`      | —       | Resume SSE stream from this event ID     |\r\n\r\n#### `runs.downloadArtifact(runId, artifactId, opts?)`\r\n\r\nDownload a run artifact as a `Buffer`.\r\n\r\n```js\r\nconst { buffer, filename, contentType } = await af.runs.downloadArtifact(\r\n  \"run_...\",\r\n  \"art_...\",\r\n);\r\nfs.writeFileSync(filename ?? \"output.bin\", buffer);\r\n```\r\n\r\n| Option   | Type          | Description         |\r\n| -------- | ------------- | ------------------- |\r\n| `signal` | `AbortSignal` | Cancellation signal |\r\n\r\nReturns: `{ buffer: Buffer, filename: string | null, contentType: string | null }`\r\n\r\n---\r\n\r\n## Error Handling\r\n\r\nAll errors thrown by the SDK are instances of `AgentFlowError` (or its subclass `AgentFlowQuotaError`).\r\n\r\n```js\r\nconst {\r\n  AgentFlow,\r\n  AgentFlowError,\r\n  AgentFlowQuotaError,\r\n} = require(\"@agflowai/sdk\");\r\n\r\ntry {\r\n  const { run } = await af.runs.create({ workspace_id: \"wks_...\" });\r\n} catch (err) {\r\n  if (err instanceof AgentFlowQuotaError) {\r\n    console.error(`Quota exceeded: ${err.code}`); // e.g. QUOTA_RUNS_MONTHLY\r\n    console.error(`Limit: ${err.limit}`);\r\n    console.error(`Upgrade: ${err.upgradeUrl}`);\r\n  } else if (err instanceof AgentFlowError) {\r\n    console.error(`API error ${err.status}: ${err.message}`); // e.g. 404 Not found\r\n    console.error(`Code: ${err.code}`); // optional machine-readable code\r\n  } else {\r\n    throw err; // network error etc.\r\n  }\r\n}\r\n```\r\n\r\n### `AgentFlowError`\r\n\r\n| Property  | Type                  | Description                          |\r\n| --------- | --------------------- | ------------------------------------ |\r\n| `message` | `string`              | Human-readable error message         |\r\n| `status`  | `number`              | HTTP status code                     |\r\n| `code`    | `string \\| undefined` | Machine-readable error code from API |\r\n\r\n### `AgentFlowQuotaError` extends `AgentFlowError`\r\n\r\nThrown on `429` responses with a `QUOTA_*` code.\r\n\r\n| Property     | Type                  | Description                       |\r\n| ------------ | --------------------- | --------------------------------- |\r\n| `limit`      | `number`              | The quota limit that was exceeded |\r\n| `upgradeUrl` | `string \\| undefined` | URL to the upgrade/billing page   |\r\n\r\n---\r\n\r\n## TypeScript\r\n\r\nTypeScript declarations are bundled. No `@types/` package needed.\r\n\r\n```ts\r\nimport { AgentFlow, AgentFlowError, AgentFlowQuotaError } from '@agflowai/sdk';\r\nimport type { Run, Task, Artifact, RunEvent } from '@agflowai/sdk';\r\n\r\nconst af = new AgentFlow({\r\n  baseUrl: process.env.AGFLOW_API_URL!,\r\n  apiKey: process.env.AGFLOW_API_KEY!,\r\n  orgId: process.env.AGFLOW_ORG_ID!,\r\n});\r\n\r\nconst { run }: { run: Run } = await af.runs.create({\r\n  workspace_id: 'wks_...',\r\n  input_text: 'Hello',\r\n});\r\n\r\nfor await (const event: RunEvent of af.runs.events(run.id)) {\r\n  console.log(event.type);\r\n}\r\n```\r\n\r\n---\r\n\r\n## Examples\r\n\r\nAll examples live in [`examples/`](./examples/). Each file is self-contained and runnable with `node`. Set the environment variables described in each file's header before running.\r\n\r\n### Common setup\r\n\r\nEvery example reads these env vars:\r\n\r\n```bash\r\nexport AGFLOW_API_URL=https://api.yourdomain.com\r\nexport AGFLOW_API_KEY=af_live_...\r\nexport AGFLOW_ORG_ID=00000000-0000-0000-0000-000000000000\r\n```\r\n\r\n---\r\n\r\n### [`start-run.js`](./examples/start-run.js) — Full end-to-end run\r\n\r\n**Use case:** Kick off a single run from scratch and wait for it to finish.\r\n\r\nCovers:\r\n\r\n- Creating a workspace from a `template_slug`\r\n- Dispatching a run with `input_text`\r\n- Polling with `runs.get()` until the run reaches a terminal status\r\n- Printing task results and downloading artifacts\r\n- Displaying estimated cost\r\n\r\n```bash\r\nTEMPLATE_SLUG=investment-analyst-desk node examples/start-run.js\r\n```\r\n\r\n---\r\n\r\n### [`stream-events.js`](./examples/stream-events.js) — Live event streaming\r\n\r\n**Use case:** Watch a run's progress in real time, event by event.\r\n\r\nCovers:\r\n\r\n- Starting a new run **or** rerunning an existing one (set `RERUN_ID`)\r\n- Consuming the SSE event stream with `for await ... of af.runs.events()`\r\n- Graceful Ctrl-C cancellation via `AbortController`\r\n- Handling `AbortError` cleanly\r\n\r\n```bash\r\nWORKSPACE_ID=<uuid> node examples/stream-events.js\r\n\r\n# or rerun an existing run:\r\nRERUN_ID=<run-uuid> node examples/stream-events.js\r\n```\r\n\r\n---\r\n\r\n### [`list-runs.js`](./examples/list-runs.js) — Run history and cost report\r\n\r\n**Use case:** Audit or report on runs in a workspace — useful for dashboards, billing checks, or validating a batch.\r\n\r\nCovers:\r\n\r\n- Paginating through `runs.list()` with `limit` / `offset`\r\n- Filtering by `status` (e.g. `STATUS=done`)\r\n- Printing a summary table with status, cost, and title\r\n- Aggregating total cost and token usage across all runs\r\n\r\n```bash\r\nWORKSPACE_ID=<uuid> node examples/list-runs.js\r\n\r\n# filter to only completed runs:\r\nSTATUS=done WORKSPACE_ID=<uuid> node examples/list-runs.js\r\n```\r\n\r\n---\r\n\r\n### [`batch-runs.js`](./examples/batch-runs.js) — Parallel fan-out\r\n\r\n**Use case:** Process a list of inputs concurrently — one run per input — and collect all results.  \r\nCommon pattern for per-ticker analysis, per-client reports, or bulk document processing.\r\n\r\nCovers:\r\n\r\n- Dispatching N runs simultaneously with `Promise.allSettled`\r\n- Polling all in-flight runs in parallel until every one settles\r\n- Handling partial failures (some succeed, some fail)\r\n- Reporting a final cost and pass/fail summary\r\n\r\n```bash\r\n# Edit the INPUTS array in the file to match your workload, then:\r\nWORKSPACE_ID=<uuid> node examples/batch-runs.js\r\n```\r\n\r\n---\r\n\r\n### [`rerun-failed.js`](./examples/rerun-failed.js) — Batch retry failed runs\r\n\r\n**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.\r\n\r\nCovers:\r\n\r\n- Listing runs filtered to `status: 'failed'`\r\n- `DRY_RUN=1` mode to preview what would be rerun before committing\r\n- Fan-out of `runs.rerun()` calls with `Promise.allSettled`\r\n- Reporting which reruns succeeded and which errored\r\n\r\n```bash\r\n# Preview what would be rerun (no mutations):\r\nDRY_RUN=1 WORKSPACE_ID=<uuid> node examples/rerun-failed.js\r\n\r\n# Execute:\r\nWORKSPACE_ID=<uuid> node examples/rerun-failed.js\r\n```\r\n\r\n---\r\n\r\n## Running Tests\r\n\r\n```bash\r\ncd packages/sdk\r\nnpm test\r\n```\r\n\r\nUses the Node.js built-in test runner (`node:test`). No extra dependencies.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md","_rev":"1-3412f29a9dd9eef9627236159a1580bf"}