"use client"; import { memo } from "react"; import { IconCheck, IconX, IconTerminal } from "@tabler/icons-react"; import { GridSpinner } from "@/components/grid-spinner"; import type { Message } from "@/lib/types/http"; import { Badge } from "@kandev/ui/badge"; import { ExpandableRow } from "./expandable-row"; import { useExpandState } from "./use-expand-state"; interface ScriptExecutionMetadata { script_type: "setup" | "cleanup" | "agent_boot"; agent_name?: string; command: string; status: "starting" | "running" | "exited" | "failed"; exit_code?: number; is_resuming?: boolean; started_at?: string; completed_at?: string; process_id?: string; error?: string; } function getAgentBootVerb( isResuming: boolean | undefined, isSuccess: boolean, isRunning: boolean, ): string { if (isResuming) { if (isSuccess) return "Resumed"; if (isRunning) return "Resuming"; return "Failed to resume"; } if (isSuccess) return "Started"; if (isRunning) return "Starting"; return "Failed to start"; } function AgentBootHeader({ metadata, isSuccess, isRunning, }: { metadata: ScriptExecutionMetadata; isSuccess: boolean; isRunning: boolean; }) { const agentName = metadata.agent_name || "agent"; const verb = getAgentBootVerb(metadata.is_resuming, isSuccess, isRunning); return (
{verb} agent {agentName} {isRunning && }
); } function ScriptHeader({ command, isSetup, isRunning, isSuccess, }: { command: string; isSetup: boolean; isRunning: boolean; isSuccess: boolean; }) { return (
{isSetup ? "Setup" : "Cleanup"} {command} {isRunning && } {isSuccess && !isRunning && } {!isSuccess && !isRunning && }
); } // Helper function to calculate duration function calculateDuration(startedAt: string, completedAt: string): string { try { const start = new Date(startedAt).getTime(); const end = new Date(completedAt).getTime(); const durationMs = end - start; if (durationMs < 1000) return `${durationMs}ms`; if (durationMs < 60000) return `${(durationMs / 1000).toFixed(1)}s`; const minutes = Math.floor(durationMs / 60000); const seconds = Math.floor((durationMs % 60000) / 1000); return `${minutes}m ${seconds}s`; } catch { return "N/A"; } } type ScriptFooterProps = { isAgentBoot: boolean; isRunning: boolean; metadata: ScriptExecutionMetadata; exitCode: number | undefined; }; function ScriptFooter({ isAgentBoot, isRunning, metadata, exitCode }: ScriptFooterProps) { if (isRunning) return null; if (isAgentBoot && metadata.started_at && metadata.completed_at) { return (
Duration: {calculateDuration(metadata.started_at, metadata.completed_at)}
); } if (!isAgentBoot && exitCode !== undefined) { return (
Exit code: {exitCode} {metadata.started_at && metadata.completed_at && ( Duration: {calculateDuration(metadata.started_at, metadata.completed_at)} )}
); } return null; } type ScriptBodyProps = { isAgentBoot: boolean; command: string; content: string; error: string | undefined; isRunning: boolean; metadata: ScriptExecutionMetadata; exitCode: number | undefined; }; function ScriptExpandedContent({ isAgentBoot, command, content, error, isRunning, metadata, exitCode, }: ScriptBodyProps) { return (
{isAgentBoot && command && (
Command
            {command}
          
)} {content && content.trim() !== "" && (
Output
            {content}
          
)} {error && (
Error
{error}
)}
); } function parseScriptMetadata(comment: Message) { const metadata = comment.metadata as ScriptExecutionMetadata | undefined; const status = metadata?.status; const scriptType = metadata?.script_type; const isRunning = status === "starting" || status === "running"; const isSuccess = status === "exited" && (metadata?.exit_code === 0 || metadata?.exit_code === undefined); return { metadata, status, scriptType, isRunning, isSuccess }; } export const ScriptExecutionMessage = memo(function ScriptExecutionMessage({ comment, }: { comment: Message; }) { const { metadata, status, scriptType, isRunning, isSuccess } = parseScriptMetadata(comment); const autoExpanded = isRunning; const { isExpanded, handleToggle } = useExpandState(status, autoExpanded); // Fallback for missing metadata if (!metadata || !scriptType) { const hasExpandableContent = !!comment.content; return ( } header={
Script Execution (metadata unavailable)
} hasExpandableContent={hasExpandableContent} isExpanded={isExpanded} onToggle={handleToggle} > {comment.content && (
              {comment.content}
            
)}
); } const { command, exit_code, error } = metadata; const isAgentBoot = scriptType === "agent_boot"; const isSetup = scriptType === "setup"; const hasDuration = !!(metadata.started_at && metadata.completed_at); const hasExpandableContent = !!( command || comment.content || error || exit_code !== undefined || hasDuration ); return ( } header={ isAgentBoot ? ( ) : ( ) } hasExpandableContent={hasExpandableContent} isExpanded={isExpanded} onToggle={handleToggle} > ); });