openclaw/apps/web/app/layout.tsx
kumarabhirup eaef8df20b
fix(telemetry): prevent anonymousId from leaking via npm package
The root layout called getOrCreateAnonymousId() in a Server Component
without marking the route as dynamic. Next.js treated it as static,
pre-rendering the developer's UUID into the standalone build shipped
via npm — so every `npx denchclaw` user shared the same PostHog identity.

- Add `export const dynamic = "force-dynamic"` to root layout
- Replace `process.env.HOME || "~"` fallback with `homedir()` in web
  telemetry and posthog-analytics plugin (Node.js path.join doesn't
  expand "~", creating a relative path under cwd instead)
2026-03-06 23:30:31 -08:00

58 lines
1.8 KiB
TypeScript

import type { Metadata, Viewport } from "next";
import { Suspense } from "react";
import { getOrCreateAnonymousId } from "@/lib/telemetry";
import { PostHogProvider } from "./components/posthog-provider";
import "./globals.css";
export const metadata: Metadata = {
title: "DenchClaw",
description:
"AI Workspace with an agent that connects to your apps and does the work for you",
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 1,
};
export const dynamic = "force-dynamic";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const anonymousId = getOrCreateAnonymousId();
return (
<html lang="en" suppressHydrationWarning>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossOrigin="anonymous"
/>
<link
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
{/* Inline script to prevent FOUC — reads localStorage or system preference */}
<script
dangerouslySetInnerHTML={{
__html: `try{if(localStorage.theme==="dark"||(!("theme" in localStorage)&&window.matchMedia("(prefers-color-scheme: dark)").matches)){document.documentElement.classList.add("dark")}else{document.documentElement.classList.remove("dark")}}catch(e){}`,
}}
/>
</head>
<body className="antialiased">
<Suspense fallback={null}>
<PostHogProvider anonymousId={anonymousId}>
{children}
</PostHogProvider>
</Suspense>
</body>
</html>
);
}