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

@agentforge-io/nest

v3.0.1

Published

NestJS bindings for AgentForge — `AgentForgeModule.forRoot/forRootAsync` plus controllers, guards and decorators. Wraps @agentforge-io/core.

Readme

AgentForge

Framework NestJS para construir micro-SaaS de Agents as a Service usando Claude Agent SDK.

AgentForge abstrae toda la complejidad de crear un sistema multi-agente con monetización, persistencia de conversaciones, procesamiento en background y billing con Stripe — para que te puedas enfocar solo en definir tus agentes y herramientas.


Características

  • Multi-agente con orquestación automática — Un agente orquestador puede delegar tareas a subagentes especializados. Claude decide cuándo y cómo delegar.
  • Persistencia de conversaciones en Postgres — Historial completo con TypeORM, incluyendo tool calls y uso de tokens.
  • Queue de tareas async con BullMQ + Redis — Procesa conversaciones largas en background, el cliente hace polling o recibe el resultado cuando está listo.
  • Billing con Stripe incluido — Planes freemium, suscripciones mensuales/anuales, pay-per-use y créditos. Un adaptador para cambiar de proveedor de pagos.
  • Tracking de uso por usuario — Límites de requests y tokens por plan, aplicados automáticamente.
  • Framework HTTP: NestJS — DI, decoradores, guards, SSE streaming, Swagger.
  • TypeScript nativo — Tipado completo, autocompletado total.

Instalación

npm install agentforge @nestjs/common @nestjs/core @nestjs/platform-express reflect-metadata rxjs

Requerimientos:

  • Node.js ≥ 18
  • PostgreSQL ≥ 14
  • Redis ≥ 6 (solo si usas queue)

Quickstart (5 minutos)

1. Configurar el módulo raíz

// app.module.ts
import { Module } from '@nestjs/common';
import { AgentForgeModule } from 'agentforge';

@Module({
  imports: [
    AgentForgeModule.forRoot({
      anthropic: {
        apiKey: process.env.ANTHROPIC_API_KEY!,
      },
      database: {
        url: process.env.DATABASE_URL!,
      },
      agents: [
        {
          id: 'assistant',
          name: 'Assistant',
          systemPrompt: 'You are a helpful assistant.',
        },
      ],
    }),
  ],
})
export class AppModule {}

2. Arrancar la app

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { rawBody: true });
  await app.listen(3000);
}
bootstrap();

3. Usar la API REST

AgentForge registra automáticamente las rutas:

# Crear una conversación
POST /conversations
{ "userId": "user_123", "agentId": "assistant" }

# Enviar un mensaje
POST /conversations/:id/messages
{ "userId": "user_123", "content": "Hello!" }

# Ver el historial
GET /conversations/:id?userId=user_123

# Listar conversaciones
GET /conversations?userId=user_123

# Stream de respuesta (Server-Sent Events)
GET /conversations/:id/stream?userId=user_123&content=Hello

Configuración completa

AgentForgeModule.forRoot({
  // ─── Anthropic ─────────────────────────────────────────────────────────
  anthropic: {
    apiKey: 'sk-ant-...',
    defaultModel: 'claude-opus-4-6',     // Modelo por defecto para todos los agentes
    defaultMaxTokens: 4096,
  },

  // ─── Base de datos ─────────────────────────────────────────────────────
  database: {
    url: 'postgresql://user:pass@localhost:5432/myapp',
    autoMigrate: true,   // Sincroniza el schema automáticamente (usa false en prod)
    logging: false,
  },

  // ─── Redis (necesario para queue) ──────────────────────────────────────
  redis: {
    host: 'localhost',
    port: 6379,
  },

  // ─── Queue async ───────────────────────────────────────────────────────
  queue: {
    enabled: true,
    concurrency: 5,
  },

  // ─── Billing ───────────────────────────────────────────────────────────
  billing: {
    provider: 'stripe',
    models: ['subscription', 'freemium'],
    appUrl: 'https://myapp.com',
    stripe: {
      secretKey: 'sk_live_...',
      webhookSecret: 'whsec_...',
    },
    plans: [
      {
        id: 'free',
        name: 'Free',
        isDefault: true,
        limits: { requestsPerMonth: 50, tokensPerMonth: 25_000 },
      },
      {
        id: 'pro',
        name: 'Pro',
        stripePriceId: 'price_xxx',
        priceInCents: 1900,
        interval: 'month',
        limits: { requestsPerMonth: 1000, tokensPerMonth: 1_000_000 },
        features: ['All agents', 'Priority support'],
      },
    ],
  },

  // ─── Agentes ───────────────────────────────────────────────────────────
  agents: [
    {
      id: 'assistant',
      name: 'General Assistant',
      systemPrompt: 'You are a helpful assistant.',
      model: 'claude-opus-4-6',
      tools: ['calculator', 'web_search'],
    },
  ],
})

