import React from "react"; import { Terminal, CheckCircle, AlertTriangle, CircleDashed } from "lucide-react"; import { ToolViewProps } from "./types"; import { extractCommand, extractCommandOutput, extractExitCode, formatTimestamp, getToolTitle } from "./utils"; import { cn } from "@/lib/utils"; export function CommandToolView({ name = "execute-command", assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false }: ToolViewProps) { // Extract command with improved XML parsing const rawCommand = React.useMemo(() => { if (!assistantContent) return null; try { // Try to parse JSON content first const parsed = JSON.parse(assistantContent); if (parsed.content) { // Look for execute-command tag const commandMatch = parsed.content.match(/]*>([\s\S]*?)<\/execute-command>/); if (commandMatch) { return commandMatch[1].trim(); } } } catch (e) { // If JSON parsing fails, try direct XML extraction const commandMatch = assistantContent.match(/]*>([\s\S]*?)<\/execute-command>/); if (commandMatch) { return commandMatch[1].trim(); } } return null; }, [assistantContent]); // Clean the command by removing any leading/trailing whitespace and newlines const command = rawCommand ?.replace(/^suna@computer:~\$\s*/g, '') // Remove prompt prefix ?.replace(/\\n/g, '') // Remove escaped newlines ?.replace(/\n/g, '') // Remove actual newlines ?.trim(); // Clean up any remaining whitespace // Extract and clean the output with improved parsing const output = React.useMemo(() => { if (!toolContent) return null; try { // Try to parse JSON content first const parsed = JSON.parse(toolContent); if (parsed.content) { // Look for tool_result tag const toolResultMatch = parsed.content.match(/\s*([\s\S]*?)<\/execute-command>\s*<\/tool_result>/); if (toolResultMatch) { return toolResultMatch[1].trim(); } // Look for output field in a ToolResult pattern const outputMatch = parsed.content.match(/ToolResult\(.*?output='([\s\S]*?)'.*?\)/); if (outputMatch) { return outputMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'); } // Try to parse as direct JSON try { const outputJson = JSON.parse(parsed.content); if (outputJson.output) { return outputJson.output; } } catch (e) { // If JSON parsing fails, use the content as-is return parsed.content; } } } catch (e) { // If JSON parsing fails, try direct XML extraction const toolResultMatch = toolContent.match(/\s*([\s\S]*?)<\/execute-command>\s*<\/tool_result>/); if (toolResultMatch) { return toolResultMatch[1].trim(); } const outputMatch = toolContent.match(/ToolResult\(.*?output='([\s\S]*?)'.*?\)/); if (outputMatch) { return outputMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'); } } return toolContent; }, [toolContent]); const exitCode = extractExitCode(toolContent); const toolTitle = getToolTitle(name); return (
Terminal
{exitCode !== null && !isStreaming && ( Exit: {exitCode} )}
{command && output && !isStreaming && (
suna@computer:~$ {command}
{output}
{isSuccess &&
suna@computer:~$ _
}
)} {command && !output && !isStreaming && (
suna@computer:~$ {command}
)} {!command && !output && !isStreaming && (
suna@computer:~$
)} {isStreaming && (
suna@computer:~$ {command || 'running command...'}
Command execution in progress...
)}
{/* Footer */}
{!isStreaming && (
{isSuccess ? ( ) : ( )} {isSuccess ? `Command completed successfully${exitCode !== null ? ` (exit code: ${exitCode})` : ''}` : `Command failed${exitCode !== null ? ` with exit code ${exitCode}` : ''}`}
)} {isStreaming && (
Executing command...
)}
{toolTimestamp && !isStreaming ? formatTimestamp(toolTimestamp) : assistantTimestamp ? formatTimestamp(assistantTimestamp) : ''}
); }