Full System Architecture & Code Walkthrough
| Metric | Value |
|---|---|
| GitHub Stars | 186,000 |
| Contributors | 510 |
| Age | 81 days |
| License | MIT |
┌─────────────────────────────────────────────────────────┐
│ Channels (WhatsApp, Discord, Slack, Telegram, etc.) │
└─────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────┐
│ Gateway :18789 │ ← WebSocket + HTTP
└────────┬─────────┘
│
▼
┌─────────────────┐
│ Agent Core │ ← Pi Agent, routing, sessions
└────────┬─────────┘
│
▼
┌────────────────────────┐
│ Tools & LLM Providers │ ← Claude, GPT, Gemini
└────────────────────────┘
| Layer | Technology |
|---|---|
| Runtime | Node.js 22 |
| Language | TypeScript |
| Build System | pnpm + tsdown |
| AI Framework | Pi Agent Core |
| Baileys (no official API) | |
| Telegram | grammY |
| Discord | discord.js |
| Slack | @slack/bolt |
| Browser | Playwright |
| Database | SQLite + sqlite-vec |
| Validation | Zod 4 + TypeBox |
| Testing | Vitest 4 |
| Linting | oxlint + oxfmt |
| Directory | Purpose | Size |
|---|---|---|
| src/ | Core TypeScript codebase | 480K LOC |
| extensions/ | 35 plugin modules | 86K LOC |
| apps/macos/ | Swift menu bar app | 12K LOC |
| apps/ios/ | Swift iOS client | 8K LOC |
| apps/android/ | Kotlin Android client | 11K LOC |
| ui/ | Lit web components | 24K LOC |
| skills/ | 52 SKILL.md definitions | — |
| scripts/ | ~70 build/dev scripts | — |
| docs/ | Mintlify documentation | — |
export async function startGatewayServer( port: number, config: GatewayConfig ): Promise<GatewayServer> { // WebSocket + HTTP server // Channel managers // Agent dispatcher }
export async function startGatewayServer(port: number, config: GatewayConfig) { const httpServer = http.createServer(handleRequest); const wss = new WebSocketServer({ server: httpServer }); wss.on('connection', async (ws, req) => { const session = await createSession(ws, req); attachFrameHandlers(session); }); httpServer.listen(port); await startChannels(config.channels); await startDiscovery(port); return { httpServer, wss, config }; }
src/gateway/server.ts
const RequestFrame = Type.Object({ type: Type.Literal('request'), id: Type.String(), method: Type.String(), params: Type.Optional(Type.Any()) }); const ResponseFrame = Type.Object({ type: Type.Literal('response'), id: Type.String(), result?: Type.Any(), error?: Type.String() }); const EventFrame = Type.Object({ type: Type.Literal('event'), event: Type.String(), data: Type.Any() });
src/gateway/protocol.ts
const READ_METHODS = new Set([ 'getChannels', 'getConversations', 'getMessages', 'getConfig' ]); const WRITE_METHODS = new Set([ 'sendMessage', 'updateConfig', 'startChannel', 'stopChannel' ]); function authorizeGatewayMethod(method: string, session: Session) { if (WRITE_METHODS.has(method) && !session.isAuthenticated) { throw new Error('Write methods require authentication'); } return true; }
src/gateway/auth.ts — 50+ RPC methods total
async function handleRequest(req: IncomingMessage, res: ServerResponse) { // Execute plugin hooks const hookResult = await runHttpHooks(req); if (hookResult) return sendResponse(res, hookResult); // Tool HTTP endpoints if (req.url?.startsWith('/tools/')) return handleToolRequest(req, res); // OpenAI-compatible API if (req.url === '/v1/chat/completions') return handleChatCompletion(req, res); // Control UI if (req.url === '/') return serveControlUI(res); res.writeHead(404); res.end(); }
src/gateway/http.ts
async function startChannel(channelId: string, config: ChannelConfig) { const abortController = new AbortController(); const adapter = await createAdapter(config.type, config); adapter.on('message', async (msg) => { const conversation = await getOrCreateConversation(channelId, msg.from); await dispatchToAgent(conversation, msg); }); await adapter.connect(abortController.signal); channelRegistry.set(channelId, { adapter, abortController }); broadcastEvent('channelStarted', { channelId }); }
src/gateway/channels.ts
16 interfaces for one unified messaging abstraction
const telegramOutbound: OutboundAdapter = { async sendText(conversationId, text, options) { const chunks = chunkMessage(text, 4096); for (const chunk of chunks) { await bot.api.sendMessage(conversationId, chunk, { parse_mode: 'Markdown', disable_web_page_preview: options?.noPreview }); } }, async sendMedia(conversationId, attachment) { const file = new InputFile(attachment.data); await bot.api.sendDocument(conversationId, file); } };
src/channels/telegram/outbound.ts
// No chunking needed chunker: null, maxLength: 2000, async sendText(channelId, text) { const channel = await client.channels.fetch(channelId); await channel.send(text); }
src/channels/discord/outbound.ts
async function loadOpenClawPlugins(pluginDir: string) { const manifests = await glob('**/openclaw.plugin.json', { cwd: pluginDir }); for (const manifestPath of manifests) { const manifest = await readJSON(manifestPath); const plugin = await import(manifest.entry); await plugin.initialize(gatewayContext); pluginRegistry.set(manifest.id, plugin); } }
{
"id": "spotify-player",
"version": "1.2.0",
"entry": "./dist/index.js",
"hooks": ["beforeMessage", "afterMessage"],
"capabilities": ["audio-playback"]
}