this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(server): add Bun HTTP server with webhook endpoint

Implements src/index.ts (bead assistant-pqh.1, assistant-pqh.3)

Server features:
- Bun.serve() with fetch handler pattern
- GET /health - full dependency health check
- GET /healthz - simple liveness probe (k8s)
- POST /webhook - Telegram webhook endpoint

Webhook security:
- Validates X-Telegram-Bot-Api-Secret-Token header
- Returns 401 for missing/invalid tokens
- Parses Telegram Update JSON body
- Fire-and-forget pattern for fast 200 OK response

Startup:
- Initializes Letta before accepting requests
- Starts polling mode when TELEGRAM_WEBHOOK_URL empty
- Graceful error handling for initialization failures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

alice 89695acf 6316a847

+132 -5
+5 -5
.beads/issues.jsonl
··· 20 20 {"id":"assistant-nno.4","title":"break_down_task tool (src/tools/breakdown.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:40.767448Z","updated_at":"2025-12-11T13:44:40.767448Z","dependencies":[{"issue_id":"assistant-nno.4","depends_on_id":"assistant-nno","type":"parent-child","created_at":"2025-12-11T13:44:40.767905Z","created_by":"daemon"}]} 21 21 {"id":"assistant-nno.5","title":"save_item / update_item tools (src/tools/items.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:41.824017Z","updated_at":"2025-12-11T13:44:41.824017Z","dependencies":[{"issue_id":"assistant-nno.5","depends_on_id":"assistant-nno","type":"parent-child","created_at":"2025-12-11T13:44:41.824488Z","created_by":"daemon"}]} 22 22 {"id":"assistant-nno.6","title":"get_open_items tool (src/tools/context.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:42.738005Z","updated_at":"2025-12-11T13:44:42.738005Z","dependencies":[{"issue_id":"assistant-nno.6","depends_on_id":"assistant-nno","type":"parent-child","created_at":"2025-12-11T13:44:42.738547Z","created_by":"daemon"}]} 23 - {"id":"assistant-pqh","title":"M1: E2E Chat","description":"","status":"open","priority":0,"issue_type":"epic","created_at":"2025-12-11T13:43:34.073168Z","updated_at":"2025-12-11T13:43:34.073168Z","dependencies":[{"issue_id":"assistant-pqh","depends_on_id":"assistant-69t","type":"blocks","created_at":"2025-12-11T13:43:49.441521Z","created_by":"daemon"}]} 24 - {"id":"assistant-pqh.1","title":"Webhook server (src/index.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:23.196257Z","updated_at":"2025-12-11T13:44:23.196257Z","dependencies":[{"issue_id":"assistant-pqh.1","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:23.196754Z","created_by":"daemon"}]} 25 - {"id":"assistant-pqh.2","title":"Telegram bot setup with Telegraf (src/bot.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:26.327934Z","updated_at":"2025-12-11T13:44:26.327934Z","dependencies":[{"issue_id":"assistant-pqh.2","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:26.328387Z","created_by":"daemon"}]} 26 - {"id":"assistant-pqh.3","title":"Secret token verification","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:27.745088Z","updated_at":"2025-12-11T13:44:27.745088Z","dependencies":[{"issue_id":"assistant-pqh.3","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:27.745547Z","created_by":"daemon"}]} 27 - {"id":"assistant-pqh.4","title":"Basic message flow to Letta and back","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:28.893703Z","updated_at":"2025-12-11T13:44:28.893703Z","dependencies":[{"issue_id":"assistant-pqh.4","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:28.894172Z","created_by":"daemon"}]} 23 + {"id":"assistant-pqh","title":"M1: E2E Chat","description":"","status":"closed","priority":0,"issue_type":"epic","created_at":"2025-12-11T13:43:34.073168Z","updated_at":"2025-12-11T14:23:40.572021Z","closed_at":"2025-12-11T14:23:40.572021Z","dependencies":[{"issue_id":"assistant-pqh","depends_on_id":"assistant-69t","type":"blocks","created_at":"2025-12-11T13:43:49.441521Z","created_by":"daemon"}]} 24 + {"id":"assistant-pqh.1","title":"Webhook server (src/index.ts)","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:23.196257Z","updated_at":"2025-12-11T14:23:32.168811Z","closed_at":"2025-12-11T14:23:32.168811Z","dependencies":[{"issue_id":"assistant-pqh.1","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:23.196754Z","created_by":"daemon"}]} 25 + {"id":"assistant-pqh.2","title":"Telegram bot setup with Telegraf (src/bot.ts)","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:26.327934Z","updated_at":"2025-12-11T14:22:36.187044Z","closed_at":"2025-12-11T14:22:36.187044Z","dependencies":[{"issue_id":"assistant-pqh.2","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:26.328387Z","created_by":"daemon"}]} 26 + {"id":"assistant-pqh.3","title":"Secret token verification","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:27.745088Z","updated_at":"2025-12-11T14:23:32.170307Z","closed_at":"2025-12-11T14:23:32.170307Z","dependencies":[{"issue_id":"assistant-pqh.3","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:27.745547Z","created_by":"daemon"}]} 27 + {"id":"assistant-pqh.4","title":"Basic message flow to Letta and back","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:28.893703Z","updated_at":"2025-12-11T14:22:37.312394Z","closed_at":"2025-12-11T14:22:37.312394Z","dependencies":[{"issue_id":"assistant-pqh.4","depends_on_id":"assistant-pqh","type":"parent-child","created_at":"2025-12-11T13:44:28.894172Z","created_by":"daemon"}]} 28 28 {"id":"assistant-swt","title":"M3: Tone + Detection","description":"","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-11T13:43:37.059037Z","updated_at":"2025-12-11T13:43:37.059037Z","dependencies":[{"issue_id":"assistant-swt","depends_on_id":"assistant-nno","type":"blocks","created_at":"2025-12-11T13:43:51.868818Z","created_by":"daemon"}]} 29 29 {"id":"assistant-swt.1","title":"Overwhelm detection (src/detect.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:50.906825Z","updated_at":"2025-12-11T13:44:50.906825Z","dependencies":[{"issue_id":"assistant-swt.1","depends_on_id":"assistant-swt","type":"parent-child","created_at":"2025-12-11T13:44:50.90734Z","created_by":"daemon"}]} 30 30 {"id":"assistant-swt.2","title":"Self-bullying detection (src/detect.ts)","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-11T13:44:51.940846Z","updated_at":"2025-12-11T13:44:51.940846Z","dependencies":[{"issue_id":"assistant-swt.2","depends_on_id":"assistant-swt","type":"parent-child","created_at":"2025-12-11T13:44:51.941304Z","created_by":"daemon"}]}
+127
src/index.ts
··· 1 + /** 2 + * Main entry point for the ADHD Support Agent 3 + * 4 + * This Bun HTTP server provides: 5 + * - Health check endpoints for monitoring 6 + * - Telegram webhook endpoint for receiving messages 7 + * - Bot initialization and message handling 8 + * 9 + * Uses Bun.serve() for high-performance HTTP handling. 10 + */ 11 + 12 + import { config, isWebhookMode } from "./config"; 13 + import { healthCheck, simpleHealthCheck } from "./health"; 14 + import { initializeLetta } from "./letta"; 15 + import { handleUpdate, startPolling } from "./bot"; 16 + import type { Update } from "telegraf/types"; 17 + 18 + /** 19 + * Main server handler using Bun.serve() 20 + */ 21 + async function main() { 22 + console.log("Starting ADHD Support Agent..."); 23 + 24 + // Initialize Letta before starting the server 25 + try { 26 + await initializeLetta(); 27 + } catch (error) { 28 + console.error("Failed to initialize Letta:", error); 29 + console.error("Server will start, but bot functionality may be limited."); 30 + } 31 + 32 + // Start the HTTP server 33 + const server = Bun.serve({ 34 + port: config.PORT, 35 + async fetch(req) { 36 + const url = new URL(req.url); 37 + const path = url.pathname; 38 + 39 + // GET /health - Full health check 40 + if (path === "/health" && req.method === "GET") { 41 + return await healthCheck(); 42 + } 43 + 44 + // GET /healthz - Simple health check (k8s liveness probe) 45 + if (path === "/healthz" && req.method === "GET") { 46 + return simpleHealthCheck(); 47 + } 48 + 49 + // POST /webhook - Telegram webhook endpoint 50 + if (path === "/webhook" && req.method === "POST") { 51 + // Verify the secret token 52 + const token = req.headers.get("X-Telegram-Bot-Api-Secret-Token"); 53 + if (!token || token !== config.TELEGRAM_WEBHOOK_SECRET_TOKEN) { 54 + console.warn("Webhook request with invalid or missing secret token"); 55 + return new Response( 56 + JSON.stringify({ error: "Unauthorized" }), 57 + { 58 + status: 401, 59 + headers: { "Content-Type": "application/json" }, 60 + } 61 + ); 62 + } 63 + 64 + // Parse the Telegram update 65 + let update: Update; 66 + try { 67 + update = await req.json(); 68 + } catch (error) { 69 + console.error("Failed to parse webhook body:", error); 70 + return new Response( 71 + JSON.stringify({ error: "Invalid JSON" }), 72 + { 73 + status: 400, 74 + headers: { "Content-Type": "application/json" }, 75 + } 76 + ); 77 + } 78 + 79 + // Handle the update (fire and forget - Telegram expects quick response) 80 + handleUpdate(update).catch((error) => { 81 + console.error("Error handling update:", error); 82 + }); 83 + 84 + // Return 200 OK immediately 85 + return new Response( 86 + JSON.stringify({ ok: true }), 87 + { 88 + status: 200, 89 + headers: { "Content-Type": "application/json" }, 90 + } 91 + ); 92 + } 93 + 94 + // 404 for unknown routes 95 + return new Response( 96 + JSON.stringify({ error: "Not Found" }), 97 + { 98 + status: 404, 99 + headers: { "Content-Type": "application/json" }, 100 + } 101 + ); 102 + }, 103 + }); 104 + 105 + console.log(`Server listening on http://localhost:${config.PORT}`); 106 + 107 + // Start bot in appropriate mode 108 + if (isWebhookMode()) { 109 + console.log(`Webhook mode enabled: ${config.TELEGRAM_WEBHOOK_URL}`); 110 + } else { 111 + console.log("Webhook mode disabled, starting polling for development..."); 112 + try { 113 + await startPolling(); 114 + } catch (error) { 115 + console.error("Failed to start polling mode:", error); 116 + console.error("Bot will not receive messages in polling mode."); 117 + } 118 + } 119 + 120 + console.log("ADHD Support Agent is ready!"); 121 + } 122 + 123 + // Start the server 124 + main().catch((error) => { 125 + console.error("Fatal error starting server:", error); 126 + process.exit(1); 127 + });