Skip to content
Open
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
64 changes: 42 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@ async function main(): Promise<void> {
console.log('[App] Starting Remote Coding Agent (Telegram + Claude MVP)');

// Validate required environment variables
const required = ['DATABASE_URL', 'TELEGRAM_BOT_TOKEN'];
const required = ['DATABASE_URL'];
const missing = required.filter(v => !process.env[v]);
if (missing.length > 0) {
console.error('[App] Missing required environment variables:', missing.join(', '));
console.error('[App] Please check .env.example for required configuration');
process.exit(1);
}

// Validate at least one platform adapter is configured
const hasTelegram = !!process.env.TELEGRAM_BOT_TOKEN;
const hasGitHub = process.env.GITHUB_TOKEN && process.env.WEBHOOK_SECRET;
if (!hasTelegram && !hasGitHub) {
console.error('[App] No platform adapters configured. Set TELEGRAM_BOT_TOKEN or GITHUB_TOKEN+WEBHOOK_SECRET.');
process.exit(1);
}

// Validate AI assistant credentials (warn if missing, don't fail)
const hasClaudeCredentials = process.env.CLAUDE_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN;
const hasCodexCredentials = process.env.CODEX_ID_TOKEN && process.env.CODEX_ACCESS_TOKEN;
Expand Down Expand Up @@ -169,34 +177,41 @@ async function main(): Promise<void> {
console.log(`[Express] Health check server listening on port ${port}`);
});

// Initialize platform adapter (Telegram)
const streamingMode = (process.env.TELEGRAM_STREAMING_MODE || 'stream') as 'stream' | 'batch';
const telegram = new TelegramAdapter(process.env.TELEGRAM_BOT_TOKEN!, streamingMode);
// Initialize Telegram adapter (conditional)
let telegram: TelegramAdapter | null = null;
if (process.env.TELEGRAM_BOT_TOKEN) {
const streamingMode = (process.env.TELEGRAM_STREAMING_MODE || 'stream') as 'stream' | 'batch';
telegram = new TelegramAdapter(process.env.TELEGRAM_BOT_TOKEN, streamingMode);

// Handle text messages
telegram.getBot().on('text', async ctx => {
const conversationId = telegram.getConversationId(ctx);
const message = ctx.message.text;
// Handle text messages
telegram.getBot().on('text', async ctx => {
const conversationId = telegram!.getConversationId(ctx);
const message = ctx.message.text;

if (!message) return;
if (!message) return;

// Fire-and-forget: handler returns immediately, processing happens async
lockManager
.acquireLock(conversationId, async () => {
await handleMessage(telegram, conversationId, message);
})
.catch(error => {
console.error('[Telegram] Failed to process message:', error);
});
});
// Fire-and-forget: handler returns immediately, processing happens async
lockManager
.acquireLock(conversationId, async () => {
await handleMessage(telegram!, conversationId, message);
})
.catch(error => {
console.error('[Telegram] Failed to process message:', error);
});
});

// Start bot
await telegram.start();
// Start bot
await telegram.start();
} else {
console.log('[Telegram] Adapter not initialized (missing TELEGRAM_BOT_TOKEN)');
}

// Graceful shutdown
const shutdown = (): void => {
console.log('[App] Shutting down gracefully...');
telegram.stop();
if (telegram) {
telegram.stop();
}
pool.end().then(() => {
console.log('[Database] Connection pool closed');
process.exit(0);
Expand All @@ -207,7 +222,12 @@ async function main(): Promise<void> {
process.once('SIGTERM', shutdown);

console.log('[App] Remote Coding Agent is ready!');
console.log('[App] Send messages to your Telegram bot to get started');
if (telegram) {
console.log('[App] Telegram bot is active - send messages to get started');
}
if (github) {
console.log('[App] GitHub webhook endpoint active at /webhooks/github');
}
console.log('[App] Test endpoint available: POST http://localhost:' + port + '/test/message');
}

Expand Down