/** * `ModelServerLaunchBadge` — presentational launch-state indicator for an agent * whose OpenCode profile binds a local model server (F35.1). All state is folded * in {@link useAgents} (`modelServerStatusByServer` + `launchFailureByAgent`); * this component only renders it. * * It shows, in order of severity: * - a launch **failure** with its actionable message (not a bare "agent failed") * — the full logs are never shown by default; a "Détails" toggle reveals the * stable code/cause only; * - otherwise the model-server lifecycle: `probing`, `starting`, `ready` * (distinguishing a reused server from a freshly-started one). * * Renders nothing when there is neither a status to show nor a failure, so a * plain (non-local-model) agent row stays untouched. */ import { useState } from "react"; import { cn } from "@/shared"; import type { ModelServerStatus } from "@/domain"; import type { AgentLaunchFailure } from "./useAgents"; export interface ModelServerLaunchBadgeProps { /** The bound server's lifecycle status, when one has been observed. */ status?: ModelServerStatus; /** The agent's last launch failure, when one occurred. */ failure?: AgentLaunchFailure; } /** Human label + tone for a non-failed lifecycle state. */ function describeStatus( status: ModelServerStatus, ): { label: string; tone: string; role?: "status" } | null { switch (status.state) { case "notConfigured": return null; // Nothing to surface for an unmanaged endpoint. case "probing": return { label: "vérification du serveur…", tone: "bg-muted/20 text-muted", role: "status" }; case "starting": return { label: "démarrage du serveur…", tone: "bg-warning/20 text-warning", role: "status" }; case "downloading": return { label: "téléchargement du modèle…", tone: "bg-warning/20 text-warning", role: "status" }; case "ready": return status.reused ? { label: "serveur prêt (réutilisé)", tone: "bg-success/20 text-success" } : { label: "serveur prêt", tone: "bg-success/20 text-success" }; case "failed": // Handled by the failure branch below (richer, actionable). return null; } } export function ModelServerLaunchBadge({ status, failure, }: ModelServerLaunchBadgeProps) { const [showDetail, setShowDetail] = useState(false); // A launch failure (from `agentLaunchFailed`) or a `failed` status both mean // the launch could not complete — prefer the agent-scoped failure (it carries // the actionable message); fall back to the server `failed` status. const failed: { code: string; message: string; cause?: string } | null = failure ? failure : status?.state === "failed" ? { code: status.code, message: status.message } : null; if (failed) { return ( Échec du lancement : {failed.message} {showDetail && ( code {failed.code} {failed.cause ? ` · cause ${failed.cause}` : ""} )} ); } if (!status) return null; const described = describeStatus(status); if (!described) return null; return ( {described.label} ); }