npm package: @fnnrodrigo/wos
Description: wOS — Open-source TypeScript framework for building production-ready conversational AI bots
Latest version: 0.1.1

# wOS — Conversational AI Framework

wOS is an open-source TypeScript framework for building production-ready conversational AI bots. Think Rails or Laravel, but for chat bots. Scaffold a project, get auth, multi-tenant accounts, billing hooks, AI interpretation, tool system, conversation flows, admin panel, and security — out of the box.

## Quick Start

```bash
# Scaffold a new project
bunx @fnnrodrigo/wos new my-bot
cd my-bot

# Configure
cp .env.example .env
# Edit .env with your credentials

# Start services
docker compose up -d

# Install & run
bun install
wos migrate
wos dev
```

Your bot is running at `http://localhost:3000`. Admin panel at `/admin`.

## Architecture

```
┌──────────────┐
│   WhatsApp   │   Channel Adapter (swappable)
└──────┬───────┘
       │ webhook
┌──────▼───────┐
│    Hono      │   HTTP Framework
│   Router     │   /webhook, /health, /admin/api/*
└──────┬───────┘
       │
┌──────▼───────┐
│   BullMQ     │   Job Queue (Redis)
│   Queue      │   Inbound / Outbound / Scheduled
└──────┬───────┘
       │
┌──────▼────────────────────────────────────────┐
│              Middleware Pipeline               │
│                                                │
│  Dedup → Auth → Account → Session → Blocked   │
│  → Billing → RateLimit → Security             │
│  → [App Middleware] → Flow/Interpreter         │
└──────┬────────────────────────────────────────┘
       │
  ┌────▼────┐    ┌──────────────┐
  │  Flow   │ OR │ AI Interpreter│
  │ Engine  │    │ (Claude +     │
  │ (rules) │    │  Tool Calls)  │
  └─────────┘    └──────────────┘
       │
  ┌────▼─────┐
  │ Drizzle  │   PostgreSQL (9 tables)
  │   ORM    │   Accounts, Users, Sessions,
  └──────────┘   Messages, Usage, Audit...
```

## Configuration

```typescript
// config/wos.config.ts
import { defineConfig } from "@fnnrodrigo/wos";

export default defineConfig({
  name: "my-bot",
  port: 3000,
  database: { url: process.env.DATABASE_URL! },
  redis: { url: process.env.REDIS_URL! },
  channel: {
    adapter: "whatsapp",
    whatsapp: {
      phoneId: process.env.WHATSAPP_PHONE_ID!,
      accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
      verifyToken: process.env.WHATSAPP_VERIFY_TOKEN!,
      appSecret: process.env.WHATSAPP_APP_SECRET!,
    },
  },
  ai: {
    provider: "claude",
    apiKey: process.env.ANTHROPIC_API_KEY!,
    model: "claude-sonnet-4-5-20250514",
  },
  billing: { provider: "free" },
  admin: { enabled: true, jwtSecret: process.env.ADMIN_JWT_SECRET },
  rateLimit: { maxPerMinute: 20 },
  session: { ttlMs: 14400000 },   // 4 hours
  interpreter: { maxToolRounds: 5, historyLimit: 20 },
  costBudget: { perAccountMonthlyUsd: 10 },
  autoDiscover: true,
});
```

## Core Concepts

### Channels

wOS ships with WhatsApp (Meta Cloud API v21.0). The adapter pattern makes it easy to add more:

```typescript
interface ChannelAdapter {
  name: string;
  send(to: string, reply: NormalizedReply): Promise<{ messageId: string }>;
  sendTemplate(to: string, template: NormalizedReply): Promise<{ messageId: string }>;
  registerRoutes(app: Hono, onMessage: (msg: NormalizedMessage) => Promise<void>): void;
  verifySignature(request: Request, body: string): boolean;
}
```

### Flows (Rule-Based)

Flows are state machines — no AI needed, no cost per message:

