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

@runctl/sdk

v0.1.8

Published

The official TypeScript/JavaScript SDK for Runctl

Readme

@runctl/sdk

The official TypeScript/JavaScript SDK for Runctl — a multi-tenant runtime and scheduler for AI agents, built for production, not prototypes.

npm version TypeScript

Features

  • 🤖 Agent Management — Create, deploy, scale, and monitor AI agents
  • 📋 Task Orchestration — Submit tasks, batch operations, and track execution
  • 🔍 Trace Analysis & Forking — Inspect execution steps, fork tasks from any point, and compare task divergences
  • 👥 Team Coordination — Organize agents into teams for collaborative workflows
  • 🔄 Workflow Automation — Define and execute multi-step agent workflows
  • 📊 Usage & Billing — Monitor usage, costs, and manage billing
  • 📈 Self-Improvement Analytics — Track agent performance, approval rates, and optimization metrics
  • 🔒 Type-Safe — Full TypeScript support with comprehensive type definitions
  • Automatic Retries — Built-in retry logic with exponential backoff
  • 🌐 Environment Support — Works in Node.js, Deno, and edge runtimes

Installation

npm install @runctl/sdk
# or
yarn add @runctl/sdk
# or
pnpm add @runctl/sdk

Quick Start

import { Runctl } from '@runctl/sdk';

// Initialize the client
const client = new Runctl({
  apiKey: process.env.RUNCTL_API_KEY!,
});

// Create an agent
const agent = await client.agents.create({
  name: 'Research Assistant',
  type: 'reactive',
  modelProvider: 'openai',
  model: 'gpt-4o-mini',
  systemPrompt: 'You are a helpful research assistant.',
});

// Deploy the agent
await client.agents.deploy(agent.id, { replicas: 1 });

// Create a task
const task = await client.tasks.create({
  agentId: agent.id,
  input: {
    version: '1.0',
    payload: { query: 'What are the latest trends in AI?' },
  },
});

// Wait for completion
const completedTask = await client.tasks.waitForCompletion(task.id);
console.log('Result:', completedTask.result);

Configuration

Direct Configuration

const client = new Runctl({
  apiKey: 'your-api-key', // Required
  baseUrl: 'https://api.runctl.com', // Optional, defaults to production
  timeout: 30000, // Request timeout in ms (default: 30000)
  retries: 3, // Max retry attempts (default: 3)
  headers: {
    // Custom headers
    'X-Custom-Header': 'value',
  },
});

Environment Variables

// Automatically reads from environment variables
const client = Runctl.fromEnv();

Supported environment variables:

| Variable | Description | Default | | ----------------- | ----------------------- | ------------------------ | | RUNCTL_API_KEY | Your API key (required) | — | | RUNCTL_BASE_URL | API base URL | https://api.runctl.com | | RUNCTL_TIMEOUT | Request timeout in ms | 30000 | | RUNCTL_RETRIES | Max retry attempts | 3 |

Agents

Agents are the core building blocks of Runctl. They represent AI-powered workers that can execute tasks.

Create an Agent

const agent = await client.agents.create({
  name: 'Code Reviewer',
  type: 'reactive',
  modelProvider: 'openai',
  model: 'gpt-4o',
  systemPrompt:
    'You are an expert code reviewer. Analyze code for bugs, security issues, and best practices.',
  tools: ['code_analysis', 'security_scan'],
  resources: {
    memoryMB: 512,
    cpuUnits: 256,
  },
});

Custom tools

You can attach custom tools to an agent by passing full AgentTool objects in tools (instead of built-in tool names). Custom tools are invoked by the runtime when the agent calls them: the runtime POSTs the validated arguments to your endpoint and returns the response.

Each custom tool must have exactly one execution target:

  • invokeUrl — HTTPS URL; the runtime POSTs the tool arguments as JSON (supported today).
  • lambdaArn — AWS Lambda ARN (when supported by the runtime).
  • inlineScript — Inline script execution (when supported by the runtime).

Use the same client.agents.create() and client.agents.update() APIs; no extra SDK endpoints are required. The AgentTool type is exported from the SDK.

import type { AgentTool } from '@runctl/sdk';

