// Single public entrypoint for HF Spaces: local dashboard + reverse proxy to OpenClaw.
const http = require("http");
const https = require("https");
const fs = require("fs");
const net = require("net");
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 TELEGRAM_ENABLED = !!process.env.TELEGRAM_BOT_TOKEN;
const WHATSAPP_ENABLED = /^true$/i.test(process.env.WHATSAPP_ENABLED || "");
const WHATSAPP_STATUS_FILE = "/tmp/huggingclaw-wa-status.json";
const HF_BACKUP_ENABLED = !!(process.env.HF_USERNAME && process.env.HF_TOKEN);
const SYNC_INTERVAL = process.env.SYNC_INTERVAL || "600";
const DASHBOARD_BASE = "/dashboard";
const DASHBOARD_STATUS_PATH = `${DASHBOARD_BASE}/status`;
const DASHBOARD_HEALTH_PATH = `${DASHBOARD_BASE}/health`;
const DASHBOARD_UPTIMEROBOT_PATH = `${DASHBOARD_BASE}/uptimerobot/setup`;
const DASHBOARD_APP_BASE = `${DASHBOARD_BASE}/app`;
const APP_BASE = "/app";
const SPACE_VISIBILITY_TTL_MS = 10 * 60 * 1000;
const spaceVisibilityCache = new Map();
function parseRequestUrl(url) {
try {
return new URL(url, "http://localhost");
} catch {
return new URL("http://localhost/");
}
}
function isDashboardRoute(pathname) {
return (
pathname === "/" ||
pathname === DASHBOARD_BASE ||
pathname === `${DASHBOARD_BASE}/`
);
}
function isDashboardAppRoute(pathname) {
return (
pathname === DASHBOARD_APP_BASE ||
pathname.startsWith(`${DASHBOARD_APP_BASE}/`)
);
}
function isAppRoute(pathname) {
return pathname === APP_BASE || pathname.startsWith(`${APP_BASE}/`);
}
function isLocalRoute(pathname) {
return (
pathname === "/health" ||
pathname === "/status" ||
pathname === "/uptimerobot/setup" ||
pathname === DASHBOARD_HEALTH_PATH ||
pathname === DASHBOARD_STATUS_PATH ||
pathname === DASHBOARD_UPTIMEROBOT_PATH
);
}
function mapAppProxyPath(path) {
if (path === DASHBOARD_APP_BASE) return APP_BASE;
if (path.startsWith(`${DASHBOARD_APP_BASE}/`)) {
return `${APP_BASE}${path.slice(DASHBOARD_APP_BASE.length)}`;
}
if (path === APP_BASE || path.startsWith(`${APP_BASE}/`)) {
return path;
}
return path;
}
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 {}
if (HF_BACKUP_ENABLED) {
return {
status: "configured",
message: `Backup is enabled. Waiting for the next sync window (${SYNC_INTERVAL}s).`,
};
}
return { status: "unknown", message: "No sync data yet" };
}
function normalizeChannelStatus(channel, configured) {
return {
configured: configured || !!channel,
connected: !!(channel && channel.connected),
};
}
function readGuardianStatus() {
if (!WHATSAPP_ENABLED) {
return { configured: false, connected: false, pairing: false };
}
try {
if (fs.existsSync(WHATSAPP_STATUS_FILE)) {
const parsed = JSON.parse(fs.readFileSync(WHATSAPP_STATUS_FILE, "utf8"));
return {
configured: parsed.configured !== false,
connected: parsed.connected === true,
pairing: parsed.pairing === true,
};
}
} catch {}
return { configured: true, connected: false, pairing: false };
}
function decodeJwtPayload(token) {
try {
const parts = String(token || "").split(".");
if (parts.length < 2) return null;
const normalized = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4);
return JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
} catch {
return null;
}
}
function getSpaceRef(parsedUrl) {
const signedToken = parsedUrl.searchParams.get("__sign");
if (!signedToken) return null;
const payload = decodeJwtPayload(signedToken);
const subject = payload && payload.sub;
const match =
typeof subject === "string"
? subject.match(/^\/spaces\/([^/]+)\/([^/]+)$/)
: null;
if (!match) return null;
return { owner: match[1], repo: match[2] };
}
function fetchStatusCode(url) {
return new Promise((resolve, reject) => {
const req = https.get(
url,
{
headers: {
"user-agent": "HuggingClaw/1.0",
accept: "application/json",
},
},
(res) => {
res.resume();
resolve(res.statusCode || 0);
},
);
req.on("error", reject);
req.setTimeout(5000, () => {
req.destroy(new Error("Request timed out"));
});
});
}
async function resolveSpaceIsPrivate(parsedUrl) {
const ref = getSpaceRef(parsedUrl);
if (!ref) return false;
const cacheKey = `${ref.owner}/${ref.repo}`;
const cached = spaceVisibilityCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < SPACE_VISIBILITY_TTL_MS) {
return cached.isPrivate;
}
try {
const statusCode = await fetchStatusCode(
`https://huggingface.co/api/spaces/${ref.owner}/${ref.repo}`,
);
const isPrivate = statusCode === 401 || statusCode === 403 || statusCode === 404;
spaceVisibilityCache.set(cacheKey, { isPrivate, timestamp: Date.now() });
return isPrivate;
} catch {
if (cached) return cached.isPrivate;
return false;
}
}
function renderChannelBadge(channel, configuredLabel) {
if (channel && channel.connected) {
return '
';
}
if (channel && channel.configured) {
return `${configuredLabel}
`;
}
return 'Disabled
';
}
function renderSyncBadge(syncData) {
let badgeClass = "status-offline";
let pulseHtml = "";
if (syncData.status === "success" || syncData.status === "configured") {
badgeClass = "status-online";
pulseHtml = '';
} else if (syncData.status === "syncing") {
badgeClass = "status-syncing";
pulseHtml = '';
}
return `${pulseHtml}${String(syncData.status || "unknown").toUpperCase()}
`;
}
function renderDashboard(initialData) {
const controlUiHref = `${APP_BASE}/`;
const keepAwakeHtml = initialData.spacePrivate
? `
This Space is private. External monitors cannot reliably access private HF health URLs, so keep-awake setup is only available on public Spaces.
`
: `
One-time setup for public Spaces. Paste your UptimeRobot Main API key to create the monitor.
Do not use the Read-only API key or a Monitor-specific API key.
One-time setup. Your key is only used to create the monitor for this Space.
`;
return `
HuggingClaw Dashboard
🦞 HuggingClaw
Space Dashboard
Model
${initialData.model}
Uptime
${initialData.uptime}
WhatsApp
${renderChannelBadge(initialData.whatsapp, "Ready to pair")}
Telegram
${renderChannelBadge(initialData.telegram, "Configured")}
Open Control UI
Last Sync Activity: ${initialData.sync.timestamp || "Never"}
${initialData.sync.message || "Waiting for first sync..."}
Keep Space Awake
${keepAwakeHtml}
`;
}
function readRequestBody(req) {
return new Promise((resolve, reject) => {
let body = "";
req.on("data", (chunk) => {
body += chunk;
if (body.length > 1024 * 64) {
reject(new Error("Request too large"));
req.destroy();
}
});
req.on("end", () => resolve(body));
req.on("error", reject);
});
}
function postUptimeRobot(path, form) {
const body = new URLSearchParams(form).toString();
return new Promise((resolve, reject) => {
const request = https.request(
{
hostname: "api.uptimerobot.com",
port: 443,
method: "POST",
path,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(body),
},
},
(response) => {
let raw = "";
response.setEncoding("utf8");
response.on("data", (chunk) => {
raw += chunk;
});
response.on("end", () => {
try {
resolve(JSON.parse(raw));
} catch {
reject(new Error("Unexpected response from UptimeRobot"));
}
});
},
);
request.on("error", reject);
request.write(body);
request.end();
});
}
async function createUptimeRobotMonitor(apiKey, host) {
const cleanHost = String(host || "")
.replace(/^https?:\/\//, "")
.replace(/\/.*$/, "");
if (!cleanHost) {
throw new Error("Missing Space host.");
}
const monitorUrl = `https://${cleanHost}/health`;
const existing = await postUptimeRobot("/v2/getMonitors", {
api_key: apiKey,
format: "json",
logs: "0",
response_times: "0",
response_times_limit: "1",
});
const existingMonitor = Array.isArray(existing.monitors)
? existing.monitors.find((monitor) => monitor.url === monitorUrl)
: null;
if (existingMonitor) {
return {
created: false,
message: `Monitor already exists for ${monitorUrl}`,
};
}
const created = await postUptimeRobot("/v2/newMonitor", {
api_key: apiKey,
format: "json",
type: "1",
friendly_name: `HuggingClaw ${cleanHost}`,
url: monitorUrl,
interval: "300",
});
if (created.stat !== "ok") {
const message =
created?.error?.message ||
created?.message ||
"Failed to create UptimeRobot monitor.";
throw new Error(message);
}
return {
created: true,
message: `Monitor created for ${monitorUrl}`,
};
}
function proxyHttp(req, res, proxyPath = req.url, proxyPort = GATEWAY_PORT) {
const proxyReq = http.request(
{
hostname: GATEWAY_HOST,
port: proxyPort,
method: req.method,
path: proxyPath,
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,
proxyPath = req.url,
proxyPort = GATEWAY_PORT,
) {
const proxySocket = net.connect(proxyPort, GATEWAY_HOST);
proxySocket.on("connect", () => {
const requestLines = [
`${req.method} ${proxyPath} 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" || pathname === DASHBOARD_HEALTH_PATH) {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
status: "ok",
uptime,
uptimeHuman,
timestamp: new Date().toISOString(),
}),
);
return;
}
if (pathname === "/status" || pathname === DASHBOARD_STATUS_PATH) {
void (async () => {
const guardianStatus = readGuardianStatus();
res.writeHead(200, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
model: LLM_MODEL,
whatsapp: {
configured: guardianStatus.configured,
connected: guardianStatus.connected,
pairing: guardianStatus.pairing,
},
telegram: normalizeChannelStatus(null, TELEGRAM_ENABLED),
sync: readSyncStatus(),
uptime: uptimeHuman,
}),
);
})();
return;
}
if (
pathname === "/uptimerobot/setup" ||
pathname === DASHBOARD_UPTIMEROBOT_PATH
) {
if (req.method !== "POST") {
res.writeHead(405, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Method not allowed" }));
return;
}
void (async () => {
try {
const body = await readRequestBody(req);
const parsed = JSON.parse(body || "{}");
const apiKey = String(parsed.apiKey || "").trim();
if (!apiKey) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
message: "Paste your UptimeRobot Main API key first.",
}),
);
return;
}
const result = await createUptimeRobotMonitor(apiKey, req.headers.host);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(result));
} catch (error) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
message:
error && error.message
? error.message
: "Failed to create UptimeRobot monitor.",
}),
);
}
})();
return;
}
if (isDashboardRoute(pathname)) {
void (async () => {
const guardianStatus = readGuardianStatus();
const initialData = {
model: LLM_MODEL,
whatsapp: {
configured: guardianStatus.configured,
connected: guardianStatus.connected,
pairing: guardianStatus.pairing,
},
telegram: normalizeChannelStatus(null, TELEGRAM_ENABLED),
sync: readSyncStatus(),
uptime: uptimeHuman,
spacePrivate: await resolveSpaceIsPrivate(parsedUrl),
};
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end(renderDashboard(initialData));
})();
return;
}
if (isDashboardAppRoute(pathname) || isAppRoute(pathname)) {
const proxyPath = mapAppProxyPath(pathname) + (parsedUrl.search || "");
proxyHttp(req, res, proxyPath, GATEWAY_PORT);
return;
}
proxyHttp(req, res);
});
server.on("upgrade", (req, socket, head) => {
const pathname = parseRequestUrl(req.url || "/").pathname;
if (isLocalRoute(pathname)) {
socket.destroy();
return;
}
if (isDashboardAppRoute(pathname) || isAppRoute(pathname)) {
const parsedUrl = parseRequestUrl(req.url || "/");
const proxyPath = mapAppProxyPath(pathname) + (parsedUrl.search || "");
proxyUpgrade(req, socket, head, proxyPath, GATEWAY_PORT);
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}`,
);
});