import { startTransition, useEffect, useMemo, useRef, useState } from "react"; import type { FormEvent, ReactNode, Ref } from "react"; import { Link, Navigate, useLocation, useNavigate } from "react-router"; import { ArrowLeftIcon, ArrowRightIcon, ArrowTopRightOnSquareIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ClipboardDocumentCheckIcon, ClipboardIcon, EyeIcon, EyeSlashIcon, } from "@heroicons/react/16/solid"; import { type InstallFinishResponse, type InstallGithubAppOwner, type InstallLlmProviderInput, type InstallObjectStoreInput, type InstallSandboxInput, type InstallSessionResponse, createInstallGithubAppManifest, finishInstall, getInstallSession, persistInstallToken, putInstallGithubToken, putInstallLlm, putInstallObjectStore, putInstallSandbox, putInstallServer, readStoredInstallToken, testInstallGithubToken, testInstallLlm, testInstallObjectStore, testInstallSandbox, } from "./install-api"; import { INSTALL_PROVIDERS } from "./install-config"; import { shouldRedirectAfterHealthPoll } from "./install-flow"; import { consumeInstallGithubErrorFromUrl, consumeInstallTokenFromUrl, shouldConsumeInstallGithubErrorForPath, } from "./mode"; import { CopyButton, ErrorMessage, INPUT_CLASS, PRIMARY_BUTTON_CLASS, SECONDARY_BUTTON_CLASS, } from "./components/ui"; import { LoadingState } from "./components/state"; const INSTALL_STEPS = [ { id: "welcome", label: "Welcome", href: "/install/welcome" }, { id: "server", label: "Server", href: "/install/server" }, { id: "object_store", label: "Storage", href: "/install/object-store" }, { id: "sandbox", label: "Sandbox", href: "/install/sandbox" }, { id: "llm", label: "LLMs", href: "/install/llm" }, { id: "github", label: "GitHub", href: "/install/github" }, { id: "review", label: "Review", href: "/install/review" }, ] as const; const STEPPER_STEPS = INSTALL_STEPS.slice(1); type StepId = (typeof INSTALL_STEPS)[number]["id"]; type FinishState = InstallFinishResponse | null; type GithubStrategy = "token" | "app"; type GithubOwnerKind = "personal" | "org"; type SessionState = | { status: "idle" } | { status: "loading" } | { status: "error"; message: string } | { status: "ready"; data: InstallSessionResponse }; type TokenForm = { token: string; username: string }; type AppForm = { owner: InstallGithubAppOwner; appName: string; allowedUsername: string; }; type ProviderSelection = Record; type ObjectStoreProvider = "local" | "s3"; type ObjectStoreCredentialMode = "runtime" | "access_key"; type ObjectStoreForm = { provider: ObjectStoreProvider; localRoot: string; bucket: string; region: string; credentialMode: ObjectStoreCredentialMode; accessKeyId: string; secretAccessKey: string; manualCredentialsSaved: boolean; }; type SandboxProvider = NonNullable; type SandboxForm = { provider: SandboxProvider; apiKey: string; apiKeySaved: boolean; }; export default function InstallApp() { const navigate = useNavigate(); const location = useLocation(); const [installToken, setInstallToken] = useState(() => readStoredInstallToken(), ); const [sessionState, setSessionState] = useState({ status: "idle" }); const session = sessionState.status === "ready" ? sessionState.data : null; const [manualToken, setManualToken] = useState(""); const [llmSelection, setLlmSelection] = useState(() => defaultProviderSelection(), ); const [objectStoreForm, setObjectStoreForm] = useState(() => defaultObjectStoreForm(), ); const [sandboxForm, setSandboxForm] = useState(() => defaultSandboxForm(), ); const [canonicalUrl, setCanonicalUrl] = useState(""); const [githubStrategy, setGithubStrategy] = useState("token"); const [tokenForm, setTokenForm] = useState({ token: "", username: "" }); const [appForm, setAppForm] = useState({ owner: { kind: "personal" }, appName: "Fabro", allowedUsername: "", }); const [saveError, setSaveError] = useState(null); const [submitting, setSubmitting] = useState(false); const [finishState, setFinishState] = useState(null); const [timedOut, setTimedOut] = useState(false); const canonicalUrlInputRef = useRef(null); const localRootInputRef = useRef(null); const bucketInputRef = useRef(null); const regionInputRef = useRef(null); const accessKeyIdInputRef = useRef(null); const secretAccessKeyInputRef = useRef(null); const sandboxApiKeyInputRef = useRef(null); useEffect(() => { const { token, sanitizedUrl } = consumeInstallTokenFromUrl(window.location.href); if (!token) return; persistInstallToken(token); setInstallToken(token); window.history.replaceState(window.history.state, "", sanitizedUrl); }, []); useEffect(() => { if (shouldConsumeInstallGithubErrorForPath(location.pathname)) { const { error, sanitizedUrl } = consumeInstallGithubErrorFromUrl(window.location.href); if (error) { setSaveError(error); window.history.replaceState(window.history.state, "", sanitizedUrl); return; } } setSaveError((current) => (current === null ? current : null)); }, [location.pathname]); useEffect(() => { if (!installToken) { setSessionState({ status: "idle" }); return; } let cancelled = false; setSessionState({ status: "loading" }); getInstallSession(installToken) .then((nextSession) => { if (cancelled) return; setSessionState({ status: "ready", data: nextSession }); setCanonicalUrl((current) => current || nextSession.server?.canonical_url || nextSession.prefill.canonical_url, ); setObjectStoreForm(hydrateObjectStoreForm(nextSession)); setSandboxForm((current) => hydrateSandboxForm(current, nextSession)); setLlmSelection((current) => hydrateProviderSelection(current, nextSession), ); if (nextSession.github?.strategy === "app") { setGithubStrategy("app"); setAppForm({ owner: nextSession.github.owner ?? { kind: "personal" }, appName: nextSession.github.app_name || "Fabro", allowedUsername: nextSession.github.allowed_username || "", }); } else if (nextSession.github?.strategy === "token") { setGithubStrategy("token"); setTokenForm((current) => ({ ...current, username: nextSession.github?.username || current.username, })); } }) .catch((error) => { if (cancelled) return; setSessionState({ status: "error", message: error instanceof Error ? error.message : "Install session failed", }); }); return () => { cancelled = true; }; }, [installToken]); useEffect(() => { if (!installToken || !session) return; if ((location.pathname === "/" || location.pathname === "/install") && !finishState) { startTransition(() => { navigate("/install/welcome", { replace: true }); }); } }, [finishState, installToken, location.pathname, navigate, session]); useEffect(() => { if (!finishState) return; setTimedOut(false); const deadline = window.setTimeout(() => { setTimedOut(true); }, 30_000); const controller = new AbortController(); let inFlight = false; const poll = async () => { if (inFlight || controller.signal.aborted) return; inFlight = true; try { const response = await fetch("/health", { signal: controller.signal }); const body = response.ok ? ((await response.json()) as { mode?: string }) : undefined; if ( shouldRedirectAfterHealthPoll({ kind: "response", ok: response.ok, mode: body?.mode, }) ) { window.location.href = finishState.restart_url; } } catch { if (controller.signal.aborted) return; if (shouldRedirectAfterHealthPoll({ kind: "error" })) { window.location.href = finishState.restart_url; } } finally { inFlight = false; } }; const interval = window.setInterval(poll, 2_000); return () => { controller.abort(); window.clearTimeout(deadline); window.clearInterval(interval); }; }, [finishState]); const currentStep = useMemo( () => STEPPER_STEPS.find((step) => location.pathname.startsWith(step.href))?.id ?? "welcome", [location.pathname], ); const completedSteps = new Set(session?.completed_steps ?? []); const sessionError = sessionState.status === "error" ? sessionState.message : null; if (!installToken) { return ( { const nextToken = manualToken.trim(); if (!nextToken) { setSessionState({ status: "error", message: "Paste the install token from the server logs.", }); return; } persistInstallToken(nextToken); setInstallToken(nextToken); setSessionState({ status: "idle" }); }} /> ); } const runStepSubmit = async (args: { action: () => Promise; fallback: string; next?: string; }) => { setSubmitting(true); setSaveError(null); try { await args.action(); if (args.next) { const nextSession = await getInstallSession(installToken); setSessionState({ status: "ready", data: nextSession }); setSandboxForm((current) => hydrateSandboxForm(current, nextSession)); navigate(args.next); } } catch (error) { setSaveError(error instanceof Error ? error.message : args.fallback); } finally { setSubmitting(false); } }; if (sessionState.status === "error") { return ( { const nextToken = manualToken.trim(); persistInstallToken(nextToken); setInstallToken(nextToken || null); }} /> ); } // Covers both sessionState "loading" AND the brief "idle" window between // the initial render and the session-fetch useEffect. Without this guard, // screens like GithubAppDoneScreen see `session == null` and navigate away // before the first fetch finishes — trapping the user in a redirect loop. if (!session) { return ( ); } if (finishState && location.pathname !== "/install/finishing") { return ; } return ( {location.pathname === "/install/finishing" ? ( ) : location.pathname === "/install/llm" ? ( { const providers = INSTALL_PROVIDERS.map(({ id }) => { const current = llmSelection[id] ?? { apiKey: "" }; return { provider: id, api_key: current.apiKey.trim(), }; }).filter((provider) => provider.api_key.length > 0); if (providers.length === 0) { setSaveError("Add at least one provider API key before continuing."); return; } await runStepSubmit({ action: async () => { await Promise.all( providers.map((provider) => testInstallLlm(installToken, provider)), ); await putInstallLlm(installToken, providers); }, fallback: "Failed to save LLM settings.", next: "/install/github", }); }} > ) : location.pathname === "/install/server" ? ( { if (!canonicalUrl.trim()) { setSaveError("Enter the canonical server URL before continuing."); focusInput(canonicalUrlInputRef); return; } await runStepSubmit({ action: () => putInstallServer(installToken, canonicalUrl.trim()), fallback: "Failed to save server settings.", next: "/install/object-store", }); }} > setCanonicalUrl(event.target.value)} className={INPUT_CLASS} placeholder="https://fabro.example.com" autoComplete="url" spellCheck={false} /> ) : location.pathname === "/install/object-store" ? ( { if (objectStoreForm.provider === "local") { if (!objectStoreForm.localRoot.trim()) { setSaveError("Enter the local object-store directory before continuing."); focusInput(localRootInputRef); return; } } else { if (!objectStoreForm.bucket.trim()) { setSaveError("Enter the S3 bucket before continuing."); focusInput(bucketInputRef); return; } if (!objectStoreForm.region.trim()) { setSaveError("Enter the AWS region before continuing."); focusInput(regionInputRef); return; } if (objectStoreForm.credentialMode === "access_key") { const accessKeyId = objectStoreForm.accessKeyId.trim(); const secretAccessKey = objectStoreForm.secretAccessKey.trim(); const keepStoredCredentials = objectStoreForm.manualCredentialsSaved && !accessKeyId && !secretAccessKey; if (!keepStoredCredentials && !accessKeyId) { setSaveError("Enter the AWS access key ID before continuing."); focusInput(accessKeyIdInputRef); return; } if (!keepStoredCredentials && !secretAccessKey) { setSaveError("Enter the AWS secret access key before continuing."); focusInput(secretAccessKeyInputRef); return; } } } const payload = buildObjectStorePayload(objectStoreForm); await runStepSubmit({ action: async () => { if (objectStoreForm.provider === "s3") { await testInstallObjectStore(installToken, payload); } await putInstallObjectStore(installToken, payload); }, fallback: "Failed to save object-store settings.", next: "/install/sandbox", }); }} > { setObjectStoreForm((current) => ({ ...current, provider })); if (provider === "s3") { focusInput(bucketInputRef); } else { focusInput(localRootInputRef); } }} /> {objectStoreForm.provider === "s3" ? (
setObjectStoreForm((current) => ({ ...current, bucket: event.target.value, })) } className={`${INPUT_CLASS} font-mono`} placeholder="my-fabro-data" spellCheck={false} autoCapitalize="off" /> setObjectStoreForm((current) => ({ ...current, region: event.target.value, })) } className={`${INPUT_CLASS} font-mono`} placeholder="us-east-1" spellCheck={false} autoCapitalize="off" /> { setObjectStoreForm((current) => ({ ...current, credentialMode })); if (credentialMode === "access_key") { focusInput(accessKeyIdInputRef); } }} /> {objectStoreForm.credentialMode === "access_key" ? (
setObjectStoreForm((current) => ({ ...current, accessKeyId: event.target.value, })) } className={`${INPUT_CLASS} font-mono`} placeholder="AKIA..." spellCheck={false} autoComplete="off" autoCapitalize="off" /> setObjectStoreForm((current) => ({ ...current, secretAccessKey: value, })) } placeholder="Secret access key" /> {objectStoreForm.manualCredentialsSaved ? (

Credentials saved. Leave both fields blank to keep them, or enter both fields to replace them.

) : null}
) : (

Fabro will use AWS credentials already provided by the runtime, such as EC2, ECS, or IRSA-based auth.

)}
) : (
setObjectStoreForm((current) => ({ ...current, localRoot: event.target.value, })) } className={`${INPUT_CLASS} font-mono`} placeholder="Local object-store directory" spellCheck={false} autoCapitalize="off" />

