feat(headroom): add proxy lifecycle management + dashboard UI

Build on the optional Headroom Token Saver from Carmelo Campos
(PR: feat: add optional Headroom token saver). Add managed start/stop
of the local headroom proxy from the dashboard, install detection,
status probing, and a simplified Token Saver UI.

- detect headroom CLI + python>=3.10, probe proxy /health
- spawn/stop proxy as a detached, pid-tracked process
- /api/headroom/{status,start,stop} routes, gated local-only in dashboardGuard
- one-click Start/Stop Headroom modal, no manual config needed
- claude<->openai shape conversion for /v1/compress via 9router translators

Thanks to Carmelo Campos (@carmelogunsroses) for the original Headroom integration.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
decolua
2026-06-20 10:09:50 +07:00
co-authored by Cursor
parent efd20be8d8
commit b55cf36d2e
33 changed files with 1019 additions and 130 deletions
@@ -14,6 +14,7 @@ import {
REACHABLE_MISS_THRESHOLD,
CLIENT_PING_FAST_MS,
CAVEMAN_LEVELS,
PONYTAIL_LEVELS,
} from "./endpointConstants";
import { clientPingUrl, clientPingAny } from "./endpointPing";
import EndpointRow from "./components/EndpointRow";
@@ -33,8 +34,17 @@ export default function APIPageClient({ machineId }) {
const [hasPassword, setHasPassword] = useState(true);
const [tunnelDashboardAccess, setTunnelDashboardAccess] = useState(false);
const [rtkEnabled, setRtkEnabledState] = useState(true);
const [headroomEnabled, setHeadroomEnabled] = useState(false);
const [headroomUrl, setHeadroomUrl] = useState("http://localhost:8787");
const [headroomCompressUserMessages, setHeadroomCompressUserMessages] = useState(false);
const [headroomStatus, setHeadroomStatus] = useState({ installed: false, running: false, python: null, loading: true });
const [showHeadroomInstallModal, setShowHeadroomInstallModal] = useState(false);
const [headroomActionLoading, setHeadroomActionLoading] = useState(false);
const [headroomActionError, setHeadroomActionError] = useState("");
const [cavemanEnabled, setCavemanEnabled] = useState(false);
const [cavemanLevel, setCavemanLevel] = useState("full");
const [ponytailEnabled, setPonytailEnabled] = useState(false);
const [ponytailLevel, setPonytailLevel] = useState("full");
const [locale, setLocale] = useState("en");
// Cloudflare Tunnel state
@@ -232,8 +242,14 @@ export default function APIPageClient({ machineId }) {
setHasPassword(data.hasPassword || false);
setTunnelDashboardAccess(data.tunnelDashboardAccess || false);
setRtkEnabledState(data.rtkEnabled !== false);
setHeadroomEnabled(!!data.headroomEnabled);
setHeadroomUrl(data.headroomUrl || "http://localhost:8787");
setHeadroomCompressUserMessages(!!data.headroomCompressUserMessages);
refreshHeadroomStatus();
setCavemanEnabled(!!data.cavemanEnabled);
setCavemanLevel(data.cavemanLevel || "full");
setPonytailEnabled(!!data.ponytailEnabled);
setPonytailLevel(data.ponytailLevel || "full");
}
if (statusRes.ok) {
const data = await statusRes.json();
@@ -313,11 +329,75 @@ export default function APIPageClient({ machineId }) {
patchSetting({ cavemanEnabled: value });
};
const handleHeadroomEnabled = (value) => {
const nextUrl = headroomUrl.trim() || "http://localhost:8787";
setHeadroomUrl(nextUrl);
setHeadroomEnabled(value);
patchSetting({ headroomEnabled: value, headroomUrl: nextUrl });
};
const handleHeadroomUrlBlur = () => {
const next = headroomUrl.trim() || "http://localhost:8787";
setHeadroomUrl(next);
patchSetting({ headroomUrl: next });
};
const handleHeadroomCompressUserMessages = (value) => {
setHeadroomCompressUserMessages(value);
patchSetting({ headroomCompressUserMessages: value });
};
const refreshHeadroomStatus = useCallback(async () => {
setHeadroomStatus((s) => ({ ...s, loading: true }));
try {
const res = await fetch("/api/headroom/status", { headers: { "Cache-Control": "no-store" } });
const data = await res.json();
setHeadroomStatus({ ...data, loading: false });
} catch {
setHeadroomStatus({ installed: false, running: false, python: null, loading: false });
}
}, []);
const handleHeadroomStart = useCallback(async () => {
setHeadroomActionError("");
setHeadroomActionLoading(true);
try {
const res = await fetch("/api/headroom/start", { method: "POST" });
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data.error || "Failed to start proxy");
await refreshHeadroomStatus();
} catch (e) {
setHeadroomActionError(e.message);
} finally {
setHeadroomActionLoading(false);
}
}, [refreshHeadroomStatus]);
const handleHeadroomStop = useCallback(async () => {
setHeadroomActionLoading(true);
try {
await fetch("/api/headroom/stop", { method: "POST" });
await refreshHeadroomStatus();
} finally {
setHeadroomActionLoading(false);
}
}, [refreshHeadroomStatus]);
const handleCavemanLevel = (level) => {
setCavemanLevel(level);
patchSetting({ cavemanLevel: level });
};
const handlePonytailEnabled = (value) => {
setPonytailEnabled(value);
patchSetting({ ponytailEnabled: value });
};
const handlePonytailLevel = (level) => {
setPonytailLevel(level);
patchSetting({ ponytailLevel: level });
};
const fetchData = async () => {
try {
const keysRes = await fetch("/api/keys");
@@ -1043,6 +1123,47 @@ export default function APIPageClient({ machineId }) {
onChange={() => handleRtkEnabled(!rtkEnabled)}
/>
</div>
<div className="flex items-center justify-between py-4 border-b border-border gap-4 flex-wrap">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-3 flex-wrap">
<p className="font-medium">
Compress context{" "}
<a
href="https://github.com/chopratejas/headroom"
target="_blank"
rel="noreferrer"
className="text-xs font-normal text-primary underline hover:opacity-80"
>
(Headroom)
</a>
</p>
<span className={`text-xs px-2 py-0.5 rounded ${headroomStatus.installed && headroomStatus.running ? "bg-success/15 text-success" : "bg-warning/15 text-warning"}`}>
{headroomStatus.loading
? "Checking…"
: !headroomStatus.installed
? "Not installed"
: !headroomStatus.running
? "Proxy off"
: "Running"}
</span>
<button
type="button"
onClick={() => setShowHeadroomInstallModal(true)}
className="text-xs text-primary underline hover:opacity-80"
>
{headroomStatus.installed && headroomStatus.running ? "Manage" : "Setup"}
</button>
</div>
<p className="text-sm text-text-muted mt-1">
Compress prompts via /v1/compress before routing to the model
</p>
</div>
<Toggle
checked={headroomEnabled && headroomStatus.installed && headroomStatus.running}
disabled={!headroomStatus.installed || !headroomStatus.running}
onChange={() => handleHeadroomEnabled(!headroomEnabled)}
/>
</div>
<div className="flex items-center justify-between pt-4 gap-4 flex-wrap">
<div className="min-w-0 flex-1">
<p className="font-medium">
@@ -1090,6 +1211,53 @@ export default function APIPageClient({ machineId }) {
/>
</div>
</div>
<div className="flex items-center justify-between pt-4 mt-4 border-t border-border gap-4 flex-wrap">
<div className="min-w-0 flex-1">
<p className="font-medium">
Lazy senior dev{" "}
<a
href="https://github.com/DietrichGebert/ponytail"
target="_blank"
rel="noreferrer"
className="text-xs font-normal text-primary underline hover:opacity-80"
>
(Ponytail)
</a>
</p>
<p className="text-sm text-text-muted">
Bias the model toward minimal code: YAGNI, reuse stdlib, deletion over addition
</p>
</div>
<div className="flex items-center gap-3 shrink-0">
{ponytailEnabled && (
<div className="flex flex-col items-end gap-1">
<div className="flex items-center gap-1.5">
{PONYTAIL_LEVELS.map((lvl) => (
<button
key={lvl.id}
onClick={() => handlePonytailLevel(lvl.id)}
className={`px-3 py-1.5 rounded text-xs font-medium border transition-colors ${
ponytailLevel === lvl.id
? "bg-primary text-white border-primary"
: "bg-transparent border-border text-text-muted hover:bg-surface-2"
}`}
title={lvl.desc}
>
{lvl.label}
</button>
))}
</div>
<p className="text-xs text-primary">
{PONYTAIL_LEVELS.find((lvl) => lvl.id === ponytailLevel)?.desc}
</p>
</div>
)}
<Toggle
checked={ponytailEnabled}
onChange={() => handlePonytailEnabled(!ponytailEnabled)}
/>
</div>
</div>
</Card>
{/* API Keys */}
@@ -1420,6 +1588,58 @@ export default function APIPageClient({ machineId }) {
</div>
</Modal>
{/* Headroom Install Guide Modal */}
<Modal
isOpen={showHeadroomInstallModal}
title={headroomStatus.installed ? "Headroom" : "Install Headroom"}
onClose={() => setShowHeadroomInstallModal(false)}
>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between text-sm">
<span>Status</span>
<span className={headroomStatus.installed && headroomStatus.running ? "text-success" : "text-warning"}>
{headroomStatus.loading
? "Checking…"
: !headroomStatus.installed
? "Not installed"
: !headroomStatus.running
? "Proxy off"
: "Running"}
</span>
</div>
{headroomStatus.installed ? (
headroomStatus.running ? (
<Button onClick={handleHeadroomStop} variant="ghost" fullWidth disabled={headroomActionLoading}>
{headroomActionLoading ? "Stopping…" : "Stop Headroom"}
</Button>
) : (
<Button onClick={handleHeadroomStart} fullWidth disabled={headroomActionLoading}>
{headroomActionLoading ? "Starting…" : "Start Headroom"}
</Button>
)
) : !headroomStatus.python ? (
<p className="text-sm text-warning">Python 3.10 required. Install Python first.</p>
) : (
<div className="flex flex-col gap-1">
<p className="text-sm font-medium">Install then click Start:</p>
<div className="flex items-center gap-2">
<pre className="flex-1 rounded bg-black/5 dark:bg-white/5 p-2 text-xs font-mono overflow-x-auto">{`pip install "headroom-ai[proxy]"`}</pre>
<Button size="sm" variant="ghost" onClick={() => copy(`pip install "headroom-ai[proxy]"`)}>
{copied ? "Copied" : "Copy"}
</Button>
</div>
</div>
)}
{headroomActionError && (
<p className="text-sm text-warning">{headroomActionError}</p>
)}
<div className="flex gap-2">
<Button onClick={() => refreshHeadroomStatus()} variant="ghost" fullWidth>Recheck</Button>
<Button onClick={() => setShowHeadroomInstallModal(false)} fullWidth>Done</Button>
</div>
</div>
</Modal>
{/* Confirm Modal */}
<ConfirmModal
isOpen={!!confirmState}
@@ -24,3 +24,9 @@ export const CAVEMAN_LEVELS = [
{ id: "wenyan", label: "文 Full", desc: "Maximum 文言文, 80-90% reduction", wenyan: true },
{ id: "wenyan-ultra", label: "文 Ultra", desc: "Extreme classical compression", wenyan: true },
];
export const PONYTAIL_LEVELS = [
{ id: "lite", label: "Lite", desc: "Build asked, name lazier option" },
{ id: "full", label: "Full", desc: "Ladder enforced: stdlib/native first" },
{ id: "ultra", label: "Ultra", desc: "YAGNI extremist, deletion first" },
];
+27
View File
@@ -0,0 +1,27 @@
import { NextResponse } from "next/server";
import { getSettings } from "@/lib/localDb";
import { startHeadroomProxy } from "@/lib/headroom/process";
export const dynamic = "force-dynamic";
function parsePortFromUrl(url) {
try {
const u = new URL(url);
const p = parseInt(u.port, 10);
if (p > 0 && p < 65536) return p;
} catch { /* ignore, fall through to default */ }
return null;
}
export async function POST() {
try {
const settings = await getSettings();
const url = settings.headroomUrl || "http://localhost:8787";
const port = parsePortFromUrl(url) || 8787;
const result = await startHeadroomProxy({ port });
return NextResponse.json({ success: true, ...result });
} catch (error) {
const status = error.code === "NOT_INSTALLED" ? 400 : 500;
return NextResponse.json({ error: error.message, code: error.code || null }, { status });
}
}
+18
View File
@@ -0,0 +1,18 @@
import { NextResponse } from "next/server";
import { getSettings } from "@/lib/localDb";
import { getHeadroomStatus } from "@/lib/headroom/detect";
import { getManagedPid } from "@/lib/headroom/process";
export const dynamic = "force-dynamic";
export async function GET() {
try {
const settings = await getSettings();
const url = settings.headroomUrl || "http://localhost:8787";
const status = await getHeadroomStatus(url);
const managedPid = getManagedPid();
return NextResponse.json({ ...status, url, managedPid });
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
+14
View File
@@ -0,0 +1,14 @@
import { NextResponse } from "next/server";
import { stopHeadroomProxy } from "@/lib/headroom/process";
export const dynamic = "force-dynamic";
export async function POST() {
try {
const result = stopHeadroomProxy();
const status = result.stopped ? 200 : 409;
return NextResponse.json({ ...result }, { status });
} catch (error) {
return NextResponse.json({ error: error.message, code: error.code || null }, { status: 500 });
}
}
+2
View File
@@ -79,6 +79,8 @@ const LOCAL_ONLY_PATHS = [
"/api/oauth/cursor/auto-import",
"/api/oauth/kiro/auto-import",
"/api/auth/reset-password",
"/api/headroom/start",
"/api/headroom/stop",
];
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);
+5
View File
@@ -34,8 +34,13 @@ const DEFAULT_SETTINGS = {
mitmRouterBaseUrl: DEFAULT_MITM_ROUTER_BASE,
dnsToolEnabled: {},
rtkEnabled: true,
headroomEnabled: false,
headroomUrl: "http://localhost:8787",
headroomCompressUserMessages: false,
cavemanEnabled: false,
cavemanLevel: "full",
ponytailEnabled: false,
ponytailLevel: "full",
};
async function readRaw() {
+63
View File
@@ -0,0 +1,63 @@
import { execSync } from "child_process";
const EXTENDED_PATH = `/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${process.env.PATH || ""}`;
const PYTHON_CANDIDATES = ["python3.13", "python3.12", "python3.11", "python3.10", "python3"];
const MIN_VERSION = [3, 10];
const HEADROOM_HEALTH_TIMEOUT_MS = 1500;
// Detect whether the headroom CLI is installed and where its binary lives.
export function findHeadroomBinary() {
try {
const path = execSync("which headroom", {
stdio: ["ignore", "pipe", "ignore"],
windowsHide: true,
env: { ...process.env, PATH: EXTENDED_PATH },
}).toString().trim();
return path || null;
} catch {
return null;
}
}
// Find a Python interpreter >= 3.10 (headroom-ai requires it). Returns null if none.
export function findPython310() {
for (const candidate of PYTHON_CANDIDATES) {
try {
const ver = execSync(`${candidate} --version`, {
stdio: ["ignore", "pipe", "ignore"],
windowsHide: true,
env: { ...process.env, PATH: EXTENDED_PATH },
}).toString().trim();
const match = ver.match(/(\d+)\.(\d+)/);
if (!match) continue;
const [major, minor] = [parseInt(match[1], 10), parseInt(match[2], 10)];
if (major > MIN_VERSION[0] || (major === MIN_VERSION[0] && minor >= MIN_VERSION[1])) {
return candidate;
}
} catch {
// candidate not present, try next
}
}
return null;
}
// Probe whether a Headroom proxy is reachable at the given URL by hitting /health.
export async function probeProxyRunning(url) {
if (!url) return false;
const base = String(url).replace(/\/$/, "");
try {
const res = await fetch(`${base}/health`, { signal: AbortSignal.timeout(HEADROOM_HEALTH_TIMEOUT_MS) });
return res.ok;
} catch {
return false;
}
}
// Aggregate status for the dashboard: installed, running, python interpreter.
export async function getHeadroomStatus(url) {
const path = findHeadroomBinary();
const python = findPython310();
const installed = Boolean(path);
const running = installed ? await probeProxyRunning(url) : false;
return { installed, path, running, python };
}
+128
View File
@@ -0,0 +1,128 @@
import fs from "fs";
import path from "path";
import { spawn } from "child_process";
import { DATA_DIR } from "@/lib/dataDir.js";
import { findHeadroomBinary } from "./detect.js";
const HEADROOM_DIR = path.join(DATA_DIR, "headroom");
const PID_FILE = path.join(HEADROOM_DIR, "proxy.pid");
const LOG_FILE = path.join(HEADROOM_DIR, "proxy.log");
const DEFAULT_PORT = 8787;
const STARTUP_TIMEOUT_MS = 8000;
function ensureDir() {
if (!fs.existsSync(HEADROOM_DIR)) fs.mkdirSync(HEADROOM_DIR, { recursive: true });
}
function readPid() {
try {
if (fs.existsSync(PID_FILE)) return parseInt(fs.readFileSync(PID_FILE, "utf8"), 10);
} catch { /* ignore */ }
return null;
}
function writePid(pid) {
ensureDir();
fs.writeFileSync(PID_FILE, String(pid));
}
function clearPid() {
try { if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
}
// process.kill throws if pid is dead — use this to probe.
export function isPidAlive(pid) {
if (!pid || typeof pid !== "number") return false;
try { process.kill(pid, 0); return true; } catch { return false; }
}
export function getManagedPid() {
const pid = readPid();
return pid && isPidAlive(pid) ? pid : null;
}
export async function startHeadroomProxy({ port = DEFAULT_PORT } = {}) {
const safePort = Number(port) > 0 && Number(port) < 65536 ? Number(port) : DEFAULT_PORT;
const binary = findHeadroomBinary();
if (!binary) {
const err = new Error("Headroom CLI not installed");
err.code = "NOT_INSTALLED";
throw err;
}
const existing = getManagedPid();
if (existing) return { pid: existing, alreadyRunning: true };
ensureDir();
// spawn stdio requires fd numbers, not WriteStream objects.
const outFd = fs.openSync(LOG_FILE, "a");
const child = spawn(binary, ["proxy", "--port", String(safePort)], {
stdio: ["ignore", outFd, outFd],
detached: true,
windowsHide: true,
env: { ...process.env },
});
if (!child.pid) {
fs.closeSync(outFd);
const err = new Error("Failed to spawn headroom proxy");
err.code = "SPAWN_FAILED";
throw err;
}
child.unref();
writePid(child.pid);
// Wait until the process either stays alive briefly (success) or exits fast (failure).
await new Promise((resolve, reject) => {
const startupTimer = setTimeout(() => {
if (isPidAlive(child.pid)) resolve();
else reject(new Error("headroom proxy exited during startup — see proxy.log"));
}, STARTUP_TIMEOUT_MS);
child.once("exit", (code) => {
clearTimeout(startupTimer);
clearPid();
fs.closeSync(outFd);
const e = new Error(`headroom proxy exited early (code=${code}) — see proxy.log`);
e.code = "EARLY_EXIT";
reject(e);
});
});
// Close parent's copy of the fd; child retains its own after unref.
fs.closeSync(outFd);
return { pid: child.pid, alreadyRunning: false };
}
export function stopHeadroomProxy() {
const pid = getManagedPid();
if (!pid) return { stopped: false, reason: "not_running" };
try {
process.kill(pid, "SIGTERM");
// Give it a moment, then force if still alive.
setTimeout(() => {
if (isPidAlive(pid)) {
try { process.kill(pid, "SIGKILL"); } catch { /* already gone */ }
}
}, 2000);
clearPid();
return { stopped: true, pid };
} catch (e) {
clearPid();
const err = new Error(`Failed to stop headroom proxy: ${e.message}`);
err.code = "STOP_FAILED";
throw err;
}
}
export function getHeadroomLogTail(maxLines = 200) {
try {
if (!fs.existsSync(LOG_FILE)) return "";
const content = fs.readFileSync(LOG_FILE, "utf8");
const lines = content.split(/\r?\n/).filter(Boolean);
return lines.slice(-maxLines).join("\n");
} catch { return ""; }
}
+5
View File
@@ -251,8 +251,13 @@ async function handleSingleModelChat(body, modelStr, clientRawRequest = null, re
apiKey,
ccFilterNaming: !!chatSettings.ccFilterNaming,
rtkEnabled: !!chatSettings.rtkEnabled,
headroomEnabled: !!chatSettings.headroomEnabled,
headroomUrl: chatSettings.headroomUrl || "http://localhost:8787",
headroomCompressUserMessages: !!chatSettings.headroomCompressUserMessages,
cavemanEnabled: !!chatSettings.cavemanEnabled,
cavemanLevel: chatSettings.cavemanLevel || "full",
ponytailEnabled: !!chatSettings.ponytailEnabled,
ponytailLevel: chatSettings.ponytailLevel || "full",
providerThinking,
// Detect source format by endpoint + body
sourceFormatOverride: request?.url ? detectFormatByEndpoint(new URL(request.url).pathname, body) : null,