import express from 'express'; import { createServer as createViteServer } from 'vite'; import path from 'path'; import { fileURLToPath } from 'url'; import 'dotenv/config'; import { WebSocketServer, WebSocket } from 'ws'; import { createServer } from 'http'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Primary (Local) Gitea const GITEA_URL = process.env.GITEA_URL || 'http://localhost:3000/api/v1'; const GITEA_TOKEN = process.env.GITEA_TOKEN || ''; // Backup (Remote) Gitea const REMOTE_GITEA_URL = process.env.REMOTE_GITEA_URL || 'http://143.198.27.163:3000/api/v1'; const REMOTE_GITEA_TOKEN = process.env.REMOTE_GITEA_TOKEN || ''; async function startServer() { const app = express(); const httpServer = createServer(app); const PORT = 3000; // WebSocket Server for Hermes/Evennia Bridge const wss = new WebSocketServer({ noServer: true }); const clients = new Set(); wss.on('connection', (ws) => { clients.add(ws); console.log(`Client connected to Nexus Bridge. Total: ${clients.size}`); ws.on('close', () => { clients.delete(ws); console.log(`Client disconnected. Total: ${clients.size}`); }); }); // Simulate Evennia Heartbeat (Source of Truth) setInterval(() => { const heartbeat = { type: 'heartbeat', frequency: 0.5 + Math.random() * 0.2, // 0.5Hz to 0.7Hz intensity: 0.8 + Math.random() * 0.4, timestamp: Date.now(), source: 'evonia-layer' }; const message = JSON.stringify(heartbeat); clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(message); } }); }, 2000); app.use(express.json({ limit: '50mb' })); // Diagnostic Endpoint for Agent Inspection app.get('/api/diagnostic/inspect', async (req, res) => { console.log('Diagnostic request received'); try { const REPO_OWNER = 'google'; const REPO_NAME = 'timmy-tower'; const [stateRes, issuesRes] = await Promise.all([ fetch(`${GITEA_URL}/repos/${REPO_OWNER}/${REPO_NAME}/contents/world_state.json`, { headers: { 'Authorization': `token ${GITEA_TOKEN}` } }), fetch(`${GITEA_URL}/repos/${REPO_OWNER}/${REPO_NAME}/issues?state=all`, { headers: { 'Authorization': `token ${GITEA_TOKEN}` } }) ]); let worldState = null; if (stateRes.ok) { const content = await stateRes.json(); worldState = JSON.parse(Buffer.from(content.content, 'base64').toString()); } else if (stateRes.status !== 404) { console.error(`Failed to fetch world state: ${stateRes.status} ${stateRes.statusText}`); } let issues = []; if (issuesRes.ok) { issues = await issuesRes.json(); } else { console.error(`Failed to fetch issues: ${issuesRes.status} ${issuesRes.statusText}`); } res.json({ worldState, issues, repoExists: stateRes.status !== 404, connected: GITEA_TOKEN !== '' }); } catch (error: any) { console.error('Diagnostic error:', error); res.status(500).json({ error: error.message }); } }); // Helper for Gitea Proxy const createGiteaProxy = (baseUrl: string, token: string) => async (req: express.Request, res: express.Response) => { const path = req.params[0] + (req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : ''); const url = `${baseUrl}/${path}`; if (!token) { console.warn(`Gitea Proxy Warning: No token provided for ${baseUrl}`); } try { const response = await fetch(url, { method: req.method, headers: { 'Content-Type': 'application/json', 'Authorization': `token ${token}`, }, body: ['GET', 'HEAD'].includes(req.method) ? undefined : JSON.stringify(req.body), }); const data = await response.text(); res.status(response.status).send(data); } catch (error: any) { console.error(`Gitea Proxy Error (${baseUrl}):`, error); res.status(500).json({ error: error.message }); } }; // Gitea Proxy - Primary (Local) app.get('/api/gitea/check', async (req, res) => { try { const response = await fetch(`${GITEA_URL}/user`, { headers: { 'Authorization': `token ${GITEA_TOKEN}` } }); if (response.ok) { const user = await response.json(); res.json({ status: 'connected', user: user.username }); } else { res.status(response.status).json({ status: 'error', message: `Gitea returned ${response.status}` }); } } catch (error: any) { res.status(500).json({ status: 'error', message: error.message }); } }); app.all('/api/gitea/*', createGiteaProxy(GITEA_URL, GITEA_TOKEN)); // Gitea Proxy - Backup (Remote) app.get('/api/gitea-remote/check', async (req, res) => { try { const response = await fetch(`${REMOTE_GITEA_URL}/user`, { headers: { 'Authorization': `token ${REMOTE_GITEA_TOKEN}` } }); if (response.ok) { const user = await response.json(); res.json({ status: 'connected', user: user.username }); } else { res.status(response.status).json({ status: 'error', message: `Gitea returned ${response.status}` }); } } catch (error: any) { res.status(500).json({ status: 'error', message: error.message }); } }); app.all('/api/gitea-remote/*', createGiteaProxy(REMOTE_GITEA_URL, REMOTE_GITEA_TOKEN)); // WebSocket Upgrade Handler httpServer.on('upgrade', (request, socket, head) => { const pathname = new URL(request.url!, `http://${request.headers.host}`).pathname; if (pathname === '/api/world/ws') { wss.handleUpgrade(request, socket, head, (ws) => { wss.emit('connection', ws, request); }); } else { socket.destroy(); } }); // Health Check app.get('/api/health', (req, res) => { res.json({ status: 'ok' }); }); // Vite middleware for development if (process.env.NODE_ENV !== 'production') { const vite = await createViteServer({ server: { middlewareMode: true }, appType: 'spa', }); app.use(vite.middlewares); } else { const distPath = path.join(process.cwd(), 'dist'); app.use(express.static(distPath)); app.get('*', (req, res) => { res.sendFile(path.join(distPath, 'index.html')); }); } httpServer.listen(PORT, '0.0.0.0', () => { console.log(`Server running on http://localhost:${PORT}`); }); } startServer();