Fabro will store SlateDB and run artifacts under this directory.

)}
) : location.pathname === "/install/sandbox" ? ( { if (sandboxForm.provider === "daytona") { const apiKey = sandboxForm.apiKey.trim(); const keepStoredKey = sandboxForm.apiKeySaved && !apiKey; if (!keepStoredKey && !apiKey) { setSaveError("Enter the Daytona API key before continuing."); focusInput(sandboxApiKeyInputRef); return; } } const payload = buildSandboxPayload(sandboxForm); await runStepSubmit({ action: async () => { if (sandboxForm.provider === "daytona") { await testInstallSandbox(installToken, payload); } await putInstallSandbox(installToken, payload); }, fallback: "Failed to save sandbox settings.", next: "/install/llm", }); }} > { setSandboxForm((current) => ({ ...current, provider })); if (provider === "daytona") { focusInput(sandboxApiKeyInputRef); } }} /> {sandboxForm.provider === "daytona" ? (
setSandboxForm((current) => ({ ...current, apiKey: event.target.value, })) } className={`${INPUT_CLASS} font-mono`} placeholder={ sandboxForm.apiKeySaved ? "•••• (saved)" : "dtn_..." } autoComplete="off" spellCheck={false} />
) : (

Fabro will use the host Docker daemon. Make sure the server has access to /var/run/docker.sock.

)}
) : location.pathname === "/install/github/done" ? ( ) : location.pathname === "/install/github" ? ( { if (githubStrategy === "token") { const trimmedToken = tokenForm.token.trim(); if (!trimmedToken) { setSaveError("Provide the GitHub token before continuing."); return; } await runStepSubmit({ action: async () => { const username = await testInstallGithubToken(installToken, trimmedToken); setTokenForm({ token: trimmedToken, username }); await putInstallGithubToken(installToken, trimmedToken, username); }, fallback: "Failed to start GitHub setup.", next: "/install/review", }); return; } const { owner, appName, allowedUsername } = appForm; if (owner.kind === "org" && !(owner.slug ?? "").trim()) { setSaveError("Enter the organization slug for the GitHub App."); return; } if (!appName.trim()) { setSaveError("Enter the GitHub App name before continuing."); return; } if (!allowedUsername.trim()) { setSaveError("Enter the GitHub username that should be allowed to log in."); return; } await runStepSubmit({ action: async () => { const manifest = await createInstallGithubAppManifest(installToken, { owner: owner.kind === "org" ? { kind: "org", slug: (owner.slug ?? "").trim() } : { kind: "personal" }, app_name: appName.trim(), allowed_username: allowedUsername.trim(), }); submitGithubManifest( manifest.github_form_action, manifest.manifest, manifest.state, ); }, fallback: "Failed to start GitHub setup.", }); }} > setGithubStrategy(strategy)} /> {githubStrategy === "token" ? (
setTokenForm((current) => ({ ...current, token: value })) } placeholder="ghp_..." />
{tokenForm.username ? (

Previously validated as{" "} @{tokenForm.username}

) : null}

Create a fine-grained or classic token with{" "} repo scope.

github.com/settings/tokens
) : (
setAppForm((current) => ({ ...current, owner: kind === "org" ? { kind: "org", slug: current.owner.kind === "org" ? current.owner.slug ?? "" : "" } : { kind: "personal" }, })) } /> {appForm.owner.kind === "org" ? ( setAppForm((current) => ({ ...current, owner: { kind: "org", slug: event.target.value }, })) } className={INPUT_CLASS} placeholder="acme" spellCheck={false} /> ) : null} setAppForm((current) => ({ ...current, allowedUsername: event.target.value, })) } className={INPUT_CLASS} placeholder="octocat" spellCheck={false} /> {session?.server?.canonical_url ? (

After creating the app, GitHub will redirect back to{" "} {session.server.canonical_url}.

) : null}
)}
) : location.pathname === "/install/review" ? ( { setSubmitting(true); setSaveError(null); try { const result = await finishInstall(installToken); setFinishState(result); navigate("/install/finishing"); } catch (error) { setSaveError(error instanceof Error ? error.message : "Install failed."); } finally { setSubmitting(false); } }} /> ) : ( )}
); } function TokenEntryScreen({ manualToken, setManualToken, sessionError, onSubmit, }: { manualToken: string; setManualToken: (value: string) => void; sessionError: string | null; onSubmit: () => void; }) { return (
Fabro Install

Finish configuring this Fabro server

Find the one-time install token in your terminal, Docker logs, or platform log viewer, then paste it here to continue.

{ event.preventDefault(); onSubmit(); }} className="mt-8 space-y-5" >
setManualToken(event.target.value)} className={`${INPUT_CLASS} font-mono`} placeholder="Paste install token" spellCheck={false} autoComplete="off" autoCapitalize="off" autoFocus />
{sessionError ? : null}

