Compare commits
2 Commits
gofai-symb
...
nexus-hear
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d6d536a1c | |||
| 3091d3cdd9 |
2147
public/nexus/app.js
Normal file
2147
public/nexus/app.js
Normal file
File diff suppressed because it is too large
Load Diff
203
server.ts
Normal file
203
server.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
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<WebSocket>();
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
clients.add(ws);
|
||||
console.log(`Client connected to Nexus Bridge. Total: ${clients.size}`);
|
||||
|
||||
ws.on('close', () => {
|
||||
clients.remove(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();
|
||||
Reference in New Issue
Block a user