@fnnrodrigo/wos
v0.1.1
Published
wOS — Open-source TypeScript framework for building production-ready conversational AI bots
Downloads
24
Maintainers
Readme
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
# 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 devYour 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
// 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:
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:
// 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:
// 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:
// 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
wos new <name> # Scaffold a new project
wos migrate # Run database migrations
wos dev # Start with hot reloadDeployment
Docker
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.
docker compose up -d # Start Postgres + Redis
wos migrate # Run migrations
wos dev # Start botEnvironment 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
bun test tests/ # Run all testsWebhook Simulator
Test your bot locally without WhatsApp:
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
