require('dotenv').config(); const dns = require('node:dns'); dns.setDefaultResultOrder('ipv4first'); const express = require('express'); const cors = require('cors'); const path = require('path'); const fs = require('fs'); const memory = require('./skills/memory'); const apiCaller = require('./skills/api_caller'); const app = express(); app.use(cors()); app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); // API Routes app.get('/api/profile', (req, res) => { const db = memory.readDB(); res.json(db.user_profile_snapshot); }); app.get('/api/sprints', (req, res) => { const db = memory.readDB(); res.json(db.active_sprints || []); }); app.get('/api/status', (req, res) => { const status = { openrouter: !!process.env.OPENROUTER_API_KEY, groq: !!process.env.GROQ_API_KEY, telegram: !!process.env.TELEGRAM_BOT_TOKEN, mayar: !!process.env.MAYAR_API_KEY, whop: !!process.env.WHOP_API_KEY, apify: !!process.env.APIFY_API_KEY }; res.json(status); }); const dailyPulse = require('./use_cases/daily_pulse'); const offerArchitect = require('./use_cases/offer_architect'); app.post('/api/generate-image', async (req, res) => { const { prompt } = req.body; if (!prompt) return res.status(400).json({ error: "Prompt is required" }); const result = await apiCaller.generateNanoBananaImage(prompt); res.json(result); }); app.post('/api/usecase/pulse', async (req, res) => { const result = await dailyPulse(); res.json(result); }); app.post('/api/usecase/offer', async (req, res) => { const { topic } = req.body; if (!topic) return res.status(400).json({ error: "Topic is required" }); const result = await offerArchitect(topic); res.json(result); }); // Daily Check-in Mechanism (Simplified) const DAILY_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours const chatID = process.env.TELEGRAM_CHAT_ID; const botToken = process.env.TELEGRAM_BOT_TOKEN; if (chatID && botToken) { console.log(`Telegram Bot Active for Chat ID: ${chatID}`); // Optional: Send immediate boot notification to confirm connection // apiCaller.sendTelegramMessage(chatID, "VinOS Core Online\nSystem is calibrated. Ready for Token Gashapon experiments."); setInterval(async () => { console.log("Triggering daily check-in..."); await apiCaller.sendTelegramMessage(chatID, "VinOS Daily Check-in\nHow is the Token Gashapon project coming along? Ready for the next arbitrage move?"); }, DAILY_INTERVAL); } // Webhook ingestion (for Mayar/Whop) app.post('/api/webhook', (req, res) => { const data = req.body; console.log("General Webhook received:", data); res.status(200).send({ status: 'success' }); }); const { logTelegramMessage } = require('./skills/api_caller'); // SSE clients list for real-time push const sseClients = []; app.get('/api/telegram-stream', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); sseClients.push(res); req.on('close', () => sseClients.splice(sseClients.indexOf(res), 1)); }); const broadcastSSE = (data) => sseClients.forEach(c => c.write(`data: ${JSON.stringify(data)}\n\n`)); app.get('/api/telegram-messages', (req, res) => { const db = memory.readDB(); res.json(db.telegram_log || []); }); app.post('/api/send-telegram', async (req, res) => { const { message } = req.body; if (!message) return res.status(400).json({ error: 'Message required' }); const chatId = process.env.TELEGRAM_CHAT_ID; const result = await apiCaller.sendTelegramMessage(chatId, `[VinOS Dashboard] ${message}`); broadcastSSE({ direction: 'OUT', chatId, text: `[VinOS Dashboard]: ${message}`, ts: new Date().toISOString() }); res.json(result); }); const voiceTranscriber = require('./skills/voice_transcriber'); const intentRouter = require('./skills/intent_router'); const conversationMemory = require('./skills/conversation_memory'); const playbookManager = require('./skills/playbook_manager'); const versionControl = require('./skills/version_control'); const autoCron = require('./skills/infinite_money_cron'); // Start the autonomous routines autoCron.startJobs(); // Helper to handle resolved intents async function handleVinIntent(chatId, from, userText, confirmed = false) { const db = memory.readDB(); const autoMode = db.user_profile_snapshot?.automatic_mode || false; // 1. Resolve intent const { intent, params } = await intentRouter.resolveIntent(userText); // Safety check: if not confirmed and NOT in auto-mode, ask for confirmation if (!confirmed && !autoMode && (intent === 'gashapon' || intent === 'pulse' || intent === 'offer')) { if (!db.pending_commands) db.pending_commands = {}; db.pending_commands[chatId] = { intent, params, userText, ts: Date.now() }; memory.writeDB(db); const confirmationMsg = `🤖 Intent: ${intent}\nParams: ${params || 'none'}\n\nShould I execute? (Reply: Confirm / Cancel)\nTip: Use /auto on for speed mode.`; return await apiCaller.sendTelegramMessage(chatId, confirmationMsg); } // 2. Route to appropriate skill switch (intent) { case 'remember': const playbookId = playbookManager.savePlaybook(`User memory trigger`, params, ['user-defined']); await apiCaller.sendTelegramMessage(chatId, `🧠 Playbook Saved\nI'll remember this approach for the future.\nTrigger: ${params.substring(0, 50)}...`); break; case 'recall': const playbooks = playbookManager.searchPlaybooks(params); if (playbooks.length > 0) { const results = playbooks.slice(0, 2).map(pb => `📌 ${pb.trigger}\n${pb.solution}`).join('\n\n'); await apiCaller.sendTelegramMessage(chatId, `🔍 Found in Playbooks:\n\n${results}`); } else { await apiCaller.sendTelegramMessage(chatId, `🤷 No playbooks found for "${params}".`); } break; case 'gashapon': const prompt = params || userText; await apiCaller.sendTelegramMessage(chatId, "🌀 Spinning the Gashapon for: " + prompt); const gResult = await apiCaller.generateNanoBananaImage(prompt); if (gResult.success) { await apiCaller.sendTelegramMessage(chatId, `✨ Gashapon Result!\nView Image`); } else { await apiCaller.sendTelegramMessage(chatId, "❌ Gashapon failed: " + gResult.error); } break; case 'pulse': await apiCaller.sendTelegramMessage(chatId, "📊 Scanning market..."); await dailyPulse(); break; case 'offer': const topic = params || userText; await apiCaller.sendTelegramMessage(chatId, `💡 Architecting offer for: ${topic}`); await offerArchitect(topic); break; case 'clarify': await apiCaller.sendTelegramMessage(chatId, `🤔 I need a bit more info:\n${params}`); break; case 'chat': case 'research': case 'plan': case 'execute': case 'analyze': case 'create': default: await processOrchestratorIntent(chatId, intent, userText, params); break; } } // Orchestrator handler that uses Memory + Playbooks async function processOrchestratorIntent(chatId, intent, userText, params) { // 1. Get Conversation History const history = conversationMemory.getHistory(chatId); // 2. Load core persona const persona = fs.readFileSync(path.join(__dirname, 'prompts/vin_personality.md'), 'utf8'); // 3. Auto-search Playbooks for relevant context BEFORE thinking const relatedPlaybooks = playbookManager.searchPlaybooks(userText).slice(0, 1); let playbookContext = ""; if (relatedPlaybooks.length > 0) { playbookContext = `\n\n# Relevant Playbook Found:\nUse this proven approach if applicable:\nTrigger: ${relatedPlaybooks[0].trigger}\nSolution: ${relatedPlaybooks[0].solution}`; playbookManager.trackUsage(relatedPlaybooks[0].id); } const systemContent = persona + playbookContext + `\n\n[System Note: Your current intent classification for the upcoming message is: ${intent}]`; const messages = [ { role: "system", content: systemContent }, ...history, { role: "user", content: userText } ]; // Save user message to rolling memory conversationMemory.addMessage(chatId, "user", userText); // Call LLM const chatResult = await apiCaller.callOpenRouter(messages); if (chatResult.success) { // Save Vin's response to rolling memory conversationMemory.addMessage(chatId, "assistant", chatResult.data); await apiCaller.sendTelegramMessage(chatId, chatResult.data); } else { await apiCaller.sendTelegramMessage(chatId, "I'm having trouble thinking clearly right now. Try again in a moment."); } } // Telegram Webhook app.post('/api/telegram-webhook', async (req, res) => { // 1. ACK immediately to Telegram to stop retries res.status(200).send({ status: 'received' }); // 2. Process in background (async () => { const update = req.body; console.log("Telegram Update received:", JSON.stringify(update)); const message = update.message; if (!message) return; const chatId = message.chat.id; const from = message.from?.first_name || 'User'; let userText = message.text; // --- Core Action Commands --- if (userText === '/commit') { const res = versionControl.commitChanges("Manual user commit"); if (res.success) { await apiCaller.sendTelegramMessage(chatId, `✅ Version Logged\nMessage: ${res.message}\nCommit: ${res.version}`); } else { await apiCaller.sendTelegramMessage(chatId, `⚠️ ${res.error}`); } return; } if (userText === '/revert') { const res = versionControl.revertToLastWorking(); if (res.success) { await apiCaller.sendTelegramMessage(chatId, `⏪ Rolled Back\nReverted changes to the last stable version.`); } else { await apiCaller.sendTelegramMessage(chatId, `⚠️ Revert failed: ${res.error}`); } return; } if (userText === '/log') { const res = versionControl.getLog(); await apiCaller.sendTelegramMessage(chatId, `📜 Version History:\n\n
${res.log}`);
return;
}
// Handle /auto on/off commands
if (userText && userText.toLowerCase().startsWith('/auto')) {
const mode = userText.toLowerCase().includes('on') ? true : false;
const db = memory.readDB();
if (!db.user_profile_snapshot) db.user_profile_snapshot = {};
db.user_profile_snapshot.automatic_mode = mode;
memory.writeDB(db);
const status = mode ? "🟢 ON (Speed Mode)" : "🟡 OFF (Safety Mode)";
return await apiCaller.sendTelegramMessage(chatId, `Automatic Mode: ${status}`);
}
// Handle Confirm/Cancel early
if (userText && (userText.toLowerCase() === 'confirm' || userText.toLowerCase() === 'cancel')) {
const db = memory.readDB();
const pending = db.pending_commands?.[chatId];
if (userText.toLowerCase() === 'confirm' && pending) {
delete db.pending_commands[chatId];
memory.writeDB(db);
await apiCaller.sendTelegramMessage(chatId, "✅ Confirmed. Running now...");
return await handleVinIntent(chatId, from, pending.userText, true);
} else if (userText.toLowerCase() === 'cancel' && pending) {
delete db.pending_commands[chatId];
memory.writeDB(db);
return await apiCaller.sendTelegramMessage(chatId, "🤝 Cancelled. What else can I do for you?");
} else {
return await apiCaller.sendTelegramMessage(chatId, "No pending command found to confirm or cancel.");
}
}
if (message.voice) {
await apiCaller.sendTelegramMessage(chatId, "🎤 Received audio... Transcribing...");
const transcription = await voiceTranscriber.transcribeVoice(message.voice.file_id);
if (transcription) {
userText = transcription;
apiCaller.logTelegramMessage('IN', chatId, `[${from} (Voice)]: ${userText}`);
broadcastSSE({ direction: 'IN', chatId, text: `[${from} (Voice)]: ${userText}`, ts: new Date().toISOString() });
} else {
return await apiCaller.sendTelegramMessage(chatId, "❌ Transcription failed. Could you try again?");
}
} else if (userText) {
const isControl = /^(confirm|cancel|\/auto|\/start)/i.test(userText);
if (!isControl) {
await apiCaller.sendTelegramMessage(chatId, "⚡ Instruction received. Processing...");
}
apiCaller.logTelegramMessage('IN', chatId, `[${from}]: ${userText}`);
broadcastSSE({ direction: 'IN', chatId, text: `[${from}]: ${userText}`, ts: new Date().toISOString() });
}
// Process commands or NL
if (userText) {
if (userText.startsWith('/start')) {
await apiCaller.sendTelegramMessage(chatId, "VinOS Core Online");
} else {
await handleVinIntent(chatId, from, userText);
}
}
})().catch(err => console.error("Webhook processing error:", err));
});
const PORT = process.env.PORT || 7860;
app.listen(PORT, '0.0.0.0', () => {
console.log(`VinOS Core Online: http://0.0.0.0:${PORT}`);
});