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

@runworkai/framework

v0.4.5

Published

The TypeScript framework for building full-stack apps on the Runwork platform — entity storage, AI agents, workflows, integrations, and more.

Downloads

416

Readme

@runworkai/framework

The TypeScript framework for building apps on Runwork — the AI workspace where teams talk to AI, get custom tools, and work together.

Every Runwork app comes with built-in data storage, AI agents, durable workflows, scheduled jobs, file storage, third-party integrations, real-time channels, and cross-app workspaces — all from a single codebase, deployed and managed by the platform.

Quick Start

npm install @runworkai/framework
// worker/index.ts
import { createApp } from '@runworkai/framework'
import { entities } from './entities'
import { routes } from './routes'

createApp({ entities, routes })

The Runwork Platform

Runwork is an AI workspace that brings AI agents, custom apps, and human teams into one place. Teams talk to the workspace to get quick answers, automate recurring work, or spin up full custom tools — without switching between dozens of SaaS products.

This framework is the developer interface to the platform. You write TypeScript, and the platform handles deployment, infrastructure, AI, and integrations:

  • Instant deployments — Push code and get a live URL. Preview environments for every branch, production at the edge.
  • Built-in AI agents — Users interact with conversational agents that read data, take actions, and coordinate across apps. No API keys to manage.
  • Zero to app in seconds — Describe what you need and Runwork generates it. Developers customize with code using this framework.
  • Connected apps — Apps in a workspace share entities, call each other's APIs, communicate through channels, and present a unified experience.
  • 3,200+ integrations — GitHub, Slack, HubSpot, Google, and thousands more via OAuth. The platform manages tokens and provides a proxy API.
  • No infrastructure to manage — Storage, compute, file hosting, cron scheduling, and workflow orchestration are all handled by the platform.
  • Local dev with any AI tool — Use the Runwork CLI (runwork dev), OpenAI Codex, Claude Code, Cursor, or any editor and AI code agent.

Sub-path Exports

The framework uses sub-path exports to keep imports clean and tree-shakeable:

| Import | Description | |--------|-------------| | @runworkai/framework | Core utilities: Env, ok, bad, notFound, isStr, createApp | | @runworkai/framework/entities | Data storage: Entity base classes, CRUD operations, indexing | | @runworkai/framework/agents | AI agent definitions and conversational runtime | | @runworkai/framework/ai | Backend AI helpers: generateText, generateObject, streamText, tool | | @runworkai/framework/workflows | Multi-step durable workflow definitions and triggers | | @runworkai/framework/scheduler | Scheduled background job definitions | | @runworkai/framework/endpoints | Public API endpoint definitions with schema validation | | @runworkai/framework/routes | Internal route types | | @runworkai/framework/workspace | Cross-app workspace operations and entity access | | @runworkai/framework/storage | File/blob storage client | | @runworkai/framework/integrations | Third-party integration client (OAuth proxy) | | @runworkai/framework/channels | Workspace channel messaging | | @runworkai/framework/events | Event emission system | | @runworkai/framework/components | Cross-app reusable UI component definitions | | @runworkai/framework/vite | Vite plugin for development and builds | | @runworkai/framework/types | Shared type definitions |

Features

Data Storage (Entities)

Persistent data storage with search, sort, filter, paginate, and bulk operations. Define Entity classes for your data models, and the platform handles persistence. Data is accessible across apps in a workspace.

// worker/entities.ts
import { Entity } from '@runworkai/framework/entities';

interface TodoState {
    id: string;
    title: string;
    completed: boolean;
    userId: string;
}

export class TodoEntity extends Entity<TodoState> {
    static readonly entityName = "Todo";
    static readonly initialState: TodoState = {
        id: "", title: "", completed: false, userId: ""
    };

    // Custom methods for domain logic
    async toggle() {
        await this.mutate(state => ({ ...state, completed: !state.completed }));
        return this.getState();
    }
}

API Routes

Define routes using Hono or the declarative EndpointDefinition format. Routes using EndpointDefinition are automatically registered with the workspace, making them discoverable by other apps and AI agents.

import { ok, bad } from '@runworkai/framework'
import type { EndpointDefinition } from '@runworkai/framework/endpoints'
import { z } from 'zod'

