// Single public entrypoint for HF Spaces: local dashboard + reverse proxy to OpenClaw. const http = require("http"); const fs = require("fs"); const net = require("net"); const { randomUUID } = require("node:crypto"); const PORT = 7861; const GATEWAY_PORT = 7860; const GATEWAY_HOST = "127.0.0.1"; const startTime = Date.now(); const LLM_MODEL = process.env.LLM_MODEL || "Not Set"; const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || ""; const TELEGRAM_ENABLED = !!process.env.TELEGRAM_BOT_TOKEN; const GATEWAY_STATUS_CACHE_MS = 5000; let gatewayStatusCache = { expiresAt: 0, value: { whatsapp: { configured: true, connected: false }, telegram: { configured: TELEGRAM_ENABLED, connected: false }, }, }; function parseRequestUrl(url) { try { return new URL(url, "http://localhost"); } catch { return new URL("http://localhost/"); } } function isDashboardRoute(pathname) { return pathname === "/dashboard" || pathname === "/dashboard/"; } function isLocalRoute(pathname) { return pathname === "/health" || pathname === "/status" || isDashboardRoute(pathname); } function appendForwarded(existingValue, nextValue) { const cleanNext = nextValue || ""; if (!existingValue) return cleanNext; if (Array.isArray(existingValue)) return `${existingValue.join(", ")}, ${cleanNext}`; return `${existingValue}, ${cleanNext}`; } function buildProxyHeaders(headers, remoteAddress) { return { ...headers, host: headers.host || `${GATEWAY_HOST}:${GATEWAY_PORT}`, "x-forwarded-for": appendForwarded( headers["x-forwarded-for"], remoteAddress, ), "x-forwarded-host": headers["x-forwarded-host"] || headers.host || "", "x-forwarded-proto": headers["x-forwarded-proto"] || "https", }; } function readSyncStatus() { try { if (fs.existsSync("/tmp/sync-status.json")) { return JSON.parse(fs.readFileSync("/tmp/sync-status.json", "utf8")); } } catch {} return { status: "unknown", message: "No sync data yet" }; } function normalizeChannelStatus(channel, configured) { return { configured: configured || !!channel, connected: !!(channel && channel.connected), }; } function extractErrorMessage(msg) { if (!msg || typeof msg !== "object") return "Unknown error"; if (typeof msg.error === "string") return msg.error; if (msg.error && typeof msg.error.message === "string") return msg.error.message; if (typeof msg.message === "string") return msg.message; return "Unknown error"; } function createGatewayConnection() { return new Promise((resolve, reject) => { const { WebSocket } = require("/home/node/.openclaw/openclaw-app/node_modules/ws"); const ws = new WebSocket(`ws://${GATEWAY_HOST}:${GATEWAY_PORT}`); let resolved = false; ws.on("message", (data) => { const msg = JSON.parse(data.toString()); if (msg.type === "event" && msg.event === "connect.challenge") { ws.send(JSON.stringify({ type: "req", id: randomUUID(), method: "connect", params: { minProtocol: 3, maxProtocol: 3, client: { id: "health-server", version: "1.0.0", platform: "linux", mode: "backend", }, caps: [], auth: { token: GATEWAY_TOKEN }, role: "operator", scopes: ["operator.read"], }, })); return; } if (!resolved && msg.type === "res" && msg.ok === false) { resolved = true; ws.close(); reject(new Error(extractErrorMessage(msg))); return; } if (!resolved && msg.type === "res" && msg.ok) { resolved = true; resolve(ws); } }); ws.on("error", (error) => { if (!resolved) reject(error); }); setTimeout(() => { if (!resolved) { ws.close(); reject(new Error("Timeout")); } }, 10000); }); } function callGatewayRpc(ws, method, params) { return new Promise((resolve, reject) => { const id = randomUUID(); const handler = (data) => { const msg = JSON.parse(data.toString()); if (msg.id === id) { ws.removeListener("message", handler); resolve(msg); } }; ws.on("message", handler); ws.send(JSON.stringify({ type: "req", id, method, params })); setTimeout(() => { ws.removeListener("message", handler); reject(new Error("RPC Timeout")); }, 15000); }); } async function getGatewayChannelStatus() { if (Date.now() < gatewayStatusCache.expiresAt) { return gatewayStatusCache.value; } let ws; try { ws = await createGatewayConnection(); const statusRes = await callGatewayRpc(ws, "channels.status", {}); const channels = (statusRes.payload || statusRes.result)?.channels || {}; const value = { whatsapp: normalizeChannelStatus(channels.whatsapp, true), telegram: normalizeChannelStatus(channels.telegram, TELEGRAM_ENABLED), }; gatewayStatusCache = { expiresAt: Date.now() + GATEWAY_STATUS_CACHE_MS, value, }; return value; } catch { return gatewayStatusCache.value; } finally { if (ws) ws.close(); } } function renderDashboard() { return ` HuggingClaw Dashboard

