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

llmconveyors

v0.4.0

Published

Official TypeScript SDK for the LLM Conveyors API

Readme

llmconveyors

Official TypeScript SDK for the LLM Conveyors API.

Zero runtime dependencies. Full type safety. SSE streaming via async iterables. Node.js 18+.

Installation

npm install llmconveyors
# or
pnpm add llmconveyors
# or
yarn add llmconveyors

Setup

  1. Get an API key from Settings > API Keys in the web UI
  2. Create a .env file (see .env.example):
LLMC_API_KEY=llmc_your_api_key_here
  1. Initialize the client:
import { LLMConveyors } from 'llmconveyors';

const client = new LLMConveyors({
  apiKey: process.env.LLMC_API_KEY,
});

Usage

There are three ways to run a generation, from simplest to most control:

1. One call with run() (recommended)

Generates, streams, handles phased interactions, and returns the final result:

const result = await client.agents.run('job-hunter', {
  companyName: 'Stripe',
  jobTitle: 'Senior Backend Engineer',
  jobDescription: 'Build payment APIs...',
  autoSelectContacts: true,
}, {
  onProgress: (step, percent) => console.log(`${step}: ${percent}%`),
  onChunk: (chunk) => process.stdout.write(chunk),
});

console.log(result.success);   // true
console.log(result.artifacts);  // [{ type: 'cv', ... }, ...]

See examples/job-hunter.ts for a complete working example.

2. Manual streaming

For full control over each SSE event:

const { generationId } = await client.agents.generate('job-hunter', {
  companyName: 'Stripe',
  jobTitle: 'Senior Backend Engineer',
  jobDescription: 'Build payment APIs...',
});

for await (const event of client.stream.generation(generationId)) {
  switch (event.event) {
    case 'progress': console.log(`${event.data.step}: ${event.data.percent}%`); break;
    case 'chunk':    process.stdout.write(event.data.chunk); break;
    case 'complete': console.log('Done!', event.data.artifacts); break;
    case 'error':    console.error(event.data.code, event.data.message); break;
  }
}

See examples/manual-streaming.ts for phased interaction handling.

3. Polling (no SSE)

For environments where streaming is not available:

const { jobId } = await client.agents.generate('b2b-sales', {
  companyName: 'Notion',
  companyWebsite: 'https://notion.so',
});

const result = await client.agents.poll('b2b-sales', jobId, {
  interval: 3000,
  timeout: 120_000,
});

console.log(result.status);    // 'completed'
console.log(result.artifacts); // [{ type: 'cold-email', ... }]

See examples/b2b-sales.ts for a complete working example.

Agents

Job Hunter

Generates tailored CVs, cover letters, and cold emails for job applications.

const result = await client.agents.run('job-hunter', {
  companyName: 'Stripe',
  jobTitle: 'Senior Backend Engineer',
  jobDescription: 'Build payment APIs...',
  autoSelectContacts: true,    // skip contact selection (headless)
  // mode: 'cold_outreach',    // or 'standard' (default)
  // theme: 'modern',          // resume template
  // webhookUrl: 'https://...',
});

Supports phased workflows: Phase A researches and finds contacts, Phase B generates documents. With autoSelectContacts: true, both phases run automatically. Without it, provide an interactionHandler:

const result = await client.agents.run('job-hunter', body, {
  interactionHandler: async (interactionType, interactionData) => {
    // interactionData contains contact candidates
    // Return your selection
    return { selectedContacts: ['contact_id_1'] };
  },
});

B2B Sales

Generates personalized sales outreach for B2B prospecting.

const result = await client.agents.run('b2b-sales', {
  companyName: 'Notion',
  companyWebsite: 'https://notion.so',
  // model: 'pro',              // or 'flash' (default)
  // skipResearchCache: false,
  // webhookUrl: 'https://...',
});

Supports phased workflows with contact selection, same as Job Hunter.

Error Handling

All API errors are thrown as typed classes:

import { RateLimitError, ValidationError, LLMConveyorsError } from 'llmconveyors';

try {
  await client.agents.generate('job-hunter', body);
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log('Retry after:', err.retryAfter, 'seconds');
  } else if (err instanceof ValidationError) {
    console.log('Hint:', err.hint);
    console.log('Field errors:', err.details?.fieldErrors);
  } else if (err instanceof LLMConveyorsError) {
    console.log(err.code, err.isRetryable());
  }
}