export const APP_ROUTES = [
  {
    path: '/api/todos',
    method: 'GET',
    auth: 'none',
    schema: {
      query: z.object({ limit: z.coerce.number().optional().default(50) }),
    },
    handler: async (ctx) => {
      const page = await TodoEntity.list({ env: ctx.env }, { limit: ctx.query.limit })
      return page
    },
    meta: { description: 'List todos', tags: ['todos'] },
  },
]

AI Agents

Conversational AI agents with tool use, memory, and integration access. Agents can read/write entities, call connected integrations, and maintain conversation history. The platform handles model selection, API keys, and billing.

// worker/agents.ts
    import type { AgentDefinition } from '@runworkai/framework/agents';

    export const APP_AGENTS: AgentDefinition[] = [
      {
        name: 'support-assistant',
        description: 'Helps users with common questions and issues',
        type: 'conversational',
        systemPrompt: `You are a helpful support assistant for our application.

Your capabilities:
- Answer questions about the product
- Help users troubleshoot issues
- Create GitHub issues for bug reports
- Send Slack notifications when needed

Be friendly, professional, and concise.`,
        integrations: ['github', 'slack'],
        entities: ['ticket', 'user'],
        memoryStrategy: 'user',
      },
    ];

Backend AI Processing

Use AI for backend tasks like classification, extraction, and summarization. Works in routes, workflows, and scheduled jobs. No external AI SDK needed — the platform proxies all LLM calls.

import { z } from 'zod';
import { generateObject } from '@runworkai/framework/ai';

app.post('/api/receipts/parse', async (c) => {
  const { receiptText } = await c.req.json();

  const result = await generateObject(c.env, {
    prompt: `Extract receipt data from: \`,
    schema: z.object({
      vendor: z.string(),
      date: z.string(),
      total: z.number(),
      items: z.array(z.object({
        name: z.string(),
        price: z.number(),
      })),
    }),
  });

  await ReceiptEntity.create({ env: c.env }, result.object);
  return ok(c, result.object);
});

Workflows

Durable, multi-step processes that survive failures. Workflows can wait for external events, retry with backoff, sleep for durations, and coordinate across services.

// worker/workflows.ts
import type { WorkflowDefinition } from '@runworkai/framework/workflows';
import { OrderEntity } from "./entities";

export const APP_WORKFLOWS: WorkflowDefinition[] = [
  {
    name: "order-fulfillment",
    description: "Process order through payment and shipping",
    handler: async (ctx, step) => {
      const entityCtx = { env: ctx.env };
      const { orderId } = ctx.params as { orderId: string };

      // Step 1: Validate order
      const order = await step.do("validate-order", async () => {
        return await OrderEntity.get(entityCtx, orderId);
      });

      // Step 2: Process payment (with retry)
      await step.do("process-payment", {
        retries: { limit: 3, delay: "10s" },
      }, async () => {
        // Payment logic here
      });

      // Step 3: Wait for external confirmation
      await step.waitForEvent("shipping-confirmed", {
        timeout: "24h",
      });

      return { success: true, orderId };
    },
  },
];

Scheduled Jobs

Run background tasks on a cron schedule. Jobs are managed by the platform with execution logging and monitoring in the workspace dashboard.

// worker/schedules.ts
import type { ScheduledJob } from '@runworkai/framework/scheduler';
import { TodoEntity } from "./entities";

export const APP_SCHEDULED_JOBS: ScheduledJob[] = [
  {
    name: "cleanup-completed-todos",
    schedule: "0 0 * * *", // Daily at midnight UTC
    description: "Remove completed todos older than 30 days",
    handler: async (ctx) => {
      const entityCtx = { env: ctx.env };
      const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
      const todos = await TodoEntity.list(entityCtx, { limit: 1000 });
      const idsToDelete = todos.items
        .filter(todo => todo.completed && todo.completedAt < thirtyDaysAgo)
        .map(todo => todo.id);
      const deleted = await TodoEntity.deleteMany(entityCtx, idsToDelete);

      return { deleted, processed: todos.items.length };
    },
  },
];

File Storage

Upload and manage files. The platform provisions object storage automatically when enabled.

// worker/routes.ts
import { createFileStorageClient } from '@runworkai/framework/storage';
import { ok, notFound } from '@runworkai/framework';

export const APP_ROUTES = [
    (app) => {
        app.post('/api/documents', async (c) => {
            const storage = createFileStorageClient(c.env);
            const entityCtx = { env: c.env };

            // Upload with custom key and metadata
            const file = await c.req.blob();
            const metadata = await storage.upload(file, {
                key: `documents/\.pdf`,
                name: 'report.pdf',
                contentType: 'application/pdf',
                customMetadata: { category: 'reports', userId: 'user123' }
            });

            // Track in your own entity
            await DocumentEntity.create(entityCtx, {
                id: crypto.randomUUID(),
                fileKey: metadata.key,
                fileName: metadata.name,
                uploadedBy: 'user123',
                createdAt: Date.now()
            });

            return ok(c, metadata);
        });

        app.get('/api/documents/:id/download', async (c) => {
            const storage = createFileStorageClient(c.env);
            const ctx = { env: c.env };
            const doc = await DocumentEntity.get(ctx, c.req.param('id'));
            if (!doc) return notFound(c, 'Document not found');

            const object = await storage.download(doc.fileKey);
            if (!object) return notFound(c, 'File not found');

            return new Response(object.body, {
                headers: {
                    'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
                    'Content-Disposition': `attachment; filename="\"`
                }
            });
        });
    },
];