🦞 HuggingClaw

Space Dashboard

Model Loading...
Uptime Loading...
WhatsApp Loading...
Telegram Loading...
Open Control UI
Workspace Sync Status
Last Sync Activity: Never Initializing synchronization...
`; } function proxyHttp(req, res) { const proxyReq = http.request( { hostname: GATEWAY_HOST, port: GATEWAY_PORT, method: req.method, path: req.url, headers: buildProxyHeaders(req.headers, req.socket.remoteAddress), }, (proxyRes) => { res.writeHead(proxyRes.statusCode || 502, proxyRes.headers); proxyRes.pipe(res); }, ); proxyReq.on("error", (error) => { res.writeHead(502, { "Content-Type": "application/json" }); res.end( JSON.stringify({ status: "error", message: "Gateway unavailable", detail: error.message, }), ); }); req.pipe(proxyReq); } function serializeUpgradeHeaders(req, remoteAddress) { const forwardedHeaders = []; for (let i = 0; i < req.rawHeaders.length; i += 2) { const name = req.rawHeaders[i]; const value = req.rawHeaders[i + 1]; const lower = name.toLowerCase(); if ( lower === "x-forwarded-for" || lower === "x-forwarded-host" || lower === "x-forwarded-proto" ) { continue; } forwardedHeaders.push(`${name}: ${value}`); } forwardedHeaders.push( `X-Forwarded-For: ${appendForwarded(req.headers["x-forwarded-for"], remoteAddress)}`, ); forwardedHeaders.push( `X-Forwarded-Host: ${req.headers["x-forwarded-host"] || req.headers.host || ""}`, ); forwardedHeaders.push( `X-Forwarded-Proto: ${req.headers["x-forwarded-proto"] || "https"}`, ); return forwardedHeaders; } function proxyUpgrade(req, socket, head) { const proxySocket = net.connect(GATEWAY_PORT, GATEWAY_HOST); proxySocket.on("connect", () => { const requestLines = [ `${req.method} ${req.url} HTTP/${req.httpVersion}`, ...serializeUpgradeHeaders(req, req.socket.remoteAddress), "", "", ]; proxySocket.write(requestLines.join("\r\n")); if (head && head.length > 0) { proxySocket.write(head); } socket.pipe(proxySocket).pipe(socket); }); proxySocket.on("error", () => { if (socket.writable) { socket.write("HTTP/1.1 502 Bad Gateway\r\nConnection: close\r\n\r\n"); } socket.destroy(); }); socket.on("error", () => { proxySocket.destroy(); }); } const server = http.createServer((req, res) => { const parsedUrl = parseRequestUrl(req.url || "/"); const pathname = parsedUrl.pathname; const uptime = Math.floor((Date.now() - startTime) / 1000); const uptimeHuman = `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`; if (pathname === "/health") { res.writeHead(200, { "Content-Type": "application/json" }); res.end( JSON.stringify({ status: "ok", uptime, uptimeHuman, timestamp: new Date().toISOString(), }), ); return; } if (pathname === "/status") { void (async () => { const channelStatus = await getGatewayChannelStatus(); res.writeHead(200, { "Content-Type": "application/json" }); res.end( JSON.stringify({ model: LLM_MODEL, whatsapp: channelStatus.whatsapp, telegram: channelStatus.telegram, sync: readSyncStatus(), uptime: uptimeHuman, }), ); })(); return; } if (isDashboardRoute(pathname)) { res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); res.end(renderDashboard()); return; } proxyHttp(req, res); }); server.on("upgrade", (req, socket, head) => { const pathname = parseRequestUrl(req.url || "/").pathname; if (isLocalRoute(pathname)) { socket.destroy(); return; } proxyUpgrade(req, socket, head); }); server.listen(PORT, "0.0.0.0", () => { console.log( `Health server listening on port ${PORT}; proxying gateway traffic to ${GATEWAY_HOST}:${GATEWAY_PORT}`, ); });