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

@openelan/elan-node-sdk

v0.2.38

Published

Elan Integration SDK — Node.js client for the Elan Integration Service sessions API

Downloads

1,049

Readme

@openelan/elan-node-sdk

Node.js SDK for the Elan Integration Service and IAM auth APIs.

Installation

npm install @openelan/elan-node-sdk

Quick Start

import { ElanIntegrationClient } from "@openelan/elan-node-sdk";

const client = new ElanIntegrationClient({
  integration: {
    baseUrl: "https://api.elan.ai",
    clientId: "your-client-id",
    clientSecret: "your-client-secret",
    timeoutMs: 10000, // optional, defaults to 10 000 ms
  },
  auth: {
    iamBaseUrl: "https://iam.elan.ai", // optional, falls back to integration.baseUrl
    timeoutMs: 5000,                   // optional, falls back to integration.timeoutMs
  },
});

const userId = "end-user-id";

// --- Swarm (session) operations ---

// Create a session
const { sessionId } = await client.swarm.createSession(userId);

// Send a plain-text message (async, returns accepted acknowledgement)
const response = await client.swarm.sendMessage(userId, sessionId, "Hello, agent!");
console.log(response.status); // "accepted"

// Send a message and wait for the final response in one call
const reply = await client.swarm.sendSync(userId, "What is 6 × 7?");
console.log(reply.finalText); // "42"

// --- Auth operations ---

// Introspect an access token server-side
const claims = await client.auth.introspectSession(accessToken);
console.log(claims.sub);   // userId
console.log(claims.orgId); // organization ID

// Request email verification
await client.auth.requestEmailVerification("[email protected]");

API Reference

new ElanIntegrationClient(config)

| Field | Type | Required | Description | |----------------------------|----------|----------|--------------------------------------------------------------------------| | integration.baseUrl | string | yes | Base URL of the Elan Integration Service | | integration.clientId | string | yes | IAM swarm client ID | | integration.clientSecret | string | yes | IAM swarm client secret | | integration.timeoutMs | number | no | Per-request timeout in ms (default: 10000) | | auth.iamBaseUrl | string | no | Base URL of the Elan IAM service (defaults to integration.baseUrl) | | auth.timeoutMs | number | no | Per-request timeout for auth calls (defaults to integration.timeoutMs) |

The constructor exposes two namespaced sub-clients:

  • client.swarm — Integration Service sessions, file upload, and sync APIs
  • client.auth — IAM auth and admin endpoints

client.swarm — Integration / Session Methods

| Method | Returns | |--------------------------------------------------------------|----------------------------| | createSession(userId, opts?) | ElanCreateSessionResponse | | listSessions(userId) | ElanSession[] | | getSession(userId, sessionId, opts?) | ElanSession | | deleteSession(userId, sessionId) | void | | sendMessage(userId, sessionId, message, opts?) | ElanSendAcceptedResponse | | sendMessage(userId, sessionId, parts, opts?) | ElanSendAcceptedResponse | | initUpload(userId, sessionId, body) | ElanInitUploadData | | downloadFile(userId, fileUid) | Response | | sendSync(userId, message, opts?) | ElanSendSyncResponse | | sendSync(userId, parts, opts?) | ElanSendSyncResponse |

createSession accepts an optional extraContext object that is forwarded as the x-extra-context request header.

getSession accepts an optional numRecentEvents number (1–200).

sendMessage accepts either a plain string (sends { message }) or an ElanSendPart[] array (sends { parts }). Both forms accept an optional extra key-value map forwarded to the runtime.

Structured parts (ElanSendPart)

| Variant | Required fields | Optional fields | |----------------------------|-----------------------------------------------|-----------------| | ElanSendTextPart | type: "text", text | — | | ElanSendFilePart | type: "file", fileName, mimeType, url | urls | | ElanSendToolCallPart | type: "toolCall", name, args | callId | | ElanSendToolResponsePart | type: "toolResponse", name, response | callId |

import type { ElanSendPart } from "@openelan/elan-node-sdk";

// Plain text
await client.swarm.sendMessage(userId, sessionId, "Hello, agent!");

// Structured text part
const textPart: ElanSendPart = { type: "text", text: "Hello, agent!" };
await client.swarm.sendMessage(userId, sessionId, [textPart]);

// File attachment
const filePart: ElanSendPart = {
  type: "file",
  fileName: "report.pdf",
  mimeType: "application/pdf",
  url: "https://example.com/report.pdf",
};
await client.swarm.sendMessage(userId, sessionId, [filePart]);

// Tool call
const toolCallPart: ElanSendPart = {
  type: "toolCall",
  name: "searchWeb",
  args: { query: "elan AI" },
  callId: "call-1",
};
await client.swarm.sendMessage(userId, sessionId, [toolCallPart]);

// Tool response
const toolResponsePart: ElanSendPart = {
  type: "toolResponse",
  name: "searchWeb",
  response: { results: ["..."] },
  callId: "call-1",
};
await client.swarm.sendMessage(userId, sessionId, [toolResponsePart]);

