// Live app entry — talks to real OPRT API instead of mock data const { useState, useEffect } = React; function adaptInstance(row, i = 0) { const hues = [162, 245, 295, 75, 20, 205, 25, 330]; const h = hues[i % hues.length]; const initials = (row.display_name || row.name || row.agent_id || "?") .split(" ").slice(0, 2).map(w => w[0]).join("").toUpperCase(); return { id: row.agent_id || row.id, uuid: row.id, name: row.display_name || row.name, contact: row.contact, email: row.email, industry: row.industry, telegram: row.telegram_bot_username || "—", status: row.status, createdAt: (row.created_at || "").slice(0, 10), plan: row.plan, skills: row.skills || [], skillDetails: row.skillDetails || [], tokensIn: Number(row.tokens_in ?? 0), tokensOut: Number(row.tokens_out ?? 0), cost: Number(row.cost_eur ?? 0), messages: Number(row.messages ?? 0), apiProvider: row.provider, model: row.model, spark: row.spark || Array(16).fill(0).map((_, i) => 20 + Math.sin(i / 2) * 10 + Math.random() * 10), logoBg: `oklch(0.55 0.15 ${h})`, logoBg2: `oklch(0.65 0.14 ${h})`, initials, }; } function LiveApp() { const [route, setRoute] = useState(() => localStorage.getItem("oprt.route") || "dashboard"); const [instanceId, setInstanceId] = useState(() => localStorage.getItem("oprt.instanceId") || null); const [theme, setTheme] = useState(() => localStorage.getItem("oprt.theme") || "dark"); const [instances, setInstances] = useState([]); const [skills, setSkills] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [editMode, setEditMode] = useState(false); const [showSettings, setShowSettings] = useState(false); useEffect(() => { document.documentElement.classList.toggle("light", theme === "light"); localStorage.setItem("oprt.theme", theme); }, [theme]); useEffect(() => { localStorage.setItem("oprt.route", route); }, [route]); useEffect(() => { if (instanceId) localStorage.setItem("oprt.instanceId", instanceId); }, [instanceId]); const loadData = async () => { setLoading(true); setError(null); try { const [ins, sk] = await Promise.all([ OprtAPI.listInstances(), OprtAPI.listSkills(), ]); const adapted = (ins || []).map(adaptInstance); setInstances(adapted); setSkills(sk || []); window.APP_DATA = { INSTANCES: adapted, SKILLS: (sk || []).map(s => s.name), }; } catch (e) { setError(e.message || String(e)); window.APP_DATA = window.APP_DATA || { INSTANCES: [], SKILLS: [] }; } finally { setLoading(false); } }; useEffect(() => { loadData(); }, []); useEffect(() => { const onMsg = (e) => { if (!e.data) return; if (e.data.type === "__activate_edit_mode") setEditMode(true); if (e.data.type === "__deactivate_edit_mode") setEditMode(false); }; window.addEventListener("message", onMsg); window.parent.postMessage({ type: "__edit_mode_available" }, "*"); return () => window.removeEventListener("message", onMsg); }, []); const [detailInstance, setDetailInstance] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const onNav = (r) => { setRoute(r); if (r !== "instance") { setInstanceId(null); setDetailInstance(null); } }; const openInstance = async (id) => { setInstanceId(id); setRoute("instance"); setDetailLoading(true); try { const listInst = instances.find(i => i.id === id || i.uuid === id); const uuid = listInst ? listInst.uuid : id; const detail = await OprtAPI.getInstance(uuid); const adapted = adaptInstance(detail, instances.findIndex(i => i.uuid === uuid)); setDetailInstance(adapted); } catch (e) { setDetailInstance(instances.find(i => i.id === id || i.uuid === id) || null); } finally { setDetailLoading(false); } }; const currentInstance = detailInstance || instances.find(i => i.id === instanceId || i.uuid === instanceId); let crumbs = [{ label: "OPRT.AI", go: "dashboard" }]; let view = null; if (loading) { view =
Lade Daten von {OprtConfig.get().base}
; } else if (error) { view = setShowSettings(true)} />; } else { switch (route) { case "dashboard": case "instances": crumbs.push({ label: "Dashboard" }); view = ; break; case "instance": crumbs.push({ label: "Instances", go: "dashboard" }, { label: currentInstance ? currentInstance.name : "—" }); view = ; break; case "launch": crumbs.push({ label: "Launch new instance" }); view = ; break; case "keys": crumbs.push({ label: "API keys" }); view = ; break; case "metrics": crumbs.push({ label: "Metrics" }); view = ; break; case "skills": crumbs.push({ label: "Skills library" }); view = ; break; case "billing": crumbs.push({ label: "Billing" }); view = ; break; default: view = ; } } const topRight = (
); return (
{view}
{showSettings && setShowSettings(false)} onSaved={loadData}/>} {editMode && }
); } function ErrorView({ error, onRetry, onOpenSettings }) { const { base } = OprtConfig.get(); return (

Verbindung zur API fehlgeschlagen

Endpoint: {base}
{error}
Prüfe: API erreichbar? Bearer-Token gesetzt? CORS erlaubt {window.location.origin}?
); } function SettingsModal({ onClose, onSaved }) { const cfg = OprtConfig.get(); const [base, setBase] = useState(cfg.base); const [token, setToken] = useState(cfg.token); const [showTok, setShowTok] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState(null); const save = () => { OprtConfig.set({ base, token }); onClose(); onSaved(); }; const test = async () => { setTesting(true); setTestResult(null); OprtConfig.set({ base, token }); try { const r = await OprtAPI.health(); setTestResult(r ? "ok" : "fail"); } catch (e) { setTestResult("fail: " + e.message); } finally { setTesting(false); } }; return (
e.stopPropagation()}>

API-Verbindung

setBase(e.target.value)} placeholder="https://launch.quinn.chat"/>
Die Basis-URL deiner OPRT Launch Board API.
setToken(e.target.value)}/>
Aus agency_user_tokens. Dev-Token initial: dev-token-please-change.
{testResult && (
{testResult === "ok" ? "Verbindung ok!" : "Fehler: " + testResult}
)}
); } function LiveLaunchForm({ onNav, onDone, skills }) { const skillNames = skills.map(s => s.name); const [state, setState] = useState({ companyName: "", agentId: "", contact: "", email: "", industry: "", plan: "Growth", telegramToken: "", provider: "openai", apiKey: "", model: "gpt-4.1", skills: new Set(skillNames), }); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [slugEdited, setSlugEdited] = useState(false); const set = (k, v) => setState(s => ({ ...s, [k]: v })); const onCompany = (v) => setState(s => ({ ...s, companyName: v, agentId: slugEdited ? s.agentId : v.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") })); const toggleSkill = (skill) => setState(s => { const n = new Set(s.skills); n.has(skill) ? n.delete(skill) : n.add(skill); return { ...s, skills: n }; }); const submit = async () => { setSubmitting(true); setError(null); try { const body = { ...state, skills: [...state.skills] }; const r = await OprtAPI.createInstance(body); onDone && onDone(); onNav("instance"); setTimeout(() => { localStorage.setItem("oprt.instanceId", r.instanceId); location.reload(); }, 200); } catch (e) { setError(e.message); setSubmitting(false); } }; const valid = state.companyName && state.agentId && state.contact && state.email && state.telegramToken && state.apiKey; return (

Launch new instance

Provisioniert eine neue OpenClaw-Instanz über POST /api/instances

{error && (
{error}
)}
Kunde
onCompany(e.target.value)}/>
{ setSlugEdited(true); set("agentId", e.target.value); }}/>
set("contact", e.target.value)}/>
set("email", e.target.value)}/>
set("industry", e.target.value)}/>
Telegram
set("telegramToken", e.target.value)}/>
Modell & API-Key
set("provider", "openai")}>OpenAI
set("provider", "anthropic")}>Anthropic
set("apiKey", e.target.value)}/>
Skills · {state.skills.size}/{skillNames.length}
{skillNames.map((s) => { const on = state.skills.has(s); return ; })}
); } function TweaksPanel({ theme, setTheme }) { return (

Tweaks

Theme
setTheme("dark")}>Dark
setTheme("light")}>Light
); } ReactDOM.createRoot(document.getElementById("root")).render();