const customTool: AgentTool = {
  id: 'tool_1',
  name: 'my_api_tool',
  description: 'Calls my external API',
  inputSchema: {
    type: 'object',
    properties: { query: { type: 'string' } },
    required: ['query'],
  },
  enabled: true,
  invokeUrl: 'https://api.example.com/invoke',
};

const agent = await client.agents.create({
  name: 'Agent with custom tool',
  type: 'reactive',
  modelProvider: 'openai',
  model: 'gpt-4o-mini',
  systemPrompt: 'You are a helpful assistant.',
  tools: [customTool],
});

Custom tool names must not conflict with built-in tool names. Tenant settings (e.g. skillAllowlist, customToolUrlPrefixes) may restrict which tools and URLs are allowed.

List Agents

// List all agents
const { data: agents, pagination } = await client.agents.list();

// With filters
const { data: runningAgents } = await client.agents.list({
  status: 'running',
  type: 'reactive',
  limit: 10,
  page: 1,
});

Deploy & Scale

// Deploy an agent
const deployment = await client.agents.deploy(agentId, {
  replicas: 2,
  strategy: 'rolling',
});

// Scale up/down
const scaleResult = await client.agents.scale(agentId, {
  desiredReplicas: 5,
  strategy: 'immediate',
});

// Stop an agent
await client.agents.stop(agentId);

// Restart
await client.agents.restart(agentId);

Monitor Agents

// Get agent metrics
const metrics = await client.agents.getMetrics(agentId, {
  period: 'day',
});

// Get agent logs
const { logs, hasMore } = await client.agents.getLogs(agentId, {
  level: 'error',
  limit: 100,
});

// List instances
const { data: instances } = await client.agents.listInstances(agentId);

// Get instance health
const health = await client.agents.getInstanceHealth(agentId, instanceId);

Tasks

Tasks represent units of work assigned to agents.

Create Tasks

// Single task
const task = await client.tasks.create({
  agentId: 'agt_123',
  input: {
    version: '1.0',
    payload: { message: 'Analyze this code', code: '...' },
  },
  priority: 'high',
});

// Batch create (up to 25 tasks)
const tasks = await client.tasks.createBatch({
  tasks: [
    { agentId: 'agt_123', input: { version: '1.0', payload: { query: 'Query 1' } } },
    { agentId: 'agt_123', input: { version: '1.0', payload: { query: 'Query 2' } } },
  ],
});

Wait for Completion

// Wait with default options (5 minute timeout)
const completed = await client.tasks.waitForCompletion(taskId);

// With progress updates
const completed = await client.tasks.waitForCompletion(taskId, {
  pollIntervalMs: 2000,
  timeoutMs: 600000, // 10 minutes
  onPoll: task => {
    console.log(`Status: ${task.status}`);
  },
});

List & Filter Tasks

// List with filters
const { data: tasks } = await client.tasks.list({
  status: 'completed',
  agentId: 'agt_123',
  fromDate: new Date('2026-01-01'),
  toDate: new Date(),
});

// Iterate over all matching tasks (auto-pagination)
for await (const task of client.tasks.listAll({ status: 'failed' })) {
  console.log(`Failed task: ${task.id}`);
}

Fork Tasks

Fork a new task from a specific execution step. This is useful for "What-if" debugging or re-running from a specific state.

// Fork from step 2 of an existing trace
const forkedTask = await client.tasks.fork('tsk_abc123', 2);

// Fork with a custom modified snapshot (What-if scenario)
const whatIfTask = await client.tasks.fork('tsk_abc123', 2, {
  prompt: 'A slightly different prompt to test...',
  memory: {
    customValue: 'something new',
  },
});

Task Statistics

// Get overall stats
const stats = await client.tasks.getStats();
console.log(`Total tasks: ${stats.total}`);
console.log(`By status:`, stats.byStatus);

// Get execution history
const history = await client.tasks.getExecutionHistory('7d');

Teams

Teams allow you to coordinate multiple agents for collaborative tasks.

Create a Team

const team = await client.teams.create({
  name: 'Research Team',
  description: 'A team for research and analysis tasks',
  members: [
    { agentId: 'agt_researcher', role: 'researcher' },
    { agentId: 'agt_analyzer', role: 'analyzer' },
    { agentId: 'agt_writer', role: 'writer' },
  ],
});

