openclaw/secure/index.ts
Claude a44d683dd7
feat: wire up sandbox/scheduler commands + complete AssureBot rebrand
- Add /sandbox, /schedule, /tasks, /deltask commands to telegram bot
- Wire sandbox and scheduler dependencies through to telegram handler
- Complete AssureBot rebrand across all files (audit, config, webhooks, etc.)
- Update secure/README.md with correct branding
- Update X-AssureBot-Token header for webhooks
- Update ASSUREBOT_GATEWAY_TOKEN env var

https://claude.ai/code/session_015VqJ7gN4vaxtYfYc92UjLs
2026-01-30 07:04:49 +00:00

201 lines
5.6 KiB
TypeScript

/**
* AssureBot - Entry Point
*
* Lean, secure, self-hosted AI assistant for Railway.
*
* Usage:
* TELEGRAM_BOT_TOKEN=xxx ANTHROPIC_API_KEY=xxx ALLOWED_USERS=123 npx tsx secure/index.ts
*/
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { loadSecureConfig, validateConfig, redactConfig } from "./config.js";
import { createAuditLogger } from "./audit.js";
import { createAgent, createConversationStore } from "./agent.js";
import { createTelegramBot } from "./telegram.js";
import { createWebhookHandler } from "./webhooks.js";
import { createSandboxRunner } from "./sandbox.js";
import { createScheduler } from "./scheduler.js";
async function main() {
console.log("=".repeat(50));
console.log(" ASSUREBOT");
console.log(" Lean, secure, self-hosted AI assistant");
console.log("=".repeat(50));
console.log();
// Load configuration
console.log("[init] Loading configuration...");
const config = loadSecureConfig();
// Validate and warn
const warnings = validateConfig(config);
if (warnings.length > 0) {
console.log("[init] Configuration warnings:");
for (const w of warnings) {
console.log(` - ${w}`);
}
}
// Log redacted config
console.log("[init] Configuration loaded:");
console.log(JSON.stringify(redactConfig(config), null, 2));
console.log();
// Create audit logger
console.log("[init] Creating audit logger...");
const audit = createAuditLogger({
enabled: config.audit.enabled,
logPath: config.audit.logPath,
});
audit.startup();
// Create AI agent
console.log(`[init] Creating AI agent (${config.ai.provider})...`);
const agent = createAgent(config, audit);
// Create conversation store
const conversations = createConversationStore();
// Create sandbox runner
console.log("[init] Creating sandbox runner...");
const sandbox = createSandboxRunner(config, audit);
const sandboxAvailable = await sandbox.isAvailable();
console.log(`[init] Sandbox available: ${sandboxAvailable}`);
// Create a placeholder bot for circular deps
// We'll create telegram, scheduler, and webhooks together
const { Bot } = await import("grammy");
const bot = new Bot(config.telegram.botToken);
// Create scheduler (needs bot for notifications)
console.log("[init] Creating scheduler...");
const scheduler = createScheduler({
config,
audit,
agent,
telegramBot: bot,
});
// Create Telegram bot handler (with sandbox and scheduler)
console.log("[init] Creating Telegram bot...");
const telegram = createTelegramBot({
config,
audit,
agent,
conversations,
sandbox,
scheduler,
});
// Create webhook handler
console.log("[init] Creating webhook handler...");
const webhooks = createWebhookHandler({
config,
audit,
agent,
telegramBot: telegram.bot,
});
// Create HTTP server
console.log("[init] Creating HTTP server...");
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
// Health check
if (url.pathname === "/health" || url.pathname === "/healthz") {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({
status: "ok",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
telegram: "connected",
sandbox: sandboxAvailable ? "available" : "unavailable",
}));
return;
}
// Readiness check
if (url.pathname === "/ready") {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain");
res.end("ready");
return;
}
// Webhook handler
if (await webhooks.handleRequest(req, res)) {
return;
}
// 404 for everything else
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain");
res.end("Not Found");
});
// Graceful shutdown
let isShuttingDown = false;
async function shutdown(signal: string) {
if (isShuttingDown) return;
isShuttingDown = true;
console.log(`\n[shutdown] Received ${signal}, shutting down...`);
audit.shutdown();
try {
scheduler.stop();
await telegram.stop();
await new Promise<void>((resolve, reject) => {
server.close((err) => {
if (err) reject(err);
else resolve();
});
});
console.log("[shutdown] Shutdown complete");
process.exit(0);
} catch (err) {
console.error("[shutdown] Error during shutdown:", err);
process.exit(1);
}
}
process.on("SIGTERM", () => void shutdown("SIGTERM"));
process.on("SIGINT", () => void shutdown("SIGINT"));
// Start everything
console.log("[start] Starting services...");
// Start HTTP server
server.listen(config.server.port, config.server.host, () => {
console.log(`[start] HTTP server listening on ${config.server.host}:${config.server.port}`);
});
// Start scheduler
scheduler.start();
// Start Telegram bot (polling mode for simplicity)
await telegram.start();
console.log();
console.log("=".repeat(50));
console.log(" ASSUREBOT IS RUNNING");
console.log();
console.log(` Telegram: Polling mode`);
console.log(` Webhooks: http://localhost:${config.server.port}${config.webhooks.basePath}/*`);
console.log(` Health: http://localhost:${config.server.port}/health`);
console.log(` Allowed: ${config.telegram.allowedUsers.length} users`);
console.log();
console.log(" Press Ctrl+C to stop");
console.log("=".repeat(50));
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});