// Mixed parts with extra metadata
await client.swarm.sendMessage(userId, sessionId, [
  { type: "text", text: "See attached" },
  { type: "file", fileName: "img.png", mimeType: "image/png", url: "https://example.com/img.png" },
], { extra: { correlationId: "abc123" } });

sendSync — synchronous response

sendSync posts the message and waits for the agent to finish its turn, returning finalText, content, and eventMetadata in a single call. No session ID is needed — the runtime manages ephemeral sessions automatically.

import type { ElanSendSyncResponse } from "@openelan/elan-node-sdk";

// Plain-text mode
const resp: ElanSendSyncResponse = await client.swarm.sendSync(userId, "What is 6 × 7?");
console.log(resp.finalText);     // "42"
console.log(resp.content);       // { parts: [{ text: "42" }], role: "model" } | null
console.log(resp.eventMetadata); // { invocationId: "…" } | null

// Structured-parts mode
import type { ElanSendPart } from "@openelan/elan-node-sdk";
const parts: ElanSendPart[] = [{ type: "text", text: "Summarise this document." }];
const resp2 = await client.swarm.sendSync(userId, parts);
console.log(resp2.finalText);

// With extra metadata
const resp3 = await client.swarm.sendSync(userId, "Hello", { extra: { correlationId: "req-1" } });

client.auth — IAM / Auth / Admin Methods

All auth methods throw ElanIntegrationError on non-2xx responses or request timeout.

Session lifecycle

| Method | Signature | Returns | |--------------------------|-------------------------------------------|--------------------------| | exchangeCodeForSession | (params: ElanExchangeCodeParams) | ElanSessionResponse | | refreshSession | (params?: ElanRefreshSessionParams) | ElanSessionResponse | | logoutSession | (params?: ElanLogoutSessionParams) | void | | introspectSession | (accessToken: string) | ElanIntrospectResponse |

Email verification

| Method | Signature | Returns | |----------------------------|-----------------------|----------------------------------------| | requestEmailVerification | (email: string) | ElanRequestEmailVerificationResponse | | verifyEmail | (token: string) | ElanVerifyEmailResponse |

Password management

| Method | Signature | Returns | |------------------|-----------------------------------------------------------------|------------------------------| | changePassword | (currentPassword, newPassword, accessToken) | ElanChangePasswordResponse | | forgetPassword | (email, action: { redirectUri: string }) | ElanForgetPasswordResponse | | resetPassword | (code: string, newPassword: string) | ElanResetPasswordResponse |

User metadata

| Method | Signature | Returns | |---------------|-----------------------------------------------------|---------------------------| | setMetadata | (metadata: Record<string, string>, accessToken) | ElanSetMetadataResponse |

Organization selection

| Method | Signature | Returns | |----------------------|------------------------------------------------------------------|-----------------------| | selectOrganization | (loginChallengeToken, organizationSlug, requestedScopes?) | ElanSessionResponse | | switchOrganization | (organizationSlug, requestedScopes, accessToken) | ElanSessionResponse |

Admin

| Method | Signature | Returns | |-------------------|---------------------------------------------------------------|-------------------------------| | adminCreateUser | (params: ElanAdminCreateUserParams, accessToken) | ElanAdminCreatedUser | | setCustomClaims | (userId, customClaims: Record<string, string>, accessToken) | ElanSetCustomClaimsResponse |

Auth method examples

// Exchange a PKCE code after OAuth redirect
const session = await client.auth.exchangeCodeForSession({
  code: "auth-code",
  codeVerifier: "verifier",
  redirectUri: "https://app.example.com/callback",
  clientId: "my-client-id",
});

// Refresh an existing session
const refreshed = await client.auth.refreshSession({
  refreshToken: session.tokens.refreshToken,
});

// Introspect a token server-side
const claims = await client.auth.introspectSession(session.tokens.accessToken);
console.log(claims.sub, claims.orgId, claims.scopes);

// Logout a session (invalidates the refresh token)
await client.auth.logoutSession({ refreshToken: session.tokens.refreshToken });

// Request email verification (safe to call even when already verified)
await client.auth.requestEmailVerification("[email protected]");

// Confirm email with the one-time token from the verification email
await client.auth.verifyEmail("one-time-token-from-email");

// Change password (requires an active session)
await client.auth.changePassword("old-pass", "new-pass", accessToken);

// Initiate forgot-password flow (always returns { requested: true } to prevent enumeration)
await client.auth.forgetPassword("[email protected]", {
  redirectUri: "https://app.example.com/reset-password",
});

// Complete password reset with the one-time code from the reset email
await client.auth.resetPassword("reset-code", "new-password");

// Set arbitrary key-value metadata on the current user
await client.auth.setMetadata({ theme: "dark", locale: "en" }, accessToken);