The SDK automatically retries on RATE_LIMITED, CONCURRENT_GENERATION_LIMIT, AI_PROVIDER_ERROR, AI_RATE_LIMITED, SERVICE_UNAVAILABLE, GENERATION_TIMEOUT, SERVER_RESTARTING, and STREAM_ERROR with exponential backoff and jitter.

Webhook Verification

Verify incoming webhook signatures using your webhook secret (not your API key):

import { verifyWebhookSignature, parseWebhookEvent } from 'llmconveyors';

app.post('/webhook', (req, res) => {
  const isValid = verifyWebhookSignature(req.body, req.headers['x-webhook-signature'], secret);
  if (!isValid) return res.status(401).send('Invalid signature');

  const event = parseWebhookEvent(req.body);
  // event.event: 'generation.completed' | 'generation.failed' | 'generation.awaiting_input'
  // event.generationId, event.sessionId, event.agentType, event.timestamp (top-level)
  // event.data: { status, artifacts?, error?, interactionData? }
  res.sendStatus(200);
});

All Resources

| Namespace | Methods | |-----------|---------| | client.agents | generate, generateCv, getStatus, interact, getManifest, run, poll | | client.stream | generation, health | | client.sessions | create, list, get, hydrate, download, delete, init, stats, log, initGenerationLog | | client.upload | resume, jobFile, jobText | | client.resume | parse, validate, render, preview, themes, importRxResume, exportRxResume, getMaster, upsertMaster, deleteMaster | | client.ats | score | | client.settings | getProfile, getPreferences, updatePreferences, createApiKey, listApiKeys, revokeApiKey, rotateApiKey, getApiKeyUsage, getProviderKeyStatus, getSupportedProviders, setProviderKey, removeProviderKey, getUsageLogs, getUsageSummary, getWebhookSecret, rotateWebhookSecret | | client.privacy | listConsents, grantConsent, revokeConsent, exportData, deleteAccount | | client.documents | download | | client.logging | send | | client.health | root, check, ready, live | | client.content | save, deleteGeneration, researchSender, listSources, getSource, deleteSource | | client.shares | create, getStats, getPublic, recordVisit, getShareStats | | client.referral | getStats, getCode, setVanityCode |

Configuration

const client = new LLMConveyors({
  apiKey: 'llmc_...',                // Required, must start with llmc_
  baseUrl: 'https://api.llmconveyors.com/api/v1',  // Default
  maxRetries: 3,                     // Default, for retryable errors
  timeout: 30_000,                   // Default, ms per request
  debug: (msg) => console.log(msg),  // Optional debug logger
});

Custom auth (Bearer tokens, SuperTokens, rotating sessions)

For flows where you do not have a static llmc_ API key — browser sessions, Chrome extensions, server-to-server OAuth — pass a getAuthHeaders callback instead of apiKey. The SDK calls it before every request (and every SSE reconnect), so token rotation is transparent:

const client = new LLMConveyors({
  baseUrl: 'https://api.llmconveyors.com/api/v1',
  getAuthHeaders: async () => ({
    Authorization: `Bearer ${await getAccessToken()}`,
  }),
});

On 401, the SDK calls the callback a second time and retries the request once if the returned headers differ — so a refresh you do inside the callback is picked up automatically. If the headers are unchanged, the 401 surfaces as UnauthorizedError with no retry loop. The SDK stores zero auth state: token lifecycle, storage, and refresh coordination all live in your code.

Examples

Working examples in the examples/ directory:

| File | What it shows | |------|---------------| | job-hunter.ts | Full run() workflow with callbacks and interaction handling | | b2b-sales.ts | Generate + poll + session hydration | | manual-streaming.ts | Raw SSE for await loop with manual interaction | | smoke-test.ts | Quick connectivity test against all read endpoints |

Run any example:

cp .env.example .env
# Edit .env with your API key
npx tsx examples/job-hunter.ts

Requirements

  • Node.js 18+ (uses native fetch, crypto, ReadableStream)
  • Zero runtime dependencies

License

MIT