Multi-Agente con Orquestación

Define un agente orquestador con canOrchestrate: true y lista sus subagentes. AgentForge inyecta automáticamente herramientas de delegación para que Claude sepa cuándo llamar a cada especialista.

agents: [
  // Especialistas
  {
    id: 'researcher',
    name: 'Research Specialist',
    systemPrompt: 'You are an expert researcher. Find and summarize information.',
    tools: ['web_search'],
  },
  {
    id: 'writer',
    name: 'Content Writer',
    systemPrompt: 'You are a professional writer. Create high-quality content.',
  },
  {
    id: 'analyst',
    name: 'Data Analyst',
    systemPrompt: 'You analyze data and provide insights.',
    tools: ['calculator', 'query_data'],
  },

  // Orquestador — coordina a los especialistas
  {
    id: 'orchestrator',
    name: 'AI Director',
    systemPrompt: `You coordinate specialist agents for complex tasks.
      Delegate research to the researcher, writing to the writer, and analysis to the analyst.
      Synthesize all results into a final comprehensive answer.`,
    canOrchestrate: true,
    subAgents: ['researcher', 'writer', 'analyst'],
  },
]

Cuando el usuario envía un mensaje al orquestador, Claude puede llamar a delegate_to_researcher, delegate_to_writer, etc. automáticamente.


Definir Tools

Opción 1: Función defineTool (recomendada)

import { defineTool, AgentForgeModule } from 'agentforge';

const weatherTool = defineTool({
  name: 'get_weather',
  description: 'Get current weather for a city',
  inputSchema: {
    type: 'object',
    properties: {
      city: { type: 'string', description: 'City name' },
    },
    required: ['city'],
  },
  // Opcional: restringir a agentes específicos
  agents: ['assistant', 'orchestrator'],
  execute: async ({ city }, context) => {
    // context tiene: userId, conversationId, agentId, messageId
    const data = await fetchWeatherAPI(city as string);
    return `${city}: ${data.temp}°C, ${data.condition}`;
  },
});

// Registrar en el módulo
AgentForgeModule.forRootAsync({
  useFactory: (config: ConfigService) => ({
    agents: [...],
    ...
  }),
  options: {
    tools: [weatherTool, calculatorTool, ...],
  },
})

Opción 2: Registro manual en tiempo de ejecución

import { ToolRegistryService } from 'agentforge';

@Injectable()
export class MyService {
  constructor(private readonly toolRegistry: ToolRegistryService) {}

  onModuleInit() {
    this.toolRegistry.register({
      name: 'my_tool',
      description: '...',
      inputSchema: { type: 'object', properties: {} },
      execute: async (input) => 'result',
    });
  }
}

Billing

API de Billing

# Ver planes disponibles
GET /billing/plans

# Crear sesión de checkout (Stripe Checkout)
POST /billing/checkout
{ "userId": "user_123", "planId": "pro" }
→ { "url": "https://checkout.stripe.com/..." }

# Portal de cliente (gestionar suscripción)
POST /billing/portal
{ "userId": "user_123" }
→ { "url": "https://billing.stripe.com/..." }

# Ver estado de billing del usuario
GET /billing/overview/user_123

# Cancelar suscripción
DELETE /billing/subscription/user_123

# Webhook de Stripe
POST /billing/webhook  (con header stripe-signature)

Agregar créditos

