UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

432 lines (414 loc) • 25.3 kB
<!doctype html> <html data-openclaw-control-ui-build-id="2026.10.4-release-383c124cc32f-2026-10-15T17-18-22.749Z-829c0937a963f553623efc5aee466ba3f31a7c54bbc9216f669c639193848f9b" lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" /> <title>OpenClaw Control</title> <meta name="color-scheme" content="dark light" /> <!-- These mirror the claw theme's --bg tokens for first paint; runtime theme application keeps them synchronized. --> <meta name="theme-color" media="(prefers-color-scheme: light)" content="#faf9f7" /> <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0e1015" /> <link rel="icon" type="image/svg+xml" href="./favicon.svg" /> <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" /> <link rel="apple-touch-icon" sizes="180x180" href="./apple-touch-icon.png" /> <link rel="manifest" href="./manifest.webmanifest" crossorigin="use-credentials" /> <script data-cfasync="false"> // Configure Zod before module evaluation so its JIT probe cannot violate strict CSP. globalThis.__zod_globalConfig ??= {}; globalThis.__zod_globalConfig.jitless = true; (function () { var THEMES = { claw: 1, knot: 1, dash: 1, absolutely: 1, tide: 1, beacon: 1, phosphor: 1, crt: 1, manuscript: 1, rose: 1, miami: 1, }; var MODES = { system: 1, light: 1, dark: 1 }; var LEGACY = { dark: "claw:dark", light: "claw:light", openknot: "knot:dark", fieldmanual: "dash:dark", clawdash: "dash:light", system: "claw:system", }; try { var keys = Object.keys(localStorage); var raw; for (var i = 0; i < keys.length; i++) { if (keys[i].indexOf("openclaw.control.settings.v1") === 0) { raw = localStorage.getItem(keys[i]); if (raw) break; } } var s = raw ? JSON.parse(raw) : null; var t = s && s.theme; var m = s && s.themeMode; if (typeof t !== "string") t = ""; if (typeof m !== "string") m = ""; var legacy = LEGACY[t]; var theme = THEMES[t] ? t : legacy ? legacy.split(":")[0] : "claw"; var mode = MODES[m] ? m : legacy ? legacy.split(":")[1] : "system"; if (mode === "system") { mode = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; } // family -> [dark, light] resolved names. Kept in lockstep with // resolveTheme() in app/theme.ts and the files in public/themes. var FAMILIES = { knot: ["openknot", "openknot-light"], dash: ["dash", "dash-light"], absolutely: ["absolutely", "absolutely-light"], tide: ["tide", "tide-light"], beacon: ["beacon", "beacon-light"], phosphor: ["phosphor", "phosphor-light"], crt: ["crt", "crt-light"], manuscript: ["manuscript", "manuscript-light"], rose: ["rose", "rose-light"], miami: ["miami", "miami-light"], }; var pair = FAMILIES[theme]; var resolved = pair ? pair[mode === "light" ? 1 : 0] : mode === "light" ? "light" : "dark"; if (pair) { // Script-created links need explicit render blocking before first paint. // The gateway stamps the mount prefix and asset version before this runs. var base = document.documentElement.getAttribute("data-openclaw-control-ui-base-path") || ""; var buildId = document.documentElement.getAttribute( "data-openclaw-control-ui-build-id", ); var version = buildId ? "?v=" + encodeURIComponent(buildId) : ""; var palette = document.createElement("link"); palette.rel = "stylesheet"; palette.setAttribute("blocking", "render"); palette.id = "openclaw-theme-palette-" + theme; palette.onerror = function () { palette.remove(); }; palette.href = base + "/themes/" + theme + ".css" + version; document.head.appendChild(palette); } document.documentElement.setAttribute("data-theme", resolved); var resolvedMode = resolved.includes("light") ? "light" : "dark"; document.documentElement.setAttribute("data-theme-mode", resolvedMode); // Plugin semantic styles select on [data-theme-resolved]; keep in lockstep. document.documentElement.setAttribute("data-theme-resolved", resolvedMode); } catch (e) {} })(); </script> <style>html,body{min-height:100%}html{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;background:#0e1015}html[data-theme-mode=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;background:#faf9f7}html[data-theme=openknot]{background:#080808}html[data-theme=openknot-light]{background:#f9f9fb}html[data-theme=dash]{background:#1a1210}html[data-theme=dash-light]{background:#f7f2ec}html[data-theme=absolutely]{background:#1c1c1a}html[data-theme=absolutely-light]{background:#faf9f5}html[data-theme=tide]{background:#10151b}html[data-theme=tide-light]{background:#f7f9fb}html[data-theme=beacon]{background:#000}html[data-theme=beacon-light]{background:#fff}html[data-theme=phosphor]{background:#0a0f0a}html[data-theme=phosphor-light]{background:#f4f7f4}html[data-theme=crt]{background:#090a09}html[data-theme=crt-light]{background:#f5f5f4}html[data-theme=manuscript]{background:#211e18}html[data-theme=manuscript-light]{background:#f6f1e4}html[data-theme=rose]{background:#191724}html[data-theme=rose-light]{background:#faf4ed}html[data-theme=miami]{background:#140f1e}html[data-theme=miami-light]{background:#f7f3f6}body{background:inherit;min-height:100%;color-scheme:inherit;margin:0}body.openclaw-mount-fallback-active{--mount-fallback-bg:var(--bg,#0e1015);--mount-fallback-card:var(--card,#161920);--mount-fallback-text:var(--text-strong,#f4f4f5);--mount-fallback-muted:var(--muted,#8b8b94);--mount-fallback-link:var(--link,#ff7070);--mount-fallback-ring:var(--ring,#ff5c5c);color:var(--mount-fallback-text);background:var(--mount-fallback-bg);font-family:var(--font-body,ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);-webkit-font-smoothing:antialiased;margin:0}body.openclaw-mount-fallback-active openclaw-app{display:none}.mount-fallback{box-sizing:border-box;height:100dvh;padding:max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right)) max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));place-items:safe center;overflow-y:auto}.mount-fallback:not([hidden]){display:grid}.mount-fallback__panel{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--mount-fallback-text) 7%, transparent);background:var(--mount-fallback-card);width:min(100%,560px);box-shadow:var(--shadow-lg,0 12px 32px #0003);border-radius:20px;padding:32px}.mount-fallback__panel .mount-fallback__eyebrow{color:var(--mount-fallback-muted);letter-spacing:.08em;text-transform:uppercase;margin:0 0 16px;font-size:.78rem;font-weight:700}.mount-fallback__panel h1{color:inherit;letter-spacing:-.02em;margin:0;font-size:clamp(1.375rem,3vw,1.625rem);font-weight:650;line-height:1.25}.mount-fallback__panel p{color:var(--mount-fallback-muted);margin:12px 0 0;font-size:.9375rem;line-height:1.55}.mount-fallback__panel ul{color:var(--mount-fallback-muted);margin:18px 0 0;padding-left:1.2rem;font-size:.875rem;line-height:1.55}.mount-fallback__panel li+li{margin-top:8px}.mount-fallback__panel a{color:var(--mount-fallback-link);text-underline-offset:.18em;text-decoration-thickness:.08em}.mount-fallback__actions{flex-wrap:wrap;gap:10px;margin-top:24px;display:flex}.mount-fallback__button{appearance:none;min-height:44px;color:inherit;background:color-mix(in srgb, var(--mount-fallback-text) 8%, var(--mount-fallback-card));font:inherit;cursor:default;border:0;border-radius:10px;padding:10px 16px;font-size:.875rem;font-weight:600;line-height:1.4;transition:background .12s}@media (hover:hover){.mount-fallback__button:hover{background:color-mix(in srgb, var(--mount-fallback-text) 12%, var(--mount-fallback-card))}}.mount-fallback__button:active{background:color-mix(in srgb, var(--mount-fallback-text) 16%, var(--mount-fallback-card))}.mount-fallback__button--primary{color:var(--primary-foreground,#fff);background:var(--primary,#d13c3c)}.mount-fallback__button--primary:active{background:var(--primary-hover,#c22e2e)}@media (hover:hover){.mount-fallback__button--primary:hover{background:var(--primary-hover,#c22e2e)}}.mount-fallback__panel:focus{outline:none}.mount-fallback__button:focus-visible,.mount-fallback__panel a:focus-visible{outline:2px solid var(--mount-fallback-ring);outline-offset:3px}html[data-theme-mode=light] body.openclaw-mount-fallback-active{--mount-fallback-bg:var(--bg,#faf9f7);--mount-fallback-card:var(--card,#fff);--mount-fallback-text:var(--text-strong,#18181b);--mount-fallback-muted:var(--muted,#64646e);--mount-fallback-link:var(--link,#b91c1c);--mount-fallback-ring:var(--ring,#dc2626)}html[data-theme-mode=light] .mount-fallback__panel{box-shadow:var(--shadow-lg,0 12px 32px #0f172a14)}@media (width<=560px){.mount-fallback__panel{padding:24px}}@media (prefers-reduced-motion:reduce){.mount-fallback__button{transition:none}}</style> <script data-cfasync="false" type="module" crossorigin src="./assets/index-6IbSgfWn.js"></script> <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-8BhlS34s.js"> <link rel="modulepreload" crossorigin href="./assets/lit-runtime-B8uOr6OX.js"> <link rel="modulepreload" crossorigin href="./assets/gateway-runtime-Cr51Pi7N.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-core-9qYjK5B2.js"> <link rel="modulepreload" crossorigin href="./assets/session-snapshot-database-BurLa_ut.js"> <link rel="modulepreload" crossorigin href="./assets/control-ui-core-BncvAZjR.js"> <link rel="modulepreload" crossorigin href="./assets/ui-DuRgK6kL.js"> <link rel="stylesheet" crossorigin href="./assets/control-ui-core-yIvVj14Y.css"> <template data-openclaw-route-preloads="chat"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-Ck7W_FPO.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-umGE-sEP.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-Dfs-gZII.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-Sj2zqjOr.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-em9-jnbz.js"> <link rel="modulepreload" crossorigin="" href="./assets/config-runtime-BecGLziM.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-D4XEBdOG.js"> <link rel="modulepreload" crossorigin="" href="./assets/plugin-contracts-runtime-CNCaYops.js"> <link rel="modulepreload" crossorigin="" href="./assets/markdown-runtime-ORQpioyT.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-VeeaXyJz.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-DJkUdZmL.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-xfDhIVun.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-D3oo_6Fq.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-styles-BaQ2sAfZ.js"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-shared-styles-DKyMAG_6.css"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-shared-CXHNz0Bh.css"> <link rel="modulepreload" crossorigin="" href="./assets/chat-attachment-image.worker-P4E63gj9.js"> <link rel="modulepreload" crossorigin="" href="./assets/sidebar-update-runtime-DonYLwN5.js"> <link rel="preload" as="style" crossorigin="" href="./assets/sidebar-update-runtime-ah7rgQE7.css"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-XcDaYmck.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-BPMc7uzL.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-C8hdsB2M.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-DxoNF0Ad.js"> <link rel="modulepreload" crossorigin="" href="./assets/chat-transcript-styles-Dzr3t9kA.js"> <link rel="preload" as="style" crossorigin="" href="./assets/chat-transcript-styles-B_dd5Ueo.css"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-chat-styles-BRrYkFSc.js"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-chat-styles-C_Ls1c0m.css"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-chat-mLz7NUiY.css"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-chat-Dh5vtL-G.css"> </template> <template data-openclaw-route-preloads="new"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-new-CxgnwT6A.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-umGE-sEP.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-Dfs-gZII.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-Sj2zqjOr.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-em9-jnbz.js"> <link rel="modulepreload" crossorigin="" href="./assets/config-runtime-BecGLziM.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-D4XEBdOG.js"> <link rel="modulepreload" crossorigin="" href="./assets/plugin-contracts-runtime-CNCaYops.js"> <link rel="modulepreload" crossorigin="" href="./assets/markdown-runtime-ORQpioyT.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-DJkUdZmL.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-xfDhIVun.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-D3oo_6Fq.js"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-shared-styles-BaQ2sAfZ.js"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-shared-styles-DKyMAG_6.css"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-shared-CXHNz0Bh.css"> <link rel="modulepreload" crossorigin="" href="./assets/chat-attachment-image.worker-P4E63gj9.js"> <link rel="modulepreload" crossorigin="" href="./assets/sidebar-update-runtime-DonYLwN5.js"> <link rel="preload" as="style" crossorigin="" href="./assets/sidebar-update-runtime-ah7rgQE7.css"> <link rel="modulepreload" crossorigin="" href="./assets/chat-transcript-styles-Dzr3t9kA.js"> <link rel="preload" as="style" crossorigin="" href="./assets/chat-transcript-styles-B_dd5Ueo.css"> <link rel="modulepreload" crossorigin="" href="./assets/control-ui-boot-new-styles-Bb6Ub5D3.js"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-new-styles-u1aN_ui5.css"> <link rel="preload" as="style" crossorigin="" href="./assets/control-ui-boot-new-D-SpiTTi.css"> </template> </head> <body> <openclaw-app></openclaw-app> <section id="openclaw-mount-fallback" class="mount-fallback" data-openclaw-mount-timeout-ms="12000" role="alert" aria-labelledby="openclaw-mount-fallback-title" hidden > <div class="mount-fallback__panel" tabindex="-1"> <p class="mount-fallback__eyebrow">OpenClaw Control UI</p> <h1 id="openclaw-mount-fallback-title">Control UI did not start</h1> <p id="openclaw-mount-fallback-summary"> The browser loaded the static page, but the app bundle did not start. The gateway may be restarting, or this page may reference assets from a different OpenClaw version. </p> <ul> <li>If startup fails, OpenClaw will retry the current app bundle automatically.</li> <li>If this persists, reload or try a clean browser profile.</li> <li> See <a href="https://docs.openclaw.ai/web/control-ui#blank-control-ui-page" target="_blank" rel="noopener noreferrer" >Control UI troubleshooting</a >. </li> </ul> <div class="mount-fallback__actions"> <button type="button" id="openclaw-mount-retry" class="mount-fallback__button mount-fallback__button--primary" > Try again </button> <button type="button" id="openclaw-mount-wait" class="mount-fallback__button"> Keep waiting </button> </div> </div> </section> <script data-cfasync="false"> (function () { var app = document.querySelector("openclaw-app"); var fallback = document.getElementById("openclaw-mount-fallback"); if (!app || !fallback) return; var panel = fallback.querySelector(".mount-fallback__panel"); var title = document.getElementById("openclaw-mount-fallback-title"); var summary = document.getElementById("openclaw-mount-fallback-summary"); var failureSummary = summary.textContent; var retry = document.getElementById("openclaw-mount-retry"); var wait = document.getElementById("openclaw-mount-wait"); var rawDelay = Number(fallback.getAttribute("data-openclaw-mount-timeout-ms")); var delay = Number.isFinite(rawDelay) && rawDelay > 0 ? rawDelay : 12000; var maxRecoveryAttempts = 6; var timer; var recoveryTimer; var recoveryAttempt = 0; var recoveryNavigation = false; var appStarted = false; var documentLoaded = document.readyState === "complete"; var recoveryController; // The recovery page cannot import the app's drag helper when its bundle failed. fallback.addEventListener("mousedown", function (event) { if (event.button !== 0 || event.defaultPrevented || event.target !== fallback) return; var drag = window.webkit?.messageHandlers?.openclawWindowDrag; if (!drag) return; try { drag.postMessage({ type: "window-drag" }); event.preventDefault(); } catch (e) {} }); try { var initialUrl = new URL(window.location.href); recoveryNavigation = initialUrl.searchParams.has("openclaw_mount_recovery"); if (recoveryNavigation) { initialUrl.searchParams.delete("openclaw_mount_recovery"); window.history.replaceState(null, "", initialUrl.href); } } catch (e) {} function hideFallback() { fallback.hidden = true; document.body.classList.remove("openclaw-mount-fallback-active"); } function setSummary(text) { if (summary) summary.textContent = text; } function cancelRecovery() { window.clearTimeout(recoveryTimer); if (recoveryController) recoveryController.abort(); recoveryController = undefined; } function scheduleRecovery() { window.clearTimeout(recoveryTimer); if (appStarted || recoveryAttempt >= maxRecoveryAttempts) return; var retryDelay = Math.min(delay, 1000 * Math.pow(2, recoveryAttempt)); recoveryTimer = window.setTimeout(retryCurrentDocument, retryDelay); } function finishRecoveryAttempt() { recoveryController = undefined; if (appStarted) return; if (recoveryAttempt >= maxRecoveryAttempts) { setSummary( "The gateway is still unavailable. Try again, then check the troubleshooting guide if the problem persists.", ); return; } setSummary( "The gateway is not reachable yet. OpenClaw will keep retrying while it restarts.", ); scheduleRecovery(); } function unregisterServiceWorkers() { if (!navigator.serviceWorker?.getRegistrations) return; // Recovery must not depend on a possibly broken worker. Navigations bypass it, // so a reload can self-heal after the registrations are removed. void navigator.serviceWorker .getRegistrations() .then(function (registrations) { return Promise.all( registrations.map(function (registration) { return registration.unregister(); }), ); }) .catch(function () {}); } function retryCurrentDocument() { if ( appStarted || recoveryController || recoveryAttempt >= maxRecoveryAttempts || typeof window.fetch !== "function" ) return; var documentUrl = new URL(window.location.href); if (recoveryNavigation) { setSummary( "A fresh page still could not start the Control UI. Try again, then check the troubleshooting guide if the problem persists.", ); return; } recoveryAttempt += 1; documentUrl.searchParams.set("openclaw_mount_recovery", String(Date.now())); var controller = new AbortController(); recoveryController = controller; var requestTimer = window.setTimeout(function () { controller.abort(); }, delay); window .fetch(documentUrl.href, { cache: "no-store", credentials: "same-origin", signal: controller.signal, }) .then(function (response) { if (!response.ok) throw new Error("gateway unavailable"); if (!appStarted && recoveryController === controller) window.location.replace(documentUrl.href); }) .catch(function () { if (recoveryController !== controller) return; unregisterServiceWorkers(); finishRecoveryAttempt(); }) .finally(function () { window.clearTimeout(requestTimer); }); } function showFallback() { if (appStarted) return; title.textContent = documentLoaded ? "Control UI did not start" : "Control UI is still loading"; fallback.setAttribute("role", documentLoaded ? "alert" : "status"); setSummary( documentLoaded ? failureSummary : "The browser is still loading the app. OpenClaw will keep waiting without interrupting the download.", ); if (documentLoaded) retryCurrentDocument(); fallback.hidden = false; document.body.classList.add("openclaw-mount-fallback-active"); if (documentLoaded && panel && typeof panel.focus === "function") { try { panel.focus({ preventScroll: true }); } catch (e) { panel.focus(); } } } function armFallbackTimer() { window.clearTimeout(timer); timer = window.setTimeout(showFallback, delay); } armFallbackTimer(); // Deferred module graphs settle before DOMContentLoaded. A slow download // is not a failed bootstrap: replacing the document would cancel it. document.addEventListener( "DOMContentLoaded", function () { documentLoaded = true; if (!fallback.hidden) showFallback(); }, { once: true }, ); window.addEventListener( "openclaw-control-ui-rendered", function () { appStarted = true; window.clearTimeout(timer); cancelRecovery(); hideFallback(); }, { once: true }, ); if (retry) { retry.addEventListener("click", function () { cancelRecovery(); unregisterServiceWorkers(); window.location.reload(); }); } if (wait) { wait.addEventListener("click", function () { hideFallback(); cancelRecovery(); recoveryAttempt = 0; armFallbackTimer(); }); } })(); </script> </body> </html>