Where to find it

Local
Output of{" "} fabro server start
Docker
docker logs <container>
Hosted
Your platform's log viewer or journalctl

Install mode is temporary and only available until setup completes.

); } function InstallLayout({ children, currentStep, completedSteps, }: { children: ReactNode; currentStep: StepId; completedSteps: Set; }) { const showStepper = currentStep !== "welcome"; return (
Fabro Install
{showStepper ? (
) : null}
{children}
); } function Stepper({ currentStep, completedSteps, }: { currentStep: StepId; completedSteps: Set; }) { const activeIndex = STEPPER_STEPS.findIndex((step) => step.id === currentStep); const safeIndex = activeIndex === -1 ? 0 : activeIndex; const activeStep = STEPPER_STEPS[safeIndex]; const progress = ((safeIndex + 1) / STEPPER_STEPS.length) * 100; return ( ); } function WelcomeScreen() { return (

Set up your Fabro server

A short walkthrough to confirm the public server URL, choose the shared object store and sandbox runtime, validate your LLM credentials, and connect GitHub. When you finish, Fabro restarts into normal mode.

    {[ ["Server URL", "Confirm where operators will reach Fabro."], [ "Object store", "Choose local disk or AWS S3 for SlateDB and artifacts.", ], ["Sandbox", "Choose Docker or Daytona for workflow execution."], ["LLMs", "Validate API keys for Anthropic, OpenAI, or Gemini."], ["GitHub", "Choose a personal access token or a GitHub App."], ["Review", "Double-check the plan, then write the files."], ].map(([title, body], index) => (
  1. {title}

    {body}

  2. ))}
Start setup
); } function StepPanel({ title, description, children, error, submitting, submitLabel = "Continue", submittingLabel = "Saving...", backHref, onSubmit, }: { title: string; description: string; children: ReactNode; error: string | null; submitting: boolean; submitLabel?: string; submittingLabel?: string; backHref?: string; onSubmit: () => Promise; }) { return (
) => { event.preventDefault(); if (submitting) return; void onSubmit(); }} className="space-y-8" >