Manage Team Members

// Add a member
await client.teams.addMember(teamId, {
  agentId: 'agt_reviewer',
  role: 'reviewer',
});

// Remove a member
await client.teams.removeMember(teamId, 'agt_reviewer');

// List members
const members = await client.teams.listMembers(teamId);

Deploy Teams

// Deploy all team members
await client.teams.deploy(teamId);

// Stop all team members
await client.teams.stop(teamId);

// Get team metrics
const metrics = await client.teams.getMetrics(teamId);

Workflows

Workflows define multi-step processes executed by agents.

Create a Workflow Definition

const definition = await client.workflowDefinitions.create({
  name: 'Content Pipeline',
  description: 'Research, write, and review content',
  pattern: 'sequential',
  steps: [
    {
      stepId: 'research',
      name: 'Research Topic',
      agentId: 'agt_researcher',
    },
    {
      stepId: 'write',
      name: 'Write Draft',
      agentId: 'agt_writer',
      dependsOn: ['research'],
    },
    {
      stepId: 'review',
      name: 'Review & Edit',
      agentId: 'agt_editor',
      dependsOn: ['write'],
    },
  ],
});

Execute Workflows

// Start a workflow
const workflow = await client.workflows.execute({
  workflowDefinitionId: 'wfd_123',
  input: { topic: 'AI in Healthcare' },
});

// Execute and wait for completion
const completed = await client.workflows.executeAndWait(
  {
    workflowDefinitionId: 'wfd_123',
    input: { topic: 'AI in Healthcare' },
  },
  {
    timeoutMs: 600000,
    onUpdate: state => {
      console.log(`Step: ${state.currentStepId}, Status: ${state.status}`);
    },
  }
);

// Check workflow status
if (client.workflows.isCompleted(completed)) {
  console.log('Output:', completed.output);
} else if (client.workflows.isFailed(completed)) {
  console.log('Error:', completed.error);
}

Monitor Workflows

// Get workflow details
const workflow = await client.workflows.getById('wfl_123');

// Get step details
const steps = await client.workflows.getSteps('wfl_123');
steps.forEach(step => {
  console.log(`${step.stepId}: ${step.status}`);
});

// Get workflow metrics
const metrics = await client.workflows.getMetrics('wfl_123');
console.log(`Duration: ${metrics.durationSeconds}s`);
console.log(`Completed: ${metrics.stepCounts.completed}/${metrics.stepCounts.total}`);

// Cancel a running workflow
await client.workflows.cancel('wfl_123');

Tenants

Manage your tenant account, usage, billing, and team members.

Usage & Metrics

// Get current usage
const usage = await client.tenants.getUsage(tenantId);
console.log(`Active agents: ${usage.activeAgents}`);
console.log(`Tasks today: ${usage.tasksToday}`);

// Get usage history
const history = await client.tenants.getUsageHistory(tenantId, { period: '30d' });

// Get token usage history
const tokens = await client.tenants.getTokenUsageHistory(tenantId, { period: '7d' });

// Get cost history
const costs = await client.tenants.getCostHistory(tenantId, { period: '30d' });

Billing

// Get billing summary
const billing = await client.tenants.getBillingSummary(tenantId);
console.log(`Current spend: $${billing.currentSpend}`);
console.log(`Period: ${billing.periodStart} to ${billing.periodEnd}`);

// Create a billing portal session
const portal = await client.tenants.createPortalSession(tenantId, {
  returnUrl: 'https://app.example.com/billing',
});
// Redirect user to portal.portalUrl

// Create a checkout session for payment setup
const checkout = await client.tenants.createCheckoutSession(tenantId, {
  returnUrl: 'https://app.example.com/success',
  cancelUrl: 'https://app.example.com/cancel',
});

Team Members

// List members
const members = await client.tenants.listMembers(tenantId);

// Update member role
await client.tenants.updateMemberRole(tenantId, userId, {
  role: 'admin',
});

// Remove member
await client.tenants.removeMember(tenantId, userId);

// Create invite code
const invite = await client.tenants.createInviteCode(tenantId, {
  role: 'member',
  expiresInDays: 30,
  maxUses: 10,
});
console.log(`Invite code: ${invite.inviteCode}`);

