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
This commit is contained in:
Claude 2026-01-30 07:04:49 +00:00
parent 8f0a3c662a
commit a44d683dd7
No known key found for this signature in database
8 changed files with 210 additions and 34 deletions

View File

@ -6,7 +6,7 @@ Your AI agent that runs on your infrastructure, answers only to you, and you can
## Why AssureBot? ## Why AssureBot?
| Full Moltbot | AssureBot | | Full OpenClaw | AssureBot |
|--------------|----------------| |--------------|----------------|
| 12+ channels | Telegram only | | 12+ channels | Telegram only |
| File-based config | Env vars only | | File-based config | Env vars only |
@ -169,7 +169,7 @@ All webhooks are:
``` ```
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
moltbot-secure │────▶│ sandbox │ assurebot │────▶│ sandbox │
│ (main container) │ │ (Docker sidecar) │ │ (main container) │ │ (Docker sidecar) │
│ │ │ │ │ │ │ │
│ • Telegram bot │ │ • Isolated exec │ │ • Telegram bot │ │ • Isolated exec │
@ -185,8 +185,8 @@ All webhooks are:
## License ## License
MIT - Same as Moltbot. MIT
--- ---
**Full Moltbot**: [github.com/moltbot/moltbot](https://github.com/moltbot/moltbot) Based on [OpenClaw](https://github.com/openclaw/openclaw)

View File

@ -1,5 +1,5 @@
/** /**
* Moltbot Secure - Audit Logger * AssureBot - Audit Logger
* *
* Every interaction is logged for transparency and debugging. * Every interaction is logged for transparency and debugging.
* Logs are append-only JSONL format. * Logs are append-only JSONL format.

View File

@ -1,5 +1,5 @@
/** /**
* Moltbot Secure - Environment-only Configuration * AssureBot - Environment-only Configuration
* *
* All configuration via environment variables. * All configuration via environment variables.
* No config files, no filesystem secrets. * No config files, no filesystem secrets.
@ -177,7 +177,7 @@ export function loadSecureConfig(): SecureConfig {
server: { server: {
port, port,
host: optional("HOST", "0.0.0.0"), host: optional("HOST", "0.0.0.0"),
gatewayToken: optional("MOLTBOT_GATEWAY_TOKEN", generateSecureToken()), gatewayToken: optional("ASSUREBOT_GATEWAY_TOKEN", generateSecureToken()),
}, },
}; };
} }

View File

@ -56,33 +56,40 @@ async function main() {
// Create conversation store // Create conversation store
const conversations = createConversationStore(); const conversations = createConversationStore();
// Create Telegram bot
console.log("[init] Creating Telegram bot...");
const telegram = createTelegramBot({
config,
audit,
agent,
conversations,
});
// Create webhook handler
console.log("[init] Creating webhook handler...");
const webhooks = createWebhookHandler({
config,
audit,
agent,
telegramBot: telegram.bot,
});
// Create sandbox runner // Create sandbox runner
console.log("[init] Creating sandbox runner..."); console.log("[init] Creating sandbox runner...");
const sandbox = createSandboxRunner(config, audit); const sandbox = createSandboxRunner(config, audit);
const sandboxAvailable = await sandbox.isAvailable(); const sandboxAvailable = await sandbox.isAvailable();
console.log(`[init] Sandbox available: ${sandboxAvailable}`); console.log(`[init] Sandbox available: ${sandboxAvailable}`);
// Create scheduler // 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..."); console.log("[init] Creating scheduler...");
const scheduler = createScheduler({ 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, config,
audit, audit,
agent, agent,

View File

@ -1,5 +1,5 @@
/** /**
* Moltbot Secure - Sandbox Execution * AssureBot - Sandbox Execution
* *
* Isolated Docker container for code/script execution. * Isolated Docker container for code/script execution.
* Security-first: no network, read-only root, resource limits. * Security-first: no network, read-only root, resource limits.

View File

@ -1,5 +1,5 @@
/** /**
* Moltbot Secure - Task Scheduler * AssureBot - Task Scheduler
* *
* Simple cron-like scheduler for recurring tasks. * Simple cron-like scheduler for recurring tasks.
* Stores jobs in memory or optionally persists to file. * Stores jobs in memory or optionally persists to file.

View File

@ -9,6 +9,8 @@ import { Bot, Context } from "grammy";
import type { SecureConfig } from "./config.js"; import type { SecureConfig } from "./config.js";
import type { AuditLogger } from "./audit.js"; import type { AuditLogger } from "./audit.js";
import type { AgentCore, ConversationStore, ImageContent } from "./agent.js"; import type { AgentCore, ConversationStore, ImageContent } from "./agent.js";
import type { SandboxRunner } from "./sandbox.js";
import type { Scheduler } from "./scheduler.js";
export type TelegramBot = { export type TelegramBot = {
bot: Bot; bot: Bot;
@ -21,6 +23,8 @@ export type TelegramDeps = {
audit: AuditLogger; audit: AuditLogger;
agent: AgentCore; agent: AgentCore;
conversations: ConversationStore; conversations: ConversationStore;
sandbox?: SandboxRunner;
scheduler?: Scheduler;
onWebhookMessage?: (userId: number, text: string) => void; onWebhookMessage?: (userId: number, text: string) => void;
}; };
@ -37,7 +41,7 @@ function formatUsername(ctx: Context): string {
} }
export function createTelegramBot(deps: TelegramDeps): TelegramBot { export function createTelegramBot(deps: TelegramDeps): TelegramBot {
const { config, audit, agent, conversations } = deps; const { config, audit, agent, conversations, sandbox, scheduler } = deps;
const bot = new Bot(config.telegram.botToken); const bot = new Bot(config.telegram.botToken);
// Error handler // Error handler
@ -70,6 +74,9 @@ Commands:
/start - Show this message /start - Show this message
/clear - Clear conversation history /clear - Clear conversation history
/status - Check bot status /status - Check bot status
/sandbox <code> - Run code in sandbox
/schedule <cron> <task> - Schedule a task
/tasks - List scheduled tasks
/help - Show help /help - Show help
Features: Features:
@ -124,21 +131,183 @@ Features:
- Chat with AI (text messages) - Chat with AI (text messages)
- Image analysis (send photos) - Image analysis (send photos)
- Forward content for analysis - Forward content for analysis
- Receive webhook notifications - Run code in isolated sandbox
- Schedule recurring AI tasks
Commands: Commands:
/start - Welcome message /start - Welcome message
/clear - Clear conversation history /clear - Clear conversation history
/status - Bot status /status - Bot status
/sandbox <code> - Run code in sandbox
/schedule "<cron>" "<name>" <prompt> - Schedule task
/tasks - List scheduled tasks
/deltask <id> - Delete a task
/help - This message /help - This message
Security: Security:
- Only authorized users can interact - Only authorized users can interact
- All interactions are logged - All interactions are logged
- No data is sent to third parties (except AI provider)` - Sandbox runs in isolated Docker (no network)`
); );
}); });
// Command: /sandbox <code>
bot.command("sandbox", async (ctx) => {
const userId = ctx.from?.id;
const username = formatUsername(ctx);
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!sandbox) {
await ctx.reply("Sandbox is not configured.");
return;
}
if (!config.sandbox.enabled) {
await ctx.reply("Sandbox is disabled.");
return;
}
const code = ctx.message?.text?.replace(/^\/sandbox\s*/, "").trim() ?? "";
if (!code) {
await ctx.reply("Usage: /sandbox <code>\n\nExample: /sandbox echo Hello World");
return;
}
const startTime = Date.now();
await ctx.replyWithChatAction("typing");
try {
const result = await sandbox.run(code);
const output = result.stdout || result.stderr || "(no output)";
const status = result.exitCode === 0 ? "✓" : `✗ (exit ${result.exitCode})`;
const timeout = result.timedOut ? " [TIMED OUT]" : "";
await ctx.reply(
`**Sandbox Result** ${status}${timeout}\n\`\`\`\n${output.slice(0, 3000)}\n\`\`\`\nDuration: ${result.durationMs}ms`,
{ parse_mode: "Markdown" }
).catch(async () => {
await ctx.reply(`Sandbox Result ${status}${timeout}\n\n${output.slice(0, 3500)}\n\nDuration: ${result.durationMs}ms`);
});
audit.sandbox({
command: code,
exitCode: result.exitCode,
durationMs: result.durationMs,
});
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
audit.error({ error: `Sandbox error: ${errorMsg}`, metadata: { userId, code } });
await ctx.reply(`Sandbox error: ${errorMsg}`);
}
});
// Command: /schedule <cron> <name> <prompt>
bot.command("schedule", async (ctx) => {
const userId = ctx.from?.id;
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!scheduler) {
await ctx.reply("Scheduler is not configured.");
return;
}
if (!config.scheduler.enabled) {
await ctx.reply("Scheduler is disabled.");
return;
}
// Parse: /schedule "*/5 * * * *" "Task Name" What to do
const text = ctx.message?.text?.replace(/^\/schedule\s*/, "").trim() ?? "";
const match = text.match(/^"([^"]+)"\s+"([^"]+)"\s+(.+)$/s);
if (!match) {
await ctx.reply(
`Usage: /schedule "<cron>" "<name>" <prompt>
Example:
/schedule "0 9 * * *" "Morning Brief" Give me a summary of what I should focus on today
Cron format: minute hour day month weekday
- "0 9 * * *" = 9:00 AM daily
- "*/30 * * * *" = Every 30 minutes
- "0 0 * * 1" = Midnight on Mondays`
);
return;
}
const [, cronExpr, name, prompt] = match;
try {
const taskId = scheduler.addTask({
name,
schedule: cronExpr,
prompt,
enabled: true,
});
await ctx.reply(`Task scheduled!\n\nID: ${taskId}\nName: ${name}\nSchedule: ${cronExpr}`);
} catch (err) {
const errorMsg = err instanceof Error ? err.message : String(err);
await ctx.reply(`Failed to schedule task: ${errorMsg}`);
}
});
// Command: /tasks
bot.command("tasks", async (ctx) => {
const userId = ctx.from?.id;
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!scheduler) {
await ctx.reply("Scheduler is not configured.");
return;
}
const tasks = scheduler.listTasks();
if (tasks.length === 0) {
await ctx.reply("No scheduled tasks.\n\nUse /schedule to create one.");
return;
}
const lines = tasks.map((t) => {
const status = t.enabled ? "✓" : "✗";
const lastRun = t.lastRun ? t.lastRun.toISOString().slice(0, 16) : "never";
return `${status} **${t.name}** (${t.id})\n ${t.schedule}\n Last: ${lastRun}`;
});
await ctx.reply(`**Scheduled Tasks**\n\n${lines.join("\n\n")}`, { parse_mode: "Markdown" }).catch(async () => {
await ctx.reply(`Scheduled Tasks\n\n${lines.join("\n\n").replace(/\*\*/g, "")}`);
});
});
// Command: /deltask <id>
bot.command("deltask", async (ctx) => {
const userId = ctx.from?.id;
if (!userId || !isUserAllowed(userId, config.telegram.allowedUsers)) {
return;
}
if (!scheduler) {
await ctx.reply("Scheduler is not configured.");
return;
}
const taskId = ctx.message?.text?.replace(/^\/deltask\s*/, "").trim() ?? "";
if (!taskId) {
await ctx.reply("Usage: /deltask <task_id>");
return;
}
if (scheduler.removeTask(taskId)) {
await ctx.reply(`Task ${taskId} deleted.`);
} else {
await ctx.reply(`Task ${taskId} not found.`);
}
});
// Handle all text messages // Handle all text messages
bot.on("message:text", async (ctx) => { bot.on("message:text", async (ctx) => {
const userId = ctx.from?.id; const userId = ctx.from?.id;

View File

@ -1,5 +1,5 @@
/** /**
* Moltbot Secure - Webhook Receiver * AssureBot - Webhook Receiver
* *
* Authenticated webhook endpoint for external integrations. * Authenticated webhook endpoint for external integrations.
* Receives events from GitHub, Stripe, uptime monitors, etc. * Receives events from GitHub, Stripe, uptime monitors, etc.
@ -48,8 +48,8 @@ function extractToken(req: IncomingMessage, url: URL): { token: string; fromQuer
return { token: authHeader.slice(7), fromQuery: false }; return { token: authHeader.slice(7), fromQuery: false };
} }
// Check X-Moltbot-Token header // Check X-AssureBot-Token header
const tokenHeader = req.headers["x-moltbot-token"]; const tokenHeader = req.headers["x-assurebot-token"];
if (typeof tokenHeader === "string") { if (typeof tokenHeader === "string") {
return { token: tokenHeader, fromQuery: false }; return { token: tokenHeader, fromQuery: false };
} }