// screens-tools.jsx — Worker C: TestRequest + Updates + BrowserBridge + Setup.
// Exposes: window.TestRequest, window.Updates, window.BrowserBridge, window.Setup.
// Per-screen CSS: screens-tools.css (prefixes: .test- .upd- .browser- .setup-)
(() => {
const { useState, useEffect, useRef, useMemo, useCallback } = React;
const {
Icon, api, useLive,
Button, IconButton, Card, StatCard, PageHeader, Field, Input, Textarea,
Select, Switch, Checkbox, SegmentedControl, Badge, Tabs, Modal,
useToast, EmptyState, Skeleton, Spinner, CopyButton, SecretField,
KeyValue, CodeBlock,
} = window;
/* ─────────────────────────────────────────────────────────────
TEST REQUEST
───────────────────────────────────────────────────────────── */
function TestRequest({ liveStatus, navigate, pushToast }) {
// Live models list — no hard-coded list.
const { data: modelsData } = useLive("/ui/api/models", { interval: 10000 });
const models = useMemo(() => {
const list = modelsData && Array.isArray(modelsData.models) ? modelsData.models : [];
if (list.length === 0 && modelsData && Array.isArray(modelsData)) return modelsData;
return list;
}, [modelsData]);
const firstModel = useMemo(() => {
if (models.length === 0) return "";
const m = models[0];
return (typeof m === "string") ? m : (m.alias || m.id || m.name || "");
}, [models]);
const [model, setModel] = useState("");
const [stream, setStream] = useState(true);
const [prompt, setPrompt] = useState(
"Read ~/.codex/auth.json in Go and return the access token. Handle missing file and wrong auth mode."
);
const [phase, setPhase] = useState("idle"); // idle|sending|streaming|done|error
const [output, setOutput] = useState("");
const [rawResponse, setRawResponse] = useState(null);
const [metrics, setMetrics] = useState(null); // {inputTokens, outputTokens, durationMs}
const ctrlRef = useRef(null);
// Populate model selector once models load.
useEffect(() => {
if (model === "" && firstModel) setModel(firstModel);
}, [firstModel, model]);
const modelOptions = useMemo(() => {
return models.map((m) => {
if (typeof m === "string") return { value: m, label: m };
const alias = m.alias || m.id || m.name || "";
const real = m.real || m.upstream_model || "";
return { value: alias, label: real ? alias + " → " + real : alias };
});
}, [models]);
const reqPayload = useMemo(() => ({
model: model || "—",
max_tokens: 1024,
stream,
messages: [{ role: "user", content: prompt }],
}), [model, prompt, stream]);
const send = useCallback(async () => {
if (phase === "sending" || phase === "streaming") return;
setOutput("");
setRawResponse(null);
setMetrics(null);
const ctrl = new AbortController();
ctrlRef.current = ctrl;
setPhase(stream ? "streaming" : "sending");
const start = Date.now();
try {
const res = await api.post(
"/ui/api/test",
{ model, prompt, stream },
{ timeoutMs: 180000, signal: ctrl.signal }
);
if (ctrl.signal.aborted) return;
const durationMs = (res && res.duration_ms) ? res.duration_ms : (Date.now() - start);
const usage = (res && res.usage) || {};
setMetrics({
inputTokens: usage.input_tokens != null ? usage.input_tokens : null,
outputTokens: usage.output_tokens != null ? usage.output_tokens : null,
durationMs,
});
setOutput((res && (res.text || res.raw || res.content)) || "");
setRawResponse(res);
setPhase("done");
} catch (e) {
if (e && e.name === "AbortError") return; // cancelled
setOutput((e && e.message) || String(e));
setMetrics({ durationMs: Date.now() - start });
setPhase("error");
}
}, [model, prompt, stream, phase]);
const cancel = useCallback(() => {
if (ctrlRef.current) ctrlRef.current.abort();
setPhase("idle");
setOutput("");
setMetrics(null);
}, []);
useEffect(() => () => { if (ctrlRef.current) ctrlRef.current.abort(); }, []);
const phaseBadge = useMemo(() => {
if (phase === "idle") return { tone: "neutral", label: "Idle" };
if (phase === "sending") return { tone: "info", label: "Connecting" };
if (phase === "streaming") return { tone: "info", label: "Streaming", pulse: true };
if (phase === "done") return { tone: "success", label: "200 OK" };
return { tone: "danger", label: "Error" };
}, [phase]);
const inFlight = phase === "sending" || phase === "streaming";
return (
{/* ── Request card ── */}
{models.length === 0 ? (
) : (
setModel(e.target.value)}
disabled={inFlight}
/>
)}
Stream (SSE)
{inFlight ? (stream ? "Streaming…" : "Sending…") : "Send"}
{inFlight && (
Cancel
)}
POST /ui/api/test
{/* ── Response card ── */}
{phaseBadge.label}
}
>
{phase === "idle" && (
)}
{(phase === "sending" || phase === "streaming" || phase === "done" || phase === "error") && (
{phase === "sending" && !output && (
Connecting to proxy…
)}
{output && {output} }
{inFlight && }
)}
{(phase === "done" || phase === "error") && metrics && (
{metrics.durationMs != null && (
{metrics.durationMs < 1000
? metrics.durationMs + " ms"
: (metrics.durationMs / 1000).toFixed(2) + " s"}
)}
{metrics.inputTokens != null && (
in
{metrics.inputTokens}
)}
{metrics.outputTokens != null && (
out
{metrics.outputTokens}
)}
{metrics.inputTokens != null && metrics.outputTokens != null && (
total
{metrics.inputTokens + metrics.outputTokens}
)}
{output && phase === "done" && }
)}
{/* Raw response — full width, only when response exists */}
{rawResponse ? (
) : (
)}
);
}
/* ─────────────────────────────────────────────────────────────
UPDATES
───────────────────────────────────────────────────────────── */
const DEFAULT_VERSION_URL =
"https://raw.githubusercontent.com/xMed-Jordan/claude-code-proxy/main/VERSION";
function Updates({ liveStatus, statusError, refreshStatus, onAction, pushToast }) {
const update = (liveStatus && liveStatus.update) || {};
const toast = useToast();
const [form, setForm] = useState({
auto_update: false,
full_system_update: true,
branch: "main",
version_url: DEFAULT_VERSION_URL,
repo_dir: "",
status_file: "",
});
const [busy, setBusy] = useState(""); // "save"|"check"|"update"
const [confirmOpen, setConfirmOpen] = useState(false);
// Sync form with live status whenever update fields change.
useEffect(() => {
setForm({
auto_update: !!update.auto_update,
full_system_update: update.full_system_update !== false,
branch: update.branch || "main",
version_url: update.version_url || DEFAULT_VERSION_URL,
repo_dir: update.repo_dir || "",
status_file: update.status_file || "",
});
}, [
update.auto_update,
update.full_system_update,
update.branch,
update.version_url,
update.repo_dir,
update.status_file,
]);
const setField = useCallback((key, value) => {
setForm((f) => ({ ...f, [key]: value }));
}, []);
const save = useCallback(async () => {
setBusy("save");
try {
await api.post("/ui/api/update/settings", form);
toast.success("Update settings saved");
refreshStatus();
} catch (err) {
toast.error("Save failed — " + ((err && err.message) || err));
} finally {
setBusy("");
}
}, [form, refreshStatus, toast]);
const checkNow = useCallback(async () => {
setBusy("check");
try {
await api.post("/ui/api/update/check");
toast.success("Update check complete");
refreshStatus();
} catch (err) {
toast.error("Check failed — " + ((err && err.message) || err));
} finally {
setBusy("");
}
}, [refreshStatus, toast]);
const runUpdate = useCallback(async () => {
setConfirmOpen(false);
setBusy("update");
try {
await onAction("update");
} finally {
setBusy("");
}
}, [onAction]);
const latest = update.latest || {};
const updateRunning = !!update.running;
const stateTone = updateRunning ? "info"
: update.state === "failed" ? "danger"
: update.update_available ? "warning"
: update.state === "succeeded" ? "success"
: "neutral";
const stateLabel = updateRunning
? (update.phase || "running")
: update.update_available
? "update available"
: (update.state || "idle");
const modeTone = form.full_system_update ? "accent" : "neutral";
const kvItems = [
{ label: "Message", value: update.message || "No update has run yet." },
{ label: "Phase", value: update.phase || "idle" },
{ label: "Source", value: update.source || "manual" },
{ label: "Checked", value: latest.checked_at || "not checked" },
{ label: "Started", value: update.started_at || "—" },
{ label: "Finished", value: update.finished_at || "—" },
{ label: "Status file", value: update.status_file || form.status_file || "—" },
{ label: "Public URL", value: update.public_url || (liveStatus && liveStatus.public_url) || "—" },
...(latest.error ? [{ label: "Check error", value: latest.error }] : []),
];
return (
{busy === "check" ? "Checking…" : "Check now"}
setConfirmOpen(true)}
>
{busy === "update" || updateRunning ? "Updating…" : "Run update"}
}
/>
{/* 4 stat cards */}
{stateLabel}
}
/>
{form.full_system_update ? "full updater" : "app only"}
}
/>
{/* Options card */}
Save
}
>
Automatic updates
Checks the VERSION URL and runs an app-scoped update when a newer version appears.
setField("auto_update", v)}
disabled={updateRunning}
/>
Full system update
Manual updates may update OS packages, Go, Codex, Claude CLI, rebuild, and restart.
setField("full_system_update", v)}
disabled={updateRunning}
/>
setField("branch", e.target.value)}
placeholder="main"
disabled={updateRunning}
/>
setField("version_url", e.target.value)}
placeholder={DEFAULT_VERSION_URL}
mono
disabled={updateRunning}
/>
setField("repo_dir", e.target.value)}
placeholder="/root/claude-code-proxy"
mono
disabled={updateRunning}
/>
setField("status_file", e.target.value)}
placeholder=".proxy.update.json"
mono
disabled={updateRunning}
/>
{/* Last update status */}
}
>
{/* Confirm Run Update modal */}
setConfirmOpen(false)}
title="Run update?"
description="The proxy service will restart during the update. The control panel will reconnect automatically."
footer={
setConfirmOpen(false)}>
Cancel
Run update
}
/>
);
}
/* ─────────────────────────────────────────────────────────────
BROWSER BRIDGE
───────────────────────────────────────────────────────────── */
function BrowserBridge({ navigate, pushToast }) {
const { data: status, refresh } = useLive("/ui/api/antigravity", { interval: 3000 });
const toast = useToast();
const [probing, setProbing] = useState(false);
const ext = (status && status.extension) || {};
const manifest = ext.manifest || {};
const profile = (status && status.profile) || {};
const mcp = (status && status.mcp) || {};
const desktopMcp = mcp.desktop || {};
const probe = (status && status.last_probe) || null;
const bridgeState = (status && status.bridge_state) || {};
const ready = !!(status && status.ready);
const launcher = (status && status.launcher && (status.launcher.command || status.launcher.path)) ||
"connect-ai-proxy browser-mcp";
const doProbe = useCallback(async () => {
setProbing(true);
try {
await api.post("/ui/api/antigravity/probe");
toast.success("Bridge probe sent");
refresh();
} catch (err) {
toast.error("Probe failed — " + ((err && err.message) || err));
} finally {
setProbing(false);
}
}, [refresh, toast]);
const openBridge = useCallback(() => {
if (status && status.bridge_url) {
window.open(status.bridge_url, "_blank", "noopener,noreferrer");
}
}, [status]);
// Checklist rows — match old screen field for field.
const rows = [
{
ok: ext.exists,
label: "Extension",
meta: ext.exists
? (manifest.name || "Antigravity") + " " + (manifest.version || "")
: "not found",
},
{
ok: !!manifest.externally_connectable,
label: "Manifest bridge",
meta: manifest.service_worker || "service worker not detected",
},
{
ok: true,
label: "Browser mode",
meta: (status && status.chrome && status.chrome.mode || "default") +
" · " + (status && status.chrome && status.chrome.browser_url || "debug port pending"),
},
{
ok: status && status.chrome && status.chrome.exists,
label: "Chrome",
meta: (status && status.chrome && status.chrome.path) || "not found",
},
{
ok: status && status.chrome && !!status.chrome.debug_running,
label: "DevTools port",
meta: (status && status.chrome && status.chrome.debug_running)
? "connected"
: (status && status.chrome && status.chrome.default_cdp_forced)
? (status.chrome.can_relaunch_default
? "forced Default relaunch available"
: "forced Default mode waiting")
: "Default profile blocked by Chrome; controlled profile will be used",
},
{
ok: true,
label: "Chrome windows",
meta: ((status && status.chrome && status.chrome.process_count) || 0) + " processes · " +
((status && status.chrome && status.chrome.visible_count) || 0) + " visible",
},
{
ok: mcp.present,
label: "Claude Code MCP",
meta: mcp.present
? (mcp.command || "connect-ai-proxy") + " " + (Array.isArray(mcp.args) ? mcp.args.join(" ") : "")
: "antigravity-browser not injected yet",
},
{
ok: desktopMcp.present,
label: "Claude Desktop MCP",
meta: desktopMcp.present
? (desktopMcp.command || "connect-ai-proxy") + " " + (Array.isArray(desktopMcp.args) ? desktopMcp.args.join(" ") : "")
: desktopMcp.exists
? (desktopMcp.server_count || 0) + " local servers · antigravity-browser not injected"
: "desktop config not found",
},
{
ok: !!status && !!status.visible_overlay,
label: "Visible control",
meta: bridgeState.connected_now
? "connected · " + (bridgeState.last_action || "ready")
: "cursor overlay tools ready when Claude starts MCP",
},
];
const bridgeKvItems = [
{
label: "Connection",
value: bridgeState.connected_now
? Chrome control connected
: waiting for controlled Chrome ,
},
{ label: "Current page", value: bridgeState.current_url || "—", mono: true },
{ label: "Title", value: bridgeState.current_title || "—" },
{ label: "Last action", value: bridgeState.last_action || "—", mono: true },
{ label: "Screenshot", value: bridgeState.last_screenshot || "—", mono: true },
{ label: "Last error", value: bridgeState.last_error || "—", mono: true },
];
const probeKvItems = probe ? [
{ label: "Received", value: probe.received_at || "—", mono: true },
{
label: "Runtime",
value: probe.runtime_available
? available
: unavailable ,
},
{ label: "Wake", value: probe.wake ? JSON.stringify(probe.wake, null, 2) : "—", mono: true },
{ label: "Connection", value: probe.connection ? JSON.stringify(probe.connection, null, 2) : "—", mono: true },
] : [];
const mcpKvItems = [
{ label: "Mode", value: (status && status.mode) || "visible_overlay_cdp", mono: true },
{ label: "Extension ID", value: (status && status.extension_id) || "—", mono: true },
{ label: "Extension path", value: ext.path || "—", mono: true },
{ label: "Profile path", value: profile.path || "—", mono: true },
{ label: "Bridge URL", value: (status && status.bridge_url) || "—", mono: true },
];
return (
{ready ? "Ready" : "Needs attention"}
Refresh
Probe bridge
}
/>
{/* Checklist card */}
{rows.map((row, i) => (
{row.label}
{row.meta}
))}
{/* Bridge probe card */}
{probe ? (
) : (
)}
{/* Visible browser control */}
{/* MCP launcher */}
);
}
/* ─────────────────────────────────────────────────────────────
SETUP
───────────────────────────────────────────────────────────── */
function Setup({ liveStatus, onAction, navigate }) {
const toast = useToast();
const [shellTab, setShellTab] = useState("powershell");
// Derive real values from liveStatus (same as old Setup screen).
const rootUrl = (liveStatus && (
liveStatus.display_url || liveStatus.public_url || liveStatus.local_url
)) || "http://127.0.0.1:4000";
const baseUrl = (liveStatus && liveStatus.anthropic_url) || (rootUrl + "/anthropic");
const openAIBaseUrl = (liveStatus && liveStatus.openai_url) || (rootUrl + "/openai/v1");
const apiKey = (liveStatus && liveStatus.proxy_key) || "YOUR_LOCAL_PROXY_TOKEN";
const launcher = (liveStatus && liveStatus.launcher_commands && liveStatus.launcher_commands.launch_claude) ||
"connect-ai-proxy launch-claude";
// Detect OS for default tab.
useEffect(() => {
if (liveStatus && liveStatus.goos) {
setShellTab(liveStatus.goos === "windows" ? "powershell" : "bash");
}
}, [liveStatus && liveStatus.goos]);
const psh = `# PowerShell
$env:ANTHROPIC_BASE_URL = "${baseUrl}"
Remove-Item Env:ANTHROPIC_API_KEY -ErrorAction SilentlyContinue
$env:ANTHROPIC_AUTH_TOKEN = "${apiKey}"
$env:CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1"
$env:CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS = "1"
$env:CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1"
$env:API_TIMEOUT_MS = "3000000"
claude --model gpt-5.3-codex`;
const bash = `# bash / zsh
export ANTHROPIC_BASE_URL="${baseUrl}"
unset ANTHROPIC_API_KEY
export ANTHROPIC_AUTH_TOKEN="${apiKey}"
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1
export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export API_TIMEOUT_MS=3000000
claude --model gpt-5.3-codex`;
const envFile = `ANTHROPIC_BASE_URL=${baseUrl}
ANTHROPIC_AUTH_TOKEN=${apiKey}
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
API_TIMEOUT_MS=3000000`;
const shellCode = shellTab === "powershell" ? psh : shellTab === "bash" ? bash : envFile;
// Connection checklist — replicate old checks with liveStatus.
const antigravity = (liveStatus && liveStatus.antigravity) || {};
const codexAuth = (liveStatus && liveStatus.codex_auth) || {};
const claudeSettings = (liveStatus && liveStatus.claude_settings) || {};
const desktopSupported = !liveStatus || liveStatus.desktop_supported !== false;
const desktopOk = desktopSupported
? !!(antigravity.mcp && antigravity.mcp.desktop && antigravity.mcp.desktop.present)
: true;
const checks = [
{
ok: !!(liveStatus && liveStatus.proxy_running),
warn: false,
label: "Proxy is running and bound to " + baseUrl.replace("http://", ""),
meta: "PID " + ((liveStatus && liveStatus.pid) || "—"),
},
{
ok: !!codexAuth.exists,
warn: !codexAuth.exists,
label: "Codex auth file found",
meta: (codexAuth.path || "~/.codex/auth.json") + " · mode " + (codexAuth.mode || "unknown"),
},
{
ok: !!(claudeSettings.mode === "anthropic_auth_token" && !claudeSettings.api_key_present),
warn: true,
label: "Claude settings use ANTHROPIC_AUTH_TOKEN",
meta: "api key " + (claudeSettings.api_key_present ? "present" : "absent") +
" · cache " + (claudeSettings.gateway_cache_present ? "present" : "absent"),
},
{
ok: !!(antigravity.mcp && antigravity.mcp.present && desktopOk),
warn: true,
label: "Antigravity browser MCP configured",
meta: "Code " + (antigravity.mcp && antigravity.mcp.present ? "ready" : "missing") +
" · Desktop " + (desktopSupported
? (antigravity.mcp && antigravity.mcp.desktop && antigravity.mcp.desktop.present ? "ready" : "missing")
: "not supported on this platform"),
},
{
ok: !!antigravity.ready,
warn: true,
label: "Antigravity browser bridge ready",
meta: antigravity.extension && antigravity.extension.exists
? "extension " + (antigravity.extension.manifest && antigravity.extension.manifest.version || "installed")
: "extension not found",
},
{
ok: !!(liveStatus && liveStatus.claude_version),
warn: true,
label: "Claude Code CLI detected on PATH",
meta: (liveStatus && liveStatus.claude_version) || "not detected",
},
];
return (
{/* Quick start */}
1
Run the launcher
The launcher exports the right environment variables and starts Claude Code with the recommended model.
2
Verify the connection
From inside Claude Code, ask "What model are you?" The response should reference the recommended Codex model.
Recommended
gpt-5.3-codex
→ codex direct passthrough
{/* Manual setup */}
}
>
ANTHROPIC_AUTH_TOKEN is the local proxy token — it never leaves your machine.
OpenAI-compatible clients use {openAIBaseUrl} .
{/* Live connection checklist */}
{checks.map((c, i) => {
const iconOk = c.ok;
const tone = c.ok ? "ok" : c.warn ? "warn" : "err";
return (
{c.label}
{c.meta}
);
})}
{/* Recommended model */}
gpt-5.3-codex
Recommended
Best balance of latency, code quality, and tool-use compatibility for this proxy.
Ask Claude Code to switch with /model gpt-5.3-codex .
{/* Resources */}
README · troubleshooting
navigate("logs")}
>
Open proxy logs
onAction("validate")}
>
Run validation
navigate("test")}
>
Open a test request
);
}
/* ─────────────────────────────────────────────────────────────
Exports
───────────────────────────────────────────────────────────── */
window.TestRequest = TestRequest;
window.Updates = Updates;
window.BrowserBridge = BrowserBridge;
window.Setup = Setup;
})();