import type { ReactNode } from "react"; import { useState } from "react"; import { useExternalStoreSnapshot } from "@tutti-os/ui-react-hooks"; import { Button, CheckIcon, CopyIcon, RefreshIcon, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, cn } from "@tutti-os/ui-system"; import { AgentSetupDialog, AgentSetupStepIcon, type AgentSetupStepStatus } from "../../../shared/agentEnv/AgentSetupDialog.tsx"; import { useAgentTargetSetupController } from "../../../shared/agentEnv/agentTargetSetupController.tsx"; import { resolveAgentErrorPresentation } from "../../../shared/agentEnv/agentErrorPresentation.ts"; import { useAgentVisibleErrorPresentation } from "../../../shared/visibleError/AgentVisibleErrorPresentationContext.tsx"; import { useAgentHostApi } from "../../../agentActivityHost.tsx"; import type { AgentHostAgentTargetSetupSnapshot } from "../../../host/agentHostApi.ts"; import { useTranslation } from "../../../i18n/index.ts"; import styles from "../AgentGUINode.styles.ts"; export interface AgentTargetSetupGateProps { children?: ReactNode; carouselMountedExternally: boolean; dialogOwner?: boolean; gateVisible?: boolean; } export function AgentTargetSetupGate({ children, carouselMountedExternally, dialogOwner = false, gateVisible = true }: AgentTargetSetupGateProps): React.JSX.Element { const controller = useAgentTargetSetupController(); const { scope } = useAgentVisibleErrorPresentation(); const { terminalLogin } = useAgentHostApi(); const { t } = useTranslation(); const state = useExternalStoreSnapshot(controller); const { agentTarget, agentTargetId, authenticatePending, dialogOpen, enabled, installPending, selectedAuthMethodId, setup, terminalLoginAvailable, terminalLoginError, terminalLoginPhase } = state; const { snapshot, loading, failed } = setup; const authMethods = snapshot?.authMethods ?? []; const account = snapshot?.account ?? null; const effectiveAuthMethodId = authMethods.some( (method) => method.id === selectedAuthMethodId ) ? (selectedAuthMethodId ?? "") : authMethods.some((method) => method.id === account?.authMethodId) ? (account?.authMethodId ?? "") : (authMethods[0]?.id ?? ""); const effectiveAuthMethod = authMethods.find( (method) => method.id === effectiveAuthMethodId ); const terminalLoginCommand = effectiveAuthMethod?.type === "terminal" ? (effectiveAuthMethod.terminalCommand?.trim() ?? "") || null : null; const terminalStartupAction = effectiveAuthMethod?.type === "terminal" ? (effectiveAuthMethod.terminalStartupAction ?? null) : null; // A shared caller can observe the Owner target's readiness, but must never // be routed into this device's setup/login/install workflow. Shared hosts // own their separate availability presentation and execution errors. if (!enabled || scope === "shared_caller") { return <>{children}; } const setupChecking = !snapshot && !failed; const setupBlocked = failed || !snapshot || snapshot.status !== "ready"; const handleInstall = async () => { const plan = snapshot?.plan; if (plan) await controller.install(plan.planDigest); }; const handleAuthenticate = async () => { if (!effectiveAuthMethodId) return; await controller.authenticate(effectiveAuthMethodId); }; const terminalLoginLaunchAvailable = terminalLoginAvailable && Boolean(terminalLoginCommand) && (!terminalStartupAction || Boolean( terminalLogin?.supportedStartupActionTypes?.includes( terminalStartupAction.type ) )); const handleTerminalLoginStart = async () => { if (!terminalLoginCommand) return; await controller.startTerminalLogin({ command: terminalLoginCommand, startupAction: terminalStartupAction }); }; const handleTerminalLoginCancel = () => controller.cancelTerminalLogin(); const actionRunning = isSetupActionRunning(snapshot?.action?.status); const actionFailed = isSetupActionFailed(snapshot?.action?.status); const installRetryAvailable = snapshot?.status === "failed" && snapshot.action?.kind === "install" && actionFailed && Boolean(snapshot.plan); const phase = actionRunning ? (snapshot?.action?.phase ?? null) : null; const statusLabel = phase ? targetSetupPhaseLabel(t, phase) : loading ? t("agentHost.agentGui.targetSetupChecking") : null; const detectionStatus: AgentSetupStepStatus = failed ? "error" : loading || !snapshot ? "running" : snapshot ? "ok" : "pending"; const installStatus = resolveInstallStepStatus(snapshot); const loginStatus = resolveLoginStepStatus(snapshot); const providerLabel = agentTarget?.label ?? agentTargetId; const setupFailurePresentation = snapshot?.status === "failed" ? resolveAgentErrorPresentation(snapshot.reason) : null; const setupFailureMessage = setupFailurePresentation?.messageKey ? t(setupFailurePresentation.messageKey, { provider: providerLabel }) : null; const accountDetail = account ? [account.displayName, account.organization].filter(Boolean).join(" · ") : undefined; const setupDescription = setupChecking ? t("agentHost.agentGui.targetSetupChecking") : snapshot?.status === "auth_required" ? t("agentHost.agentGui.targetSetupAuthRequired") : snapshot?.status === "ready" ? t("agentHost.agentGui.targetSetupReady") : (setupFailureMessage ?? t("agentHost.agentGui.targetSetupDescription")); const authenticationAvailable = snapshot?.status === "auth_required" || snapshot?.status === "ready"; const signInApplicable = authMethods.length > 0 || account !== null || snapshot?.status === "auth_required" || snapshot?.status === "authenticating"; // Prefer setup projection over the local authenticate promise. OAuth can mark // the account ready before the host login call settles. const loginActionPending = authenticatePending && snapshot?.status !== "ready"; return ( <> {gateVisible && setupBlocked ? (
{carouselMountedExternally ? (
) : ( children )} {dialogOwner ? ( void controller.refresh()} > {t("agentHost.agentGui.targetSetupRetry")} } >

{statusLabel ?? (snapshot?.status === "ready" ? t("agentHost.agentGui.targetSetupComplete", { provider: providerLabel }) : t("agentHost.agentGui.targetSetupRemaining", { provider: providerLabel }))}

    void handleInstall()} > {installPending ? t("agentHost.agentGui.targetSetupStarting") : t( installRetryAvailable ? "agentHost.agentGui.targetSetupReinstall" : "agentHost.agentGui.targetSetupInstall" )} ) : undefined } > {snapshot?.plan ? ( {snapshot.plan.installRoot} ) : null} {signInApplicable ? ( 0 && !terminalLoginCommand ? ( ) : terminalLoginCommand && terminalLoginLaunchAvailable && terminalLoginPhase !== "waiting" ? ( ) : undefined } > {terminalLoginCommand ? ( ) : null} {snapshot?.status === "auth_required" && authMethods.length > 0 ? ( ) : snapshot?.status === "auth_required" ? (

    {t("agentHost.agentGui.targetSetupNoAuthMethods")}

    ) : null}
    ) : null}
{failed || snapshot?.status === "failed" || actionFailed ? (

{snapshot?.action?.kind === "authenticate" ? t("agentHost.agentGui.targetSetupAuthFailed") : t("agentHost.agentGui.targetSetupFailed")} {setupFailureMessage ? ( {setupFailureMessage} ) : snapshot?.action?.errorMessage?.trim() ? ( {snapshot.action.errorMessage.trim()} ) : null}

) : null}
) : null} ); } function TerminalLoginGuide({ command, error, onCancelLogin, waiting = false }: { command: string; error?: string | null; onCancelLogin?: () => void; waiting?: boolean; }): React.JSX.Element { const { t } = useTranslation(); const { clipboard } = useAgentHostApi(); const [copiedCommand, setCopiedCommand] = useState(null); const copied = copiedCommand === command; const handleCopy = async () => { try { await clipboard.writeText(command); setCopiedCommand(command); } catch (error) { // Clipboard unavailable; the command text remains selectable. console.warn("agent-gui: clipboard copy failed", error); } }; return (

{waiting ? t("agentHost.agentGui.targetSetupTerminalLoginWaiting") : t("agentHost.agentGui.targetSetupTerminalAuthHint")}

{command}
{waiting && onCancelLogin ? (
) : null} {error ? (

{error}

) : null}
); } function isSetupActionRunning(status: string | undefined): boolean { return status === "queued" || status === "running"; } function isSetupActionFailed(status: string | undefined): boolean { return status === "failed" || status === "interrupted"; } function SetupTrackRow({ action, children, detail, label, status, warning = false }: { action?: ReactNode; children?: ReactNode; detail?: string; label: string; status: AgentSetupStepStatus; warning?: boolean; }): React.JSX.Element { return (
  • {label} {detail ? ( {detail} ) : null} {children} {action}
  • ); } function resolveInstallStepStatus( snapshot: AgentHostAgentTargetSetupSnapshot | null ): AgentSetupStepStatus { if (!snapshot) return "pending"; if (snapshot.status === "installing") return "running"; if (snapshot.status === "not_installed") return "error"; if ( snapshot.status === "auth_required" || snapshot.status === "authenticating" || snapshot.status === "ready" || snapshot.runtimeSource ) { return "ok"; } return snapshot.status === "failed" ? "error" : "pending"; } function resolveLoginStepStatus( snapshot: AgentHostAgentTargetSetupSnapshot | null ): AgentSetupStepStatus { if (!snapshot) return "pending"; if (snapshot.status === "authenticating") return "running"; if (snapshot.status === "auth_required") return "error"; if (snapshot.status === "ready") return "ok"; if ( snapshot.status === "failed" && snapshot.action?.kind === "authenticate" ) { return "error"; } return "pending"; } function targetSetupPhaseLabel( t: ReturnType["t"], phase: NonNullable["phase"] ): string { switch (phase) { case "preparing": return t("agentHost.agentGui.targetSetupPhase.preparing"); case "installing": return t("agentHost.agentGui.targetSetupPhase.installing"); case "verifying": return t("agentHost.agentGui.targetSetupPhase.verifying"); case "probing": return t("agentHost.agentGui.targetSetupPhase.probing"); case "activating": return t("agentHost.agentGui.targetSetupPhase.activating"); case "authenticating": return t("agentHost.agentGui.targetSetupPhase.authenticating"); case "complete": return t("agentHost.agentGui.targetSetupPhase.complete"); } }