openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
432 lines (414 loc) • 25.3 kB
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>