// Select an org after a login challenge (multi-org users only)
// A login via your own flow returns { status: "ORG_SELECTION_REQUIRED", loginChallengeToken, organizations }
const authenticated = await client.auth.selectOrganization(
  loginChallengeToken,
  "my-org-slug",
  ["session:all"],
);

// Switch to a different org mid-session
const switched = await client.auth.switchOrganization(
  "other-org-slug",
  undefined,
  accessToken,
);

// Admin: create a new user in your organization
const user = await client.auth.adminCreateUser({
  email: "[email protected]",
  password: "Secure123",
  displayName: "New User",
  role: "DEVELOPER",
}, adminAccessToken);

// Admin: set custom claims on a user (fully replaces existing claims)
await client.auth.setCustomClaims(user.uid, { tier: "pro" }, adminAccessToken);

Webhook Verification

The Elan realtime service signs outbound callback payloads using HMAC-SHA256. Use the helper to verify inbound webhook requests in your callback handler:

import { verifyWebhookSignature } from "@openelan/elan-node-sdk";
import type { ElanWebhookPayload } from "@openelan/elan-node-sdk";
import type { Request, Response } from "express";

app.post("/webhook", (req: Request, res: Response) => {
  const valid = verifyWebhookSignature({
    headers: req.headers as Record<string, string>,
    body: req.body as ElanWebhookPayload,
    clientSecret: process.env.ELAN_CLIENT_SECRET!,
  });

  if (!valid) {
    res.status(401).send("Invalid signature");
    return;
  }

  // Process req.body as ElanWebhookPayload
  res.sendStatus(200);
});

Signature algorithm

signingInput = `${clientId}_${swarmId}_${nonce}`
signature    = base64(HMAC-SHA256(clientSecret, signingInput))

Headers delivered with each callback:

| Header | Description | |--------------------|------------------------------| | x-elan-signature | Base64 HMAC-SHA256 signature | | x-elan-nonce | Random 32-char hex nonce |

You can also compute a signature independently:

import { computeWebhookSignature } from "@openelan/elan-node-sdk";

const sig = computeWebhookSignature(clientSecret, clientId, swarmId, nonce);

Typed payload processing

After verifying the signature, cast the body to ElanWebhookPayload and work with the strongly-typed event tree:

import type {
  ElanWebhookPayload,
  ElanWebhookPayloadEvent,
  ElanEventContent,
  ElanEventContentPart,
} from "@openelan/elan-node-sdk";

Accessing content.parts

payload.payload.content is optional — it is only present on model-response events. Always guard before iterating:

app.post("/webhook", (req: Request, res: Response) => {
  // ... signature verification omitted for brevity

  const body = req.body as ElanWebhookPayload;
  const event: ElanWebhookPayloadEvent = body.payload;

  const parts: ElanEventContentPart[] = event.content?.parts ?? [];

  for (const part of parts) {
    if (part.text !== undefined) {
      console.log("text:", part.text);
    } else if (part.functionCall !== undefined) {
      console.log("functionCall:", part.functionCall.name, part.functionCall.args);
    } else if (part.functionResponse !== undefined) {
      console.log("functionResponse:", part.functionResponse.name, part.functionResponse.response);
    }
  }

  res.sendStatus(200);
});

Migration from v1 (flat API)

Prior versions of the SDK used a flat constructor and flat method calls directly on the client. The SDK now uses a namespace pattern. Update your code as follows:

Constructor

// Before (v1 — flat)
const client = new ElanIntegrationClient({
  baseUrl: "https://api.elan.ai",
  clientId: "my-client-id",
  clientSecret: "my-client-secret",
  timeoutMs: 10000,
});

// After (current — nested namespaces)
const client = new ElanIntegrationClient({
  integration: {
    baseUrl: "https://api.elan.ai",
    clientId: "my-client-id",
    clientSecret: "my-client-secret",
    timeoutMs: 10000, // optional
  },
  auth: {
    iamBaseUrl: "https://iam.elan.ai", // optional
  },
});

Method calls

| v1 (flat) | Current (namespaced) | |-----------------------------------------------|-----------------------------------------------------| | client.createSession(userId) | client.swarm.createSession(userId) | | client.listSessions(userId) | client.swarm.listSessions(userId) | | client.getSession(userId, sessionId, opts?) | client.swarm.getSession(userId, sessionId, opts?) | | client.deleteSession(userId, sessionId) | client.swarm.deleteSession(userId, sessionId) | | client.sendMessage(userId, sessionId, msg) | client.swarm.sendMessage(userId, sessionId, msg) | | client.sendSync(userId, msg) | client.swarm.sendSync(userId, msg) | | client.initUpload(userId, sessionId, body) | client.swarm.initUpload(userId, sessionId, body) | | client.downloadFile(userId, fileUid) | client.swarm.downloadFile(userId, fileUid) | | client.introspectSession(token) | client.auth.introspectSession(token) | | client.logoutSession(params?) | client.auth.logoutSession(params?) |