Third-Party Integrations

Connect to 3,200+ external services via OAuth. The platform manages OAuth flows and token refresh — your code just calls the proxy.

import { useIntegration } from '@runworkai/framework/integrations'

const github = useIntegration(env, 'github')
const repos = await github.proxy('/user/repos', { method: 'GET' })

Cross-App Workspaces & Channels

Apps in a workspace share entities, call each other's APIs, post to channels, and trigger each other's workflows.

// worker/routes.ts
import { postToChannel } from '@runworkai/framework/channels';
import { ok } from '@runworkai/framework';

export const APP_ROUTES = [
    (app) => {
        // Post alert when stock drops below threshold
        app.post('/api/inventory/check-stock', async (c) => {
            const { productName, quantity, threshold } = await c.req.json();

            if (quantity < threshold) {
                postToChannel(c.executionCtx, c.env, {
                    channel: '#inventory-alerts',
                    content: `Low stock alert: **\** is at \ units (threshold: \).`,
                    metadata: { productName, quantity, threshold }
                });
            }

            return ok(c, { checked: true });
        });
    }
];

Public Endpoints

Expose authenticated APIs for external consumers with API key authentication. Accessible at https://{workspace}.runwork.ai/{app}/endpoints/{path}.

// worker/endpoints.ts
import { z } from 'zod';
import type { EndpointDefinition } from '@runworkai/framework/endpoints';
import { EndpointError } from '@runworkai/framework/endpoints';
import { TodoEntity } from './entities';

export const APP_PUBLIC_ENDPOINTS: EndpointDefinition[] = [
  {
    path: '/v1/todos',
    method: 'GET',
    auth: 'apiKey',  // Requires API key
    schema: {
      query: z.object({
        limit: z.coerce.number().optional().default(50),
      }),
      response: z.object({
        items: z.array(z.object({
          id: z.string(),
          title: z.string(),
          completed: z.boolean(),
        })),
      }),
    },
    handler: async (ctx) => {
      const entityCtx = { env: ctx.env };
      const { items } = await TodoEntity.list(entityCtx, { limit: ctx.query.limit });
      return { items };
    },
    meta: {
      description: 'List all todos',
      tags: ['todos'],
    },
  },
  {
    path: '/v1/todos',
    method: 'POST',
    auth: 'apiKey',
    schema: {
      body: z.object({ title: z.string().min(1) }),
    },
    handler: async (ctx) => {
      const entityCtx = { env: ctx.env };
      const todo = await TodoEntity.create(entityCtx, {
        title: ctx.body!.title,
        completed: false,
      });
      return todo;
    },
    meta: { description: 'Create a new todo', tags: ['todos'] },
  },
];

Reusable Components

Define UI components and backend APIs that other apps in the workspace can embed or call.

export const APP_COMPONENTS: ComponentDefinition[] = [
    {
        name: "user-card",
        title: "User Card",
        frontend: true,
        backend: true,
        metadata: {
            tag: "user-card",
            description: "Displays user profile information",
        },
    },
];

Local Development

Develop Runwork apps locally using the CLI or any AI coding tool:

# Using the Runwork CLI
runwork dev      # Start preview, sync changes
runwork deploy   # Deploy to production

# Or use any editor / AI tool
# Framework types are available via node_modules after bun install

Built With

Requirements

Links

License

MIT