billing: {
  models: ['credits'],
  credits: {
    tokensPerCredit: 1000,    // 1 crédito = 1000 tokens
    creditsPerRequest: 1,      // +1 crédito por request
    initialCredits: 100,       // Créditos iniciales al registrarse
  },
}

Adaptador personalizado (PayPal, Paddle, LemonSqueezy, etc.)

import { IBillingAdapter } from 'agentforge';

export class PaddleAdapter implements IBillingAdapter {
  async createCheckoutSession(params) { ... }
  async createSubscription(params) { ... }
  async cancelSubscription(id, atPeriodEnd) { ... }
  async handleWebhook(payload, signature) { ... }
  async getPortalUrl(customerId, returnUrl) { ... }
  async createCustomer(params) { ... }
  async getSubscription(id) { ... }
}

// En la config:
billing: {
  provider: 'custom',
  customAdapter: PaddleAdapter,
}

Queue asíncrona

Envía mensajes en background y haz polling del resultado:

// Enviar mensaje asíncrono
POST /conversations/:id/messages
{ "userId": "user_123", "content": "...", "async": true }
→ { "jobId": "abc123", "estimatedWait": 5000 }

// Hacer polling
GET /jobs/abc123
→ { "status": "active", "progress": 70 }
→ { "status": "completed", "result": { "content": "..." } }

Guards y decoradores

@RequirePlan() — Restricciones por plan

import { RequirePlan, UsageLimitGuard } from 'agentforge';

@Controller('conversations')
@UseGuards(UsageLimitGuard)   // Aplica límites de uso automáticamente
export class MyController {

  @Post()
  @RequirePlan('pro', 'enterprise')  // Solo usuarios en plan pro o enterprise
  async createConversation(@Body() dto: CreateConversationDto) {
    return this.agentService.createConversation(dto);
  }
}

UsageLimitGuard — Límites automáticos

Aplica el guard globalmente en main.ts:

import { UsageLimitGuard } from 'agentforge';

app.useGlobalGuards(app.get(UsageLimitGuard));

Acceso a servicios internos

Todos los servicios se exportan para que puedas inyectarlos en tu propia lógica:

import {
  AgentService,
  BillingService,
  ConversationService,
  UsageService,
  ToolRegistryService,
  OrchestratorService,
} from 'agentforge';

@Injectable()
export class MyCustomService {
  constructor(
    private readonly agents: AgentService,
    private readonly billing: BillingService,
    private readonly usage: UsageService,
  ) {}

  async onboardUser(userId: string, email: string) {
    // Crear conversación de bienvenida
    const { conversation } = await this.agents.createConversation({
      userId,
      agentId: 'assistant',
      title: 'Welcome',
      initialMessage: 'Hello! I am your AI assistant. How can I help you today?',
    });

    // Ver el uso
    const summary = await this.usage.getSummary(userId);
    console.log(`User ${userId}: ${summary.requestsUsed}/${summary.requestsLimit} requests used`);

    return conversation;
  }
}

Variables de entorno

# Anthropic
ANTHROPIC_API_KEY=sk-ant-api03-...

# PostgreSQL
DATABASE_URL=postgresql://user:password@localhost:5432/myapp

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# App
APP_URL=http://localhost:3000

Estructura de la base de datos

AgentForge crea automáticamente las siguientes tablas (prefijo af_):

| Tabla | Descripción | |---|---| | af_users | Usuarios con plan actual y balance de créditos | | af_conversations | Sesiones de conversación con agentes | | af_messages | Historial de mensajes con tool calls y uso de tokens | | af_subscriptions | Suscripciones de Stripe por usuario | | af_usage_records | Registro detallado de uso para billing |


Roadmap

  • [ ] Soporte para MCP servers (Model Context Protocol)
  • [ ] Adapter para LemonSqueezy y Paddle
  • [ ] Panel de administración (React)
  • [ ] Rate limiting por IP
  • [ ] Webhooks de eventos para tu sistema (conversación iniciada, límite alcanzado, etc.)
  • [ ] Soporte para archivos adjuntos en mensajes
  • [ ] Streaming con WebSockets (además de SSE)

Licencia

MIT