{title}

{description}

{children}
{error ? : null}
{backHref ? ( Back ) : ( )}
); } function ReviewScreen({ session, error, submitting, onInstall, }: { session: InstallSessionResponse | null; error: string | null; submitting: boolean; onInstall: () => Promise; }) { const providers = (session?.llm?.providers ?? []) .map((provider) => describeProvider(provider.provider)) .join(", "); const serverUrl = session?.server?.canonical_url || session?.prefill.canonical_url || "Unknown"; return (
) => { event.preventDefault(); if (submitting) return; void onInstall(); }} className="space-y-8" >

Review and install

Confirm the plan below. Fabro writes the configuration to disk, then restarts into normal mode.

} /> {renderObjectStoreSummaryRows(session?.object_store)} {renderSandboxSummaryRows(session?.sandbox)} {renderGithubSummaryRows(session?.github, serverUrl)}
{error ? : null}
Back
); } function FinishingScreen({ finishState, timedOut, }: { finishState: FinishState; timedOut: boolean; }) { if (!finishState) { return ; } return (

{timedOut ? "Install complete" : "Finishing up"}

{timedOut ? "The server didn't come back automatically. Start it manually and return to the URL below." : "Configuration written. Waiting for the server to restart into normal mode."}

{timedOut ? (
Run fabro server start, then visit{" "} {finishState.restart_url}.
) : (

Polling /health

)} {finishState.dev_token ? (

Development token

Use this to sign in after the server restarts.

) : null}
); } function ProviderFields({ value, onChange, }: { value: ProviderSelection; onChange: (nextValue: ProviderSelection) => void; }) { return (
{INSTALL_PROVIDERS.map((provider) => { const current = value[provider.id] ?? { apiKey: "" }; return (
onChange({ ...value, [provider.id]: { ...current, apiKey: next }, }) } placeholder={provider.envVar} />

{provider.keyHelp.text}

{provider.keyHelp.url.replace(/^https?:\/\//, "")}
); })}
); } type CardOption = { id: T; title: string; body: string }; function CardPicker({ legend, options, value, onChange, }: { legend: string; options: ReadonlyArray>; value: T; onChange: (value: T) => void; }) { return (
{legend}
{options.map((option) => ( onChange(option.id)} title={option.title} body={option.body} /> ))}
); } const GITHUB_STRATEGY_OPTIONS: ReadonlyArray> = [ { id: "token", title: "Personal access token", body: "Quickest path. Validates a PAT and stores it in the vault.", }, { id: "app", title: "GitHub App", body: "Recommended for teams. Enables OAuth.", }, ]; const OBJECT_STORE_PROVIDER_OPTIONS: ReadonlyArray> = [ { id: "local", title: "Local disk", body: "Uses the host filesystem for SlateDB and run artifacts.", }, { id: "s3", title: "AWS S3", body: "Uses one S3 bucket with fixed slatedb/ and artifacts/ prefixes.", }, ]; const OBJECT_STORE_CREDENTIAL_MODE_OPTIONS: ReadonlyArray< CardOption > = [ { id: "runtime", title: "Use AWS runtime credentials", body: "Use credentials already supplied by the deployment environment.", }, { id: "access_key", title: "Enter AWS access key credentials", body: "Store an access key pair in server.env for startup and validation.", }, ]; const SANDBOX_PROVIDER_OPTIONS: ReadonlyArray> = [ { id: "docker", title: "Docker", body: "Default. Uses the host Docker daemon to run sandbox containers.", }, { id: "daytona", title: "Daytona", body: "Each run gets a managed Daytona cloud sandbox. Requires an API key.", }, ]; const GITHUB_OWNER_OPTIONS: ReadonlyArray> = [ { id: "personal", title: "Personal account", body: "GitHub's personal app creation flow.", }, { id: "org", title: "Organization", body: "GitHub's org flow — requires the org slug.", }, ]; function OptionCard({ selected, onSelect, title, body, }: { selected: boolean; onSelect: () => void; title: string; body: string; }) { const base = "group relative flex items-start gap-3 rounded-lg px-4 py-3.5 text-left outline-1 -outline-offset-1 transition-colors"; const state = selected ? "bg-teal-500/10 outline-teal-500/60" : "bg-overlay outline-white/10 hover:bg-overlay-strong hover:outline-white/15"; return ( ); } function GithubAppDoneScreen({ github, }: { github: InstallSessionResponse["github"]; }) { if (!github || github.strategy !== "app") { return ; } return (

GitHub App connected

The app credentials are staged. They'll be written into the runtime env file when the install finishes.

Continue to review
); } function SummaryRow({ label, value, mono, action, }: { label: string; value: string; mono?: boolean; action?: ReactNode; }) { return (
{label}
{value} {action}
); } function Field({ label, hint, children, }: { label: string; hint?: string; children: ReactNode; }) { return ( ); } function PasswordInput({ id, name, value, onChange, placeholder, inputRef, }: { id?: string; name: string; value: string; onChange: (value: string) => void; placeholder?: string; inputRef?: Ref; }) { const [visible, setVisible] = useState(false); return (
onChange(event.target.value)} className={`${INPUT_CLASS} pr-11 font-mono`} placeholder={placeholder} spellCheck={false} autoComplete="off" autoCapitalize="off" />
); } function CopyableToken({ token }: { token: string }) { const [copied, setCopied] = useState(false); return (
        {token}
      
); } function HelpDisclosure({ summary, children, }: { summary: string; children: ReactNode; }) { return (
{summary}
{children}
); } function ExternalLink({ href, children, }: { href: string; children: ReactNode; }) { return ( {children} ); } function Spinner({ className = "" }: { className?: string }) { return ( ); } function defaultProviderSelection(): ProviderSelection { return Object.fromEntries( INSTALL_PROVIDERS.map((provider) => [provider.id, { apiKey: "" }]), ); } function defaultObjectStoreForm(localRoot = ""): ObjectStoreForm { return { provider: "local", localRoot, bucket: "", region: "", credentialMode: "runtime", accessKeyId: "", secretAccessKey: "", manualCredentialsSaved: false, }; } function hydrateProviderSelection( current: ProviderSelection, session: InstallSessionResponse, ): ProviderSelection { const hasUserInput = Object.values(current).some((provider) => provider.apiKey); if (hasUserInput) return current; const next = defaultProviderSelection(); for (const provider of session.llm?.providers ?? []) { next[provider.provider] = { apiKey: "" }; } return next; } function hydrateObjectStoreForm(session: InstallSessionResponse): ObjectStoreForm { const summary = session.object_store; if (!summary || summary.provider === "local") { return defaultObjectStoreForm( summary?.root ?? session.prefill.object_store_local_root, ); } return { provider: "s3", localRoot: session.prefill.object_store_local_root, bucket: summary.bucket ?? "", region: summary.region ?? "", credentialMode: summary.credential_mode === "access_key" ? "access_key" : "runtime", accessKeyId: "", secretAccessKey: "", manualCredentialsSaved: Boolean(summary.manual_credentials_saved), }; } function buildObjectStorePayload(form: ObjectStoreForm): InstallObjectStoreInput { if (form.provider === "local") { return { provider: "local", root: form.localRoot.trim() }; } const payload: InstallObjectStoreInput = { provider: "s3", bucket: form.bucket.trim(), region: form.region.trim(), credential_mode: form.credentialMode, }; const accessKeyId = form.accessKeyId.trim(); const secretAccessKey = form.secretAccessKey.trim(); if (form.credentialMode === "access_key") { if (accessKeyId) { payload.access_key_id = accessKeyId; } if (secretAccessKey) { payload.secret_access_key = secretAccessKey; } } return payload; } function defaultSandboxForm(): SandboxForm { return { provider: "docker", apiKey: "", apiKeySaved: false }; } function hydrateSandboxForm( current: SandboxForm, session: InstallSessionResponse, ): SandboxForm { const summary = session.sandbox; if (!summary) { return current.apiKey ? { ...current, apiKeySaved: false } : defaultSandboxForm(); } if (current.apiKey) { return { ...current, apiKeySaved: Boolean(summary.api_key_saved) }; } return { provider: summary.provider === "daytona" ? "daytona" : "docker", apiKey: "", apiKeySaved: Boolean(summary.api_key_saved), }; } function buildSandboxPayload(form: SandboxForm): InstallSandboxInput { if (form.provider === "docker") { return { provider: "docker" }; } const apiKey = form.apiKey.trim(); const payload: InstallSandboxInput = { provider: "daytona" }; if (apiKey) { payload.api_key = apiKey; } return payload; } function focusInput(ref: { current: HTMLInputElement | null }): void { window.setTimeout(() => ref.current?.focus(), 0); } function describeProvider(id: string): string { const match = INSTALL_PROVIDERS.find((provider) => provider.id === id); return match?.label ?? id; } function renderGithubSummaryRows( github: InstallSessionResponse["github"], serverUrl: string, ): ReactNode { if (!github) { return ; } if (github.strategy === "app") { return ( <> ); } return ( <> ); } function githubCallbackUrl(serverUrl: string): string { return `${serverUrl.replace(/\/+$/, "")}/auth/callback/github`; } function renderObjectStoreSummaryRows( objectStore: InstallSessionResponse["object_store"], ): ReactNode { if (!objectStore) { return ; } if (objectStore.provider === "local") { return ( <> ); } return ( <> ); } function renderSandboxSummaryRows( sandbox: InstallSessionResponse["sandbox"], ): ReactNode { if (!sandbox) { return ; } if (sandbox.provider === "daytona") { return ( <> ); } return ; } function describeGithubAppOwner( owner: InstallGithubAppOwner | undefined, ): string { if (!owner || owner.kind === "personal") return "Personal account"; return owner.slug ? `@${owner.slug} (organization)` : "Organization"; } function submitGithubManifest( formAction: string, manifest: Record, state: string, ): void { const form = document.createElement("form"); form.method = "post"; form.action = formAction; form.style.display = "none"; const manifestInput = document.createElement("input"); manifestInput.type = "hidden"; manifestInput.name = "manifest"; manifestInput.value = JSON.stringify(manifest); form.appendChild(manifestInput); const stateInput = document.createElement("input"); stateInput.type = "hidden"; stateInput.name = "state"; stateInput.value = state; form.appendChild(stateInput); document.body.appendChild(form); form.submit(); }