// List and revoke invite codes
const codes = await client.tenants.listInviteCodes(tenantId);
await client.tenants.revokeInviteCode(tenantId, 'ABC123XYZ');

Notification Preferences

// Get preferences
const prefs = await client.tenants.getNotificationPreferences(tenantId);

// Update preferences
await client.tenants.updateNotificationPreferences(tenantId, {
  emailEnabled: true,
  emailRecipients: ['[email protected]'],
  thresholds: [50, 80, 100],
  webhookEnabled: true,
  webhookUrl: 'https://example.com/webhooks/billing',
});

Error Handling

The SDK provides typed errors for different failure scenarios:

import {
  RunctlError,
  RunctlAuthenticationError,
  RunctlAuthorizationError,
  RunctlValidationError,
  RunctlNotFoundError,
  RunctlRateLimitError,
  RunctlQuotaExceededError,
  RunctlServerError,
  RunctlNetworkError,
  isRetryableError,
} from '@runctl/sdk';

try {
  const agent = await client.agents.getById('agt_invalid');
} catch (error) {
  if (error instanceof RunctlNotFoundError) {
    console.log(`Agent not found: ${error.resourceId}`);
  } else if (error instanceof RunctlAuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof RunctlRateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter} seconds`);
  } else if (error instanceof RunctlValidationError) {
    console.log('Validation error:', error.details);
  } else if (error instanceof RunctlQuotaExceededError) {
    console.log(`Quota exceeded: ${error.resource}`);
  } else if (isRetryableError(error)) {
    console.log('Transient error, safe to retry');
  }
}

Error Types

| Error | HTTP Code | Description | | --------------------------- | --------- | -------------------------- | | RunctlAuthenticationError | 401 | Invalid or missing API key | | RunctlAuthorizationError | 403 | Insufficient permissions | | RunctlValidationError | 400 | Invalid request parameters | | RunctlNotFoundError | 404 | Resource not found | | RunctlConflictError | 409 | Resource state conflict | | RunctlRateLimitError | 429 | Rate limit exceeded | | RunctlQuotaExceededError | 402 | Usage quota exceeded | | RunctlServerError | 5xx | Server-side error | | RunctlNetworkError | — | Network/connection error |

TypeScript Support

The SDK is written in TypeScript and provides comprehensive type definitions:

import type {
  Agent,
  Task,
  AgentTeam,
  WorkflowState,
  WorkflowDefinition,
  Tenant,
  AgentType,
  TaskStatus,
  WorkflowStatus,
  CreateAgentInput,
  CreateTaskInput,
  PaginatedResponse,
} from '@runctl/sdk';

// All method parameters and return types are fully typed
const agent: Agent = await client.agents.create({
  name: 'My Agent',
  type: 'reactive',
  modelProvider: 'openai',
  model: 'gpt-4o-mini',
  systemPrompt: 'You are helpful.',
});

// Pagination types
const response: PaginatedResponse<Task> = await client.tasks.list();

Advanced Usage

Custom HTTP Headers

const client = new Runctl({
  apiKey: 'your-api-key',
  headers: {
    'X-Request-ID': 'custom-request-id',
    'X-Correlation-ID': 'trace-123',
  },
});

Polling with Exponential Backoff

const workflow = await client.workflows.pollUntilComplete('wfl_123', {
  intervalMs: 1000, // Start with 1 second
  backoffFactor: 1.5, // Multiply by 1.5 each time
  maxIntervalMs: 30000, // Cap at 30 seconds
  timeoutMs: 600000, // 10 minute timeout
  maxRetries: 5, // Max consecutive failures
  onUpdate: state => {
    console.log(`Status: ${state.status}`);
  },
});

Iterate All Resources

// Auto-paginate through all tasks
for await (const task of client.tasks.listAll({
  status: 'completed',
  fromDate: new Date('2026-01-01'),
})) {
  // Process each task
  console.log(task.id);
}

Requirements

  • Node.js 20+ (or compatible runtime)
  • TypeScript 5.0+ (for TypeScript users)

License

Proprietary - All rights reserved. See your Runctl service agreement for terms of use.

Support