// 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 ? ( ) : (