```typescript
// app/flows/welcome.flow.ts
import { createFlow } from "@fnnrodrigo/wos/flow";

export default createFlow("welcome", "Welcome Flow")
  .trigger(/^(hi|hello|hey)$/i)
  .step("greet", "Hello! How can I help?", (s) => {
    s.buttons([
      { id: "help", title: "Help" },
      { id: "pricing", title: "Pricing" },
    ]);
    s.transition("button", "help", { value: "help" });
    s.transition("button", "pricing", { value: "pricing" });
    s.transition("always", "END");
  })
  .step("help", "Just type your question and I'll do my best!", (s) => {
    s.transition("always", "END");
  })
  .step("pricing", "Plans start at $29/mo. Visit example.com/pricing", (s) => {
    s.transition("always", "END");
  })
  .build();
```

### Tools (AI-Powered)

Tools extend the AI interpreter. When the AI decides it needs external data, it calls your tools:

```typescript
// app/tools/weather.tool.ts
import { BaseTool } from "@fnnrodrigo/wos/tools";
import type { ToolContext, ToolResult } from "@fnnrodrigo/wos";

export default class WeatherTool extends BaseTool {
  name = "get_weather";
  description = "Get current weather for a city";
  inputSchema = {
    type: "object" as const,
    properties: {
      city: { type: "string", description: "City name" },
    },
    required: ["city"],
  };

  async execute(input: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult> {
    const city = input.city as string;
    const res = await fetch(`https://api.weather.com/v1?city=${city}`);
    const data = await res.json();
    return { success: true, data };
  }
}
```

Place it in `app/tools/` and wOS auto-discovers it.

### Middleware

Add custom middleware to the pipeline:

```typescript
// app/middleware/logging.ts
import type { Middleware } from "@fnnrodrigo/wos";

const loggingMiddleware: Middleware = async (ctx, next) => {
  console.log(`Message from ${ctx.message.from}: ${ctx.message.text}`);
  await next();
  console.log(`Reply sent`);
};

export default loggingMiddleware;
```

### Multi-Tenant Model

```
Account (tenant)
  ├── status: trial | active | suspended | cancelled
  ├── billing_external_id → Billing Provider
  ├── trial_ends_at
  │
  ├── AccountMembers
  │   ├── User (role: owner)
  │   ├── User (role: admin)
  │   └── User (role: member)
  │
  ├── Sessions (conversations)
  ├── Messages (full history)
  └── UsageLog (AI tokens, costs)
```

Users authenticate via phone (OTP). Each user can belong to multiple accounts with different roles.

### Admin Panel

Enable with `admin: { enabled: true }` in config. Access at `/admin`.

Features:
- Dashboard with stats and message volume charts
- Account management (view, search, update status)
- User management (view, search, block/unblock)
- Conversation viewer with message bubbles
- Tool registry and usage stats
- Runtime settings editor
- Audit log with severity filtering

First-time setup: `POST /admin/api/auth/setup` with `{ email, password, name }`.

## CLI

```bash
wos new <name>      # Scaffold a new project
wos migrate         # Run database migrations
wos dev             # Start with hot reload
```

## Deployment

### Docker

```dockerfile
FROM oven/bun:1 AS base
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production
COPY . .
EXPOSE 3000
CMD ["bun", "run", "index.ts"]
```

### Docker Compose

The scaffolded project includes `docker-compose.yml` with PostgreSQL and Redis.

```bash
docker compose up -d    # Start Postgres + Redis
wos migrate             # Run migrations
wos dev                 # Start bot
```

### Environment Variables

| Variable | Required | Description |
|---|---|---|
| `DATABASE_URL` | Yes | PostgreSQL connection string |
| `REDIS_URL` | Yes | Redis connection string |
| `WHATSAPP_PHONE_ID` | Yes | Meta WhatsApp phone number ID |
| `WHATSAPP_ACCESS_TOKEN` | Yes | Meta API access token |
| `WHATSAPP_VERIFY_TOKEN` | Yes | Webhook verification token |
| `WHATSAPP_APP_SECRET` | Yes | App secret for webhook signature |
| `ANTHROPIC_API_KEY` | For AI | Claude API key |
| `PORT` | No | Server port (default: 3000) |

## Testing

```bash
bun test tests/          # Run all tests
```

### Webhook Simulator

Test your bot locally without WhatsApp:

```bash
bun run tests/simulator/whatsapp.simulator.ts send "Hello"
bun run tests/simulator/whatsapp.simulator.ts send --button "btn_id"
bun run tests/simulator/whatsapp.simulator.ts send --from "+5511999990001" "Hi there"
```

## License

MIT