import { useState, type FormEvent } from "react"; import { Button } from "@executor-js/react/components/button"; import { Input } from "@executor-js/react/components/input"; import { Label } from "@executor-js/react/components/label"; import { authClient } from "./auth-client"; import { mcpAuthorizeResumeTarget, safeReturnTo } from "../src/auth/return-to"; // Self-host login: email + password sign-in via Better Auth. On success we // reload so the shared AuthProvider re-reads /account/me and the AuthGate swaps // in the app. (Cloud's equivalent is a WorkOS redirect — this is the // provider-specific piece injected into the shared shell.) // // There is no self-signup here: open registration is closed. New people join by // redeeming an invite — either the full /join/ link, or by entering the // code here ("Have an invite code?"), which forwards to the same join page. export const LoginPage = () => { const search = window.location.search; // Where to go after sign-in: resume an interrupted MCP OAuth authorize if we // arrived from one (Better Auth redirects it here with the OAuth params), // otherwise honor a safe returnTo (e.g. an integration OAuth callback), else // land on the dashboard. const postLogin = mcpAuthorizeResumeTarget(search) ?? safeReturnTo(new URLSearchParams(search).get("returnTo")) ?? "/"; const [mode, setMode] = useState<"signin" | "code">("signin"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [code, setCode] = useState(""); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const signIn = async (event: FormEvent) => { event.preventDefault(); setBusy(true); setError(null); const result = await authClient.signIn.email({ email, password }); if (result.error) { setBusy(false); setError(result.error.message ?? "Sign in failed"); return; } window.location.href = postLogin; }; const redeem = (event: FormEvent) => { event.preventDefault(); const trimmed = code.trim(); if (!trimmed) return; // Forward to the join page, which collects name/email/password and redeems. window.location.href = `/join/${encodeURIComponent(trimmed)}`; }; return (

Executor

{mode === "signin" ? "Sign in to your instance" : "Join with your invite code"}

{mode === "signin" ? (
setEmail((e.target as HTMLInputElement).value)} autoComplete="email" required />
setPassword((e.target as HTMLInputElement).value)} autoComplete="current-password" required minLength={8} />
{error &&

{error}

}
) : (
setCode((e.target as HTMLInputElement).value)} autoFocus />
)}
); };