Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ dist/
*.db-shm
*.db-wal
.env
web/tsconfig.tsbuildinfo
86 changes: 78 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
[![Version](https://img.shields.io/badge/version-0.1.0-blueviolet)](package.json)

**A local-first LLM runtime for developers who demand more.**
Persistent sessions · Multi-provider · Branching · Diffing · Sandboxing · Evals · Pipelines · MCP
Persistent sessions · Multi-provider · Branching · Diffing · Sandboxing · Evals · Pipelines · MCP · **WhatsApp**

</div>

Expand Down Expand Up @@ -65,6 +65,9 @@ Export conversation datasets in JSONL format and submit fine-tuning jobs directl
<tr>
<td width="50%" valign="top">

### 📱 WhatsApp Business Integration
Send and receive WhatsApp messages via the Meta Business Cloud API. Configure webhooks, test sends, view a live message log, and optionally enable auto-replies driven by any JCLAW session.

### 📡 Automation-Native Output Piping
Pipe LLM responses to **files**, the **system clipboard**, **webhooks**, or arbitrary **shell scripts** — first-class primitives, not afterthoughts.

Expand All @@ -80,6 +83,9 @@ Instantly search across your entire message history using SQLite FTS5 — blazin
### 🔌 Model Context Protocol (MCP)
Expose JCLAW as an MCP tool provider for other AI agents. Also acts as an MCP client — connect to any external MCP server and use its tools inside your chat sessions.

### 📈 Live Processing Dashboard
The Overview page shows a real-time processing bar (tokens/sec, last model, last provider) and a Framework Status panel with live indicators for every active framework — Sandbox, Red Team, MCP, WhatsApp, Pipeline, Evals, Fine-Tune, and Embeddings.

</td>
</tr>
</table>
Expand All @@ -90,13 +96,15 @@ Expose JCLAW as an MCP tool provider for other AI agents. Also acts as an MCP cl

```
┌─────────────────────────────────────────────────────────────────────────┐
│ CLI (commander) Web Dashboard (React + Vite + TailwindCSS)
│ CLI (commander) Web Dashboard (React + Vite)
│ │ │ │
│ └──────────── WebSocket ───────┘ │
│ │ │
│ gate/server.ts (Express + WebSocketServer) │
│ ├─ /webhook/whatsapp GET (verify) + POST (receive) │
│ └─ /health │
│ │ │
│ gate/protocol.ts (JSON-RPC method router, ~30 methods) │
│ gate/protocol.ts (JSON-RPC method router, 40+ methods) │
│ │ │
│ ┌──────────────────────┼────────────────────────────────────────┐ │
│ │ │ │ │
Expand All @@ -111,12 +119,12 @@ Expose JCLAW as an MCP tool provider for other AI agents. Also acts as an MCP cl
│ └─ metrics*.ts │
│ │
│ agent/runtime.ts ← agentic loop mcp/ ← server + client │
│ channels/ ← I/O plugin channels
│ channels/ ← I/O plugin channels (WhatsApp, …)
│ plugins/ ← plugin registry │
└─────────────────────────────────────────────────────────────────────────┘
```

**Stack:** Node.js 20 (ESM) · TypeScript 5.6 · Express · WebSocket · SQLite (`better-sqlite3`, WAL + FTS5) · React + Vite · TailwindCSS · Vitest
**Stack:** Node.js 20 (ESM) · TypeScript 5.6 · Express · WebSocket · SQLite (`better-sqlite3`, WAL + FTS5) · React + Vite · Vitest

---

Expand Down Expand Up @@ -197,15 +205,65 @@ jclaw search "context window optimization"

| Page | Description |
|---|---|
| **Overview** | Session stats, live provider health pings, and cost metrics |
| **Overview** | Live processing bar, framework status panel, session stats, provider health |
| **Sessions** | Sortable list of all sessions with token/cost info |
| **Session Detail** | Full message thread with inline model tags |
| **Prompts** | Saved system prompts with `{{variable}}` templating |
| **Templates** | Pre-configured session templates (model, system prompt, cost ceiling) |
| **Chat** | Live streaming chat with tool-call trace |
| **Terminal** | Raw JSON-RPC console |
| **Activity** | Real-time WebSocket frame monitor with tokens/sec counter |
| **Metrics** | Historical token/latency/cost graphs |
| **Providers** | API key management, base URL config, connection testing, model listing |
| **Search** | Full-text search across all message history (FTS5) |
| **MCP** | Add/edit/delete MCP server configs, connection status, available tools |
| **Sandbox** | Prompt-injection protection, prefix/suffix injection, red-team harness |
| **MCP** | Add/edit/delete MCP server configs, connection status, available tools |
| **WhatsApp** | Meta Business Cloud API channel — config, live message log, test send |
| **Datasets** | Conversation dataset management for fine-tuning |
| **Fine-Tune** | Submit and monitor fine-tuning jobs (OpenAI/Groq) |
| **Evals** | Create eval suites, run benchmarks, view scored results |
| **Embed Search** | Semantic search over message history |
| **Search** | Full-text search (FTS5) across all message history |

---

## 📱 WhatsApp Integration

JCLAW integrates with the **Meta WhatsApp Business Cloud API** to send and receive WhatsApp messages directly from the dashboard.

### Setup (5 steps)

1. **Create a Meta app** at [developers.facebook.com](https://developers.facebook.com/apps/) and add the *WhatsApp Business* product.

2. **Copy your credentials** from *WhatsApp → API Setup*:
- **Phone Number ID** — a numeric ID tied to your test/production number
- **System User Access Token** — long-lived token from Meta Business Manager
- **App Secret** — found in *App Dashboard → Basic* (used for webhook signature verification)

3. **Configure JCLAW** — open the **WhatsApp** dashboard page and enter:
- Phone Number ID
- Access Token
- Verify Token (any string you choose, e.g. `jclaw-verify`)
- App Secret *(optional but recommended — enables `X-Hub-Signature-256` validation on inbound webhooks)*

4. **Register the webhook** in your Meta app under *WhatsApp → Configuration*:
- **Webhook URL:** `https://<your-host>/webhook/whatsapp`
- **Verify Token:** same string you entered above
- Subscribe to the `messages` field

5. **Test** — use the *Test Send* panel to send a message to any number approved in your Meta app.

> **Note:** Inbound messages are broadcast as `whatsapp.message` WebSocket events and appear in the live log instantly. Enable **Auto-Reply** in the config panel to have JCLAW forward incoming messages to a session and reply automatically.

### Protocol methods

| Method | Description |
|---|---|
| `whatsapp.config.get` | Get current config (access token, verify token, and app secret are masked) |
| `whatsapp.config.set` | Update Phone Number ID, access token, verify token, app secret, auto-reply settings |
| `whatsapp.send` | Send a text message to a phone number |
| `whatsapp.messages.list` | List recent inbound/outbound messages |

Inbound messages are also emitted as `whatsapp.message` WebSocket events for real-time display.

---

Expand Down Expand Up @@ -233,14 +291,25 @@ jclaw-framework/
├── src/
│ ├── agent/ # Agentic workflow runtime
│ ├── channels/ # I/O plugin channels
│ │ └── plugins/
│ │ ├── exampleChannel.ts # Stdout logger (template)
│ │ └── whatsapp.ts # Meta WhatsApp Business Cloud API
│ ├── cli/ # CLI entry point (commander)
│ ├── gate/ # Express + WebSocket server & protocol router
│ │ ├── server.ts # HTTP + WS server, WhatsApp webhook endpoints
│ │ ├── protocol.ts # JSON-RPC method router (40+ methods)
│ │ └── whatsapp-store.ts # In-process WhatsApp message store
│ ├── mcp/ # MCP server, client manager, shared types
│ ├── plugins/ # Plugin registry
│ ├── providers/ # LLM adapters (Anthropic, OpenAI, Ollama, …)
│ ├── runtime/ # Chat, eval, fine-tune, embeddings, diffing, piping
│ └── storage/ # SQLite layer (sessions, messages, prompts, sandbox, …)
├── web/ # React + Vite dashboard frontend
│ └── src/
│ └── pages/
│ ├── Overview.tsx # Live processing bar + framework status panel
│ ├── WhatsApp.tsx # WhatsApp channel management page
│ └── ... # All other dashboard pages
├── scripts/ # Utility scripts
├── tsconfig.json
└── package.json
Expand Down Expand Up @@ -273,3 +342,4 @@ This project is licensed under the **MIT License** — see the [LICENSE](LICENSE
Built with ☕ and TypeScript · [Report a bug](https://github.com/Jacobcdsmith/jclaw-framework/issues) · [Request a feature](https://github.com/Jacobcdsmith/jclaw-framework/issues)

</div>

49 changes: 38 additions & 11 deletions replit.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,24 @@ A local-first LLM runtime that treats LLM APIs as a persistent, automated runtim
## Project Structure

- `src/gate/` - Core WebSocket + Express gate server (serves the dashboard at `/`)
- `server.ts` - HTTP + WS server, WhatsApp webhook routes (`/webhook/whatsapp`)
- `protocol.ts` - JSON-RPC method router (40+ methods including `whatsapp.*`)
- `whatsapp-store.ts` - In-process message store for the WhatsApp channel
- `src/cli/` - CLI entry point using `commander`
- `src/storage/` - SQLite persistence layer (sessions, messages, prompts, templates, config, sandbox enforcement)
- `src/providers/` - LLM provider adapters (Anthropic, OpenAI-compatible, Ollama, LM Studio)
- `src/runtime/` - Core business logic (chat, context management, output piping)
- `src/mcp/` - MCP support: server (jclaw as MCP server), client-manager (connects to external MCP servers), shared types
- `src/channels/` - Input/output plugin channels
- `src/mcp/` - MCP support: server (jclaw as MCP server), client-manager, shared types
- `src/channels/` - I/O plugin channels
- `plugins/whatsapp.ts` - Meta WhatsApp Business Cloud API outbound adapter
- `plugins/exampleChannel.ts` - Stdout logger (channel plugin template)
- `src/agent/` - Agentic workflow runtime
- `src/plugins/` - Plugin registry
- `dist/` - Compiled gate server output (from `npm run build`)
- `web/` - React + Vite dashboard frontend
- `web/src/` - React components (Overview, Sessions, SessionDetail, Prompts, Templates, Providers, Search)
- `web/src/pages/Overview.tsx` - Live processing bar + framework status panel
- `web/src/pages/WhatsApp.tsx` - WhatsApp channel management page
- `web/src/ws.ts` - WebSocket JSON-RPC client
- `web/dist/` - Built dashboard (served by Express at `/`)

## Running

Expand All @@ -41,25 +46,47 @@ The "Start application" workflow:

## Dashboard

The web dashboard is a React SPA with a **Star Trek / LCARS terminal** aesthetic (deep black, amber + cyan accents, monospace font, CRT scanlines). Served from the same Express server as the gate.
The web dashboard is a React SPA with a **Star Trek / LCARS terminal** aesthetic (deep black, amber + cyan accents, monospace font). Served from the same Express server as the gate.

Pages:
- **Overview** - Session stats and live provider health pings
- **Overview** - Live processing bar (tokens/sec, last model, last provider), framework status panel with live indicators for Sandbox/Red Team/MCP/WhatsApp/Pipeline/Evals/Fine-Tune/Embeddings, session stats, provider health pings
- **Sessions** - Sortable list of all sessions with token/cost info
- **Session Detail** - Full message thread for a session
- **Prompts** - Saved system prompts (expandable cards)
- **Templates** - Session configuration templates (expandable cards)
- **Templates** - Session configuration templates
- **Providers** - Per-provider config: API key entry (masked), base URL, Test Connection, available models list
- **MCP** - MCP server management: add/edit/delete/enable server configs, view connection status, list available tools
- **Sandbox** - Prompt sandbox: master enable/disable toggle, system prompt prefix/suffix injection (server-level), client override blocking, injection pattern protection, custom blocked phrases
- **Sandbox** - Prompt sandbox: master enable/disable toggle, system prompt prefix/suffix injection, client override blocking, injection pattern protection
- **WhatsApp** - Meta WhatsApp Business Cloud API channel: configuration form (Phone Number ID, access token, verify token, auto-reply), webhook URL display, live message log, test send panel
- **Activity** - Real-time WebSocket frame monitor with tokens/sec counter
- **Metrics** - Historical token/latency/cost graphs
- **Search** - Full-text search across all session messages (FTS5)

The frontend communicates with the gate using the existing JSON-RPC WebSocket protocol.
## WhatsApp Integration

JCLAW integrates with the Meta WhatsApp Business Cloud API:

**Setup:**
1. Create a Meta app at developers.facebook.com and add the WhatsApp Business product
2. Copy the **Phone Number ID** and **System User Access Token** from WhatsApp → API Setup
3. Open the **WhatsApp** dashboard page and enter the credentials plus a Verify Token
4. Register the webhook in Meta app settings:
- URL: `https://<your-host>/webhook/whatsapp`
- Verify Token: same string entered in the dashboard
- Subscribe to the `messages` field
5. Use the **Test Send** panel to verify the setup

**Protocol methods added:**
- `whatsapp.config.get` / `whatsapp.config.set` — read/write config (access token is masked on read)
- `whatsapp.send` — send a text message to a phone number
- `whatsapp.messages.list` — list recent inbound/outbound messages

Inbound messages are also broadcast as `whatsapp.message` WebSocket events for real-time display.

## Provider Config

Provider API keys can be configured via:
1. **The Providers dashboard page** — type and save via `config.set` RPC method (persists to `~/.jclaw/config.json`, re-initializes provider in-memory instantly)
1. **The Providers dashboard page** — type and save via `config.set` RPC method (persists to `~/.jclaw/config.json`)
2. **Environment variables** — `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` (fallback if not in config file)

Config file: `~/.jclaw/config.json`
Expand All @@ -76,4 +103,4 @@ Config file: `~/.jclaw/config.json`
## Port

Server runs on port 5000 (configurable via `JCLAW_PORT` environment variable).
Default changed from 18789 → 5000 to match Replit's webview port requirement.

71 changes: 71 additions & 0 deletions src/channels/plugins/whatsapp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { JclawChannelPlugin, OutboundContext, DeliveryResult } from "../types.js";

const WHATSAPP_API_VERSION = "v20.0";
const WHATSAPP_BASE = `https://graph.facebook.com/${WHATSAPP_API_VERSION}`;

// Node 18+ ships global fetch. Guard against environments where it may be missing.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const _globalFetch = (globalThis as any).fetch as
| ((url: string, init?: Record<string, unknown>) => Promise<{ ok: boolean; status: number; statusText: string; json: () => Promise<unknown> }>)
| undefined;

export interface WhatsAppSendOptions {
phoneNumberId: string;
accessToken: string;
}

export async function sendWhatsAppText(
to: string,
text: string,
opts: WhatsAppSendOptions
): Promise<DeliveryResult> {
if (!_globalFetch) {
return { ok: false, error: "fetch is not available in this runtime (Node 18+ required)" };
}
const url = `${WHATSAPP_BASE}/${opts.phoneNumberId}/messages`;
const body = {
messaging_product: "whatsapp",
recipient_type: "individual",
to,
type: "text",
text: { preview_url: false, body: text }
};

const resp = await _globalFetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${opts.accessToken}`,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});

const raw = await resp.json().catch(() => ({}));

if (!resp.ok) {
const errMsg = (raw as { error?: { message?: string } }).error?.message ?? `HTTP ${resp.status}`;
return { ok: false, raw, error: errMsg };
}

return { ok: true, raw };
}

export function createWhatsAppPlugin(opts: WhatsAppSendOptions): JclawChannelPlugin {
return {
id: "whatsapp",
meta: {
id: "whatsapp",
name: "WhatsApp Business",
description: "Meta WhatsApp Business Cloud API outbound adapter"
},
capabilities: { text: true, media: false },
outbound: {
async sendText(ctx: OutboundContext): Promise<DeliveryResult> {
if (!opts.phoneNumberId || !opts.accessToken) {
return { ok: false, error: "WhatsApp not configured (missing phoneNumberId or accessToken)" };
}
return sendWhatsAppText(ctx.to, ctx.text ?? "", opts);
}
}
};
}
1 change: 1 addition & 0 deletions src/channels/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface OutboundContext {
export interface DeliveryResult {
ok: boolean;
raw?: unknown;
error?: string;
}

export interface ChannelOutboundAdapter {
Expand Down
Loading