import { Outlet, createRootRoute, useRouterState } from "@tanstack/react-router" import { TanStackRouterDevtools } from "@tanstack/react-router-devtools" import { useEffect, useState } from "react" import { getLauncherAuthStatus } from "@/api/launcher-auth" import { AppLayout } from "@/components/app-layout" import { initializeChatStore } from "@/features/chat/controller" import { isLauncherAuthPathname } from "@/lib/launcher-login-path" const RootLayout = () => { // Prefer the real address bar path: stale embedded bundles may not register // /launcher-login or /launcher-setup in the route tree, which would otherwise // keep AppLayout + gateway polling → 401 → launcherFetch redirect loop. const routerState = useRouterState({ select: (s) => ({ pathname: s.location.pathname, matches: s.matches, }), }) const windowPath = typeof globalThis.location !== "undefined" ? globalThis.location.pathname || "/" : routerState.pathname const isAuthPage = isLauncherAuthPathname(windowPath) || isLauncherAuthPathname(routerState.pathname) || routerState.matches.some( (m) => m.routeId === "/launcher-login" || m.routeId === "/launcher-setup", ) const [authError, setAuthError] = useState(null) // Session guard: proactively check auth status on every page load. // This catches the case where ?token= auto-login bypassed the login/setup UI. useEffect(() => { if (isAuthPage) return void getLauncherAuthStatus() .then((s) => { if (!s.initialized) { globalThis.location.assign("/launcher-setup") } else if (!s.authenticated) { globalThis.location.assign("/launcher-login") } }) .catch((err: unknown) => { // On 401/403, redirect to login — the session is invalid. // On 5xx (e.g. 503 when the auth store is unavailable) or network errors, // do NOT redirect: a subsequent successful login would loop straight back here. // launcherFetch handles 401 on real API calls regardless. if (err instanceof Error && /^status 40[13]$/.test(err.message)) { globalThis.location.assign("/launcher-login") } else { setAuthError( err instanceof Error ? err.message : "Auth service unavailable, please try to delete the launcher-auth.db at picoclaw home directory and restart the application.", ) } }) }, [isAuthPage]) useEffect(() => { if (isAuthPage) { return } initializeChatStore() }, [isAuthPage]) if (isAuthPage) { return ( <> {import.meta.env.DEV ? : null} ) } return ( <> {authError && (
Auth service error: {authError}
)} {import.meta.env.DEV ? : null} ) } export const Route = createRootRoute({ component: RootLayout })