diff --git a/docs/app/(home)/openclaw-os/page.tsx b/docs/app/(home)/openclaw-os/page.tsx
index 9819b9e5e..a2b70f3a5 100644
--- a/docs/app/(home)/openclaw-os/page.tsx
+++ b/docs/app/(home)/openclaw-os/page.tsx
@@ -41,7 +41,8 @@ export default function OpenClawOSPage() {
mobilePreviewImageWidth={804}
mobilePreviewImageHeight={880}
mobilePreviewImageCropTopPercent={20}
- showGitHubBanner={false}
+ githubRepoUrl="https://github.com/thesysdev/openclaw-os"
+ githubButtonLabel="Star on GitHub"
widePreview
showTagline
taglineCompact
diff --git a/docs/app/(home)/sections/FeaturesSection/FeaturesSection.tsx b/docs/app/(home)/sections/FeaturesSection/FeaturesSection.tsx
index 906bf1d30..a5f39d8c4 100644
--- a/docs/app/(home)/sections/FeaturesSection/FeaturesSection.tsx
+++ b/docs/app/(home)/sections/FeaturesSection/FeaturesSection.tsx
@@ -1,12 +1,13 @@
"use client";
import {
- Browsers,
- Eye,
- MagicWand,
- PushPin,
- SlidersHorizontal,
- SquaresFour,
+ BellIcon,
+ BrowserIcon,
+ EyeIcon,
+ MagicWandIcon,
+ PushPinIcon,
+ SlidersHorizontalIcon,
+ SquaresFourIcon,
} from "@phosphor-icons/react";
import svgPaths from "@/imports/svg-urruvoh2be";
import { PillLink } from "../../components/Button/Button";
@@ -80,32 +81,37 @@ export const OPENCLAW_FEATURES: FeatureListItem[] = [
{
title: "Generative UI",
description: "Build apps, dashboards, and artifacts on demand",
- icon: ,
+ icon: ,
},
{
title: "Persistent apps",
description: "Apps stay in place and refresh with live data automatically",
- icon: ,
+ icon: ,
},
{
title: "Structured workspace",
description: "Agents, sessions, artifacts, and apps in one organized space",
- icon: ,
+ icon: ,
},
{
title: "Full visibility",
description: "Inspect tool calls, context, and agent actions in real time",
- icon: ,
+ icon: ,
},
{
title: "Direct control",
description: "Permissions, schedules, and execution from one interface",
- icon: ,
+ icon: ,
+ },
+ {
+ title: "Live notifications",
+ description: "Cron jobs notifications",
+ icon: ,
},
{
title: "Elegant interface",
description: "Built for clarity with responsive layouts and themes",
- icon: ,
+ icon: ,
},
];
diff --git a/docs/app/(home)/sections/HeroSection/HeroSection.tsx b/docs/app/(home)/sections/HeroSection/HeroSection.tsx
index 7bfd87232..2c0cc26a9 100644
--- a/docs/app/(home)/sections/HeroSection/HeroSection.tsx
+++ b/docs/app/(home)/sections/HeroSection/HeroSection.tsx
@@ -116,6 +116,30 @@ function MobilePlaygroundButton({ className = "" }: { className?: string }) {
);
}
+function DesktopGithubButton({
+ href,
+ label = "Star on GitHub",
+ className = "",
+}: {
+ href: string;
+ label?: string;
+ className?: string;
+}) {
+ return (
+ }
+ >
+
+
+
+ {label}
+
+ );
+}
+
function AnnouncementBanner({ className = "" }: { className?: string }) {
return (
<>
@@ -150,10 +174,18 @@ function AnnouncementBanner({ className = "" }: { className?: string }) {
);
}
-function GitHubBanner({ className = "" }: { className?: string }) {
+function GitHubBanner({
+ href = "https://github.com/thesysdev/openui",
+ label = "Star us on Github",
+ className = "",
+}: {
+ href?: string;
+ label?: string;
+ className?: string;
+}) {
return (
- Star us on Github
+ {label}
@@ -180,6 +212,8 @@ function DesktopHero({
compact,
showBanner,
showPlaygroundButton,
+ githubRepoUrl,
+ githubButtonLabel,
}: {
title: ReactNode;
subtitle: ReactNode;
@@ -187,7 +221,13 @@ function DesktopHero({
compact: boolean;
showBanner: boolean;
showPlaygroundButton: boolean;
+ githubRepoUrl?: string;
+ githubButtonLabel?: string;
}) {
+ // The shadow-room class compensates for the absent secondary CTA — only
+ // applied when both the playground button AND the GitHub button are off.
+ const hasSecondaryCta = showPlaygroundButton || !!githubRepoUrl;
+
return (
@@ -203,11 +243,14 @@ function DesktopHero({
{showPlaygroundButton && }
+ {githubRepoUrl && (
+
+ )}
@@ -227,6 +270,7 @@ function MobileHero({
showBanner,
showPlaygroundButton,
showGitHubBanner,
+ githubRepoUrl,
mobileImageOverride,
mobileImageAlt,
mobileImageWidth,
@@ -241,6 +285,7 @@ function MobileHero({
showBanner: boolean;
showPlaygroundButton: boolean;
showGitHubBanner: boolean;
+ githubRepoUrl?: string;
mobileImageOverride?: string;
mobileImageAlt?: string;
mobileImageWidth?: number;
@@ -282,7 +327,9 @@ function MobileHero({
{showPlaygroundButton && }
- {showGitHubBanner && }
+ {showGitHubBanner && (
+
+ )}
{/* Mobile hero image */}
@@ -391,6 +438,8 @@ export function HeroSection({
tagline,
taglineCompact = false,
showGitHubBanner = true,
+ githubRepoUrl,
+ githubButtonLabel,
mobilePreviewImage,
mobilePreviewImageAlt,
mobilePreviewImageWidth,
@@ -412,6 +461,12 @@ export function HeroSection({
tagline?: ReactNode;
taglineCompact?: boolean;
showGitHubBanner?: boolean;
+ /** When set, adds a desktop GitHub PillLink CTA pointing here AND uses
+ * this URL for the mobile GitHub banner (instead of the default openui
+ * repo). Useful for sub-product pages like /openclaw-os. */
+ githubRepoUrl?: string;
+ /** Optional override for the desktop GitHub button label (default: "Star on GitHub"). */
+ githubButtonLabel?: string;
mobilePreviewImage?: string;
mobilePreviewImageAlt?: string;
mobilePreviewImageWidth?: number;
@@ -429,6 +484,8 @@ export function HeroSection({
compact={compact}
showBanner={showBanner}
showPlaygroundButton={showPlaygroundButton}
+ githubRepoUrl={githubRepoUrl}
+ githubButtonLabel={githubButtonLabel}
/>
&1 | Out-Null
+ if ($LASTEXITCODE -eq 0) {
+ # The openclaw CLI emits plugin-registration logs to stdout when loading a
+ # plugin to discover its commands; grep just the URL line.
+ $output = (& openclaw os url 2>$null | Out-String)
+ if ($output) {
+ $match = [regex]::Match($output, 'https?://[^\s]+')
+ if ($match.Success) { $url = $match.Value }
+ }
+ }
+
+ if (-not $url) {
+ Write-Warn2 '`openclaw os url` not available — older plugin or missing token.'
+ Write-Log "Open http://127.0.0.1:18789/plugins/$PluginPathSlug and paste the token from $OpenclawConfig."
+ return
+ }
+
+ Write-Host ""
+ Write-Host " Dashboard URL: " -NoNewline
+ Write-Host $url -ForegroundColor Cyan
+ Write-Host ""
+
+ try {
+ Set-Clipboard -Value $url -ErrorAction Stop
+ Write-Log 'Copied to clipboard.'
+ } catch {
+ # Set-Clipboard missing on PS 5.0; ignore — URL was printed above.
+ }
+
+ try {
+ Start-Process $url -ErrorAction Stop
+ Write-Ok 'Opened in your browser. Keep that tab to use OpenClaw OS.'
+ } catch {
+ Write-Log 'Open the URL above to use OpenClaw OS.'
+ }
+}
+
function Do-Install {
Banner
Check-Prereqs
@@ -245,8 +290,7 @@ function Do-Install {
Verify
Write-Host ""
Write-Host "✓ OpenClaw OS installed." -ForegroundColor Cyan
- Write-Host " Open the Claw UI from your OpenClaw client to start generating apps." -ForegroundColor DarkGray
- Write-Host ""
+ Print-DashboardUrl
}
function Do-Uninstall {
diff --git a/docs/public/openclaw-os/install.sh b/docs/public/openclaw-os/install.sh
index 151dfccea..da10d45c6 100755
--- a/docs/public/openclaw-os/install.sh
+++ b/docs/public/openclaw-os/install.sh
@@ -13,6 +13,7 @@ REPO="thesysdev/openclaw-os"
SRC_DIR="$HOME/.openclaw/openui/openclaw-os"
PLUGIN_DIR="$SRC_DIR/packages/claw-plugin"
PLUGIN_ID="openclaw-os-plugin"
+PLUGIN_PATH_SLUG="openclawos"
OPENCLAW_CONFIG="$HOME/.openclaw/openclaw.json"
BOLD='\033[1m'
@@ -34,7 +35,7 @@ require_cmd() {
}
banner() {
- printf "\n${BOLD}${ACCENT}OpenClaw OS${NC} ${INFO}— Generative UI for OpenClaw${NC}\n\n"
+ printf "\n${BOLD}${ACCENT}OpenClaw OS${NC} ${INFO}— The default workspace for OpenClaw${NC}\n\n"
}
check_prereqs() {
@@ -170,6 +171,53 @@ verify() {
fi
}
+print_dashboard_url() {
+ step "Opening OpenClaw OS"
+
+ # The plugin (via `api.registerCli`) constructs the auth-bearing URL from the
+ # gateway-validated config — survives `--dev`/`--profile`, no JSON parsing.
+ # Clipboard + browser open stay in shell so the plugin doesn't need
+ # `child_process` (would trip openclaw's install security scan).
+ local url=""
+ if openclaw os --help >/dev/null 2>&1; then
+ # The openclaw CLI emits plugin-registration logs to stdout when loading a
+ # plugin to discover its commands. The action runs *after* registration, so
+ # the URL is the last http-shaped line. `tail -n1` is more robust than
+ # `head -1` against future log lines that happen to contain URLs.
+ url="$(openclaw os url 2>/dev/null | grep -Eo 'https?://[^[:space:]]+' | tail -n1 || true)"
+ fi
+
+ if [[ -z "$url" ]]; then
+ warn "\`openclaw os url\` not available — older plugin or missing token."
+ log "Open http://127.0.0.1:18789/plugins/$PLUGIN_PATH_SLUG and paste the token from $OPENCLAW_CONFIG."
+ return
+ fi
+
+ printf "\n ${BOLD}Dashboard URL:${NC} %s\n\n" "$url"
+
+ case "$(uname -s)" in
+ Darwin)
+ command -v pbcopy >/dev/null 2>&1 && printf '%s' "$url" | pbcopy 2>/dev/null && log "Copied to clipboard."
+ open "$url" >/dev/null 2>&1 && ok "Opened in your browser. Keep that tab to use OpenClaw OS." \
+ || log "Open the URL above to use OpenClaw OS."
+ ;;
+ Linux)
+ if command -v wl-copy >/dev/null 2>&1; then printf '%s' "$url" | wl-copy 2>/dev/null && log "Copied to clipboard."
+ elif command -v xclip >/dev/null 2>&1; then printf '%s' "$url" | xclip -selection clipboard 2>/dev/null && log "Copied to clipboard."
+ fi
+ if command -v xdg-open >/dev/null 2>&1; then
+ xdg-open "$url" >/dev/null 2>&1 && ok "Opened in your browser. Keep that tab to use OpenClaw OS." \
+ || log "Open the URL above to use OpenClaw OS."
+ else
+ log "Open the URL above to use OpenClaw OS."
+ fi
+ ;;
+ *)
+ log "Open the URL above to use OpenClaw OS."
+ ;;
+ esac
+}
+
uninstall_plugin() {
step "Disabling $PLUGIN_ID"
if openclaw plugins disable "$PLUGIN_ID" 2>&1; then
@@ -179,7 +227,8 @@ uninstall_plugin() {
fi
step "Uninstalling $PLUGIN_ID"
- if openclaw plugins uninstall "$PLUGIN_ID" 2>&1; then
+ # --force skips the interactive y/N prompt (no TTY in `curl | bash`).
+ if openclaw plugins uninstall "$PLUGIN_ID" --force 2>&1; then
ok "Plugin uninstalled"
else
warn "Could not uninstall plugin (may not be registered). Continuing."
@@ -217,7 +266,7 @@ do_install() {
verify
printf "\n${SUCCESS}${BOLD}✓ OpenClaw OS installed.${NC}\n"
- printf "${INFO} Open the Claw UI from your OpenClaw client to start generating apps.${NC}\n\n"
+ print_dashboard_url
}
do_uninstall() {