|
|
|
|
|
|
|
|
import { |
|
|
FileText, |
|
|
FileCode, |
|
|
FileImage, |
|
|
FileJson, |
|
|
File, |
|
|
FolderOpen, |
|
|
FileType, |
|
|
FileVideo, |
|
|
FileAudio, |
|
|
FileArchive, |
|
|
Table, |
|
|
} from 'lucide-react'; |
|
|
import { parseXmlToolCalls, isNewXmlFormat } from './xml-parser'; |
|
|
import { parseToolResult, ParsedToolResult } from './tool-result-parser'; |
|
|
|
|
|
|
|
|
export function formatTimestamp(isoString?: string): string { |
|
|
if (!isoString) return ''; |
|
|
try { |
|
|
const date = new Date(isoString); |
|
|
return isNaN(date.getTime()) ? 'Invalid date' : date.toLocaleString(); |
|
|
} catch (e) { |
|
|
return 'Invalid date'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function getToolTitle(toolName: string): string { |
|
|
|
|
|
const normalizedName = toolName.toLowerCase(); |
|
|
|
|
|
|
|
|
const toolTitles: Record<string, string> = { |
|
|
'execute-command': 'Execute Command', |
|
|
'check-command-output': 'Check Command Output', |
|
|
'str-replace': 'String Replace', |
|
|
'create-file': 'Create File', |
|
|
'full-file-rewrite': 'Rewrite File', |
|
|
'delete-file': 'Delete File', |
|
|
'web-search': 'Web Search', |
|
|
'crawl-webpage': 'Web Crawl', |
|
|
'scrape-webpage': 'Web Scrape', |
|
|
'browser-navigate': 'Browser Navigate', |
|
|
'browser-click': 'Browser Click', |
|
|
'browser-extract': 'Browser Extract', |
|
|
'browser-fill': 'Browser Fill', |
|
|
'browser-wait': 'Browser Wait', |
|
|
'see-image': 'View Image', |
|
|
'ask': 'Ask', |
|
|
'complete': 'Task Complete', |
|
|
'execute-data-provider-call': 'Data Provider Call', |
|
|
'get-data-provider-endpoints': 'Data Endpoints', |
|
|
'deploy': 'Deploy', |
|
|
|
|
|
'generic-tool': 'Tool', |
|
|
'default': 'Tool', |
|
|
}; |
|
|
|
|
|
|
|
|
if (toolTitles[normalizedName]) { |
|
|
return toolTitles[normalizedName]; |
|
|
} |
|
|
|
|
|
|
|
|
if (normalizedName.startsWith('browser-')) { |
|
|
const operation = normalizedName.replace('browser-', '').replace(/-/g, ' '); |
|
|
return 'Browser ' + operation.charAt(0).toUpperCase() + operation.slice(1); |
|
|
} |
|
|
|
|
|
|
|
|
return toolName |
|
|
.split('-') |
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) |
|
|
.join(' '); |
|
|
} |
|
|
|
|
|
|
|
|
export function extractCommand(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
|
|
|
const commandMatch = contentStr.match( |
|
|
/<execute-command[^>]*>([\s\S]*?)<\/execute-command>/, |
|
|
); |
|
|
if (commandMatch) { |
|
|
return commandMatch[1].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
const parsed = JSON.parse(contentStr); |
|
|
if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) { |
|
|
const execCommand = parsed.tool_calls.find((tc: any) => |
|
|
tc.function?.name === 'execute-command' || |
|
|
tc.function?.name === 'execute_command' |
|
|
); |
|
|
if (execCommand && execCommand.function?.arguments) { |
|
|
try { |
|
|
const args = typeof execCommand.function.arguments === 'string' |
|
|
? JSON.parse(execCommand.function.arguments) |
|
|
: execCommand.function.arguments; |
|
|
if (args.command) return args.command; |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!contentStr.includes('<execute-command') && !contentStr.includes('</execute-command>')) { |
|
|
|
|
|
if (!contentStr.startsWith('{') && !contentStr.startsWith('<')) { |
|
|
|
|
|
if (!contentStr.includes('ToolResult') && !contentStr.includes('No command')) { |
|
|
return contentStr.trim(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
console.log('extractCommand: Could not extract command from content:', contentStr.substring(0, 200)); |
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractSessionName(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
|
|
|
const sessionMatch = contentStr.match( |
|
|
/<check-command-output[^>]*session_name=["']([^"']+)["']/, |
|
|
); |
|
|
if (sessionMatch) { |
|
|
return sessionMatch[1].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
const parsed = JSON.parse(contentStr); |
|
|
if (parsed.tool_calls && Array.isArray(parsed.tool_calls)) { |
|
|
const checkCommand = parsed.tool_calls.find((tc: any) => |
|
|
tc.function?.name === 'check-command-output' || |
|
|
tc.function?.name === 'check_command_output' |
|
|
); |
|
|
if (checkCommand && checkCommand.function?.arguments) { |
|
|
try { |
|
|
const args = typeof checkCommand.function.arguments === 'string' |
|
|
? JSON.parse(checkCommand.function.arguments) |
|
|
: checkCommand.function.arguments; |
|
|
if (args.session_name) return args.session_name; |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const sessionNameMatch = contentStr.match(/session_name=["']([^"']+)["']/); |
|
|
if (sessionNameMatch) { |
|
|
return sessionNameMatch[1].trim(); |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractCommandOutput( |
|
|
content: string | object | undefined | null, |
|
|
): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
|
|
|
|
|
|
if (parsedContent.output && typeof parsedContent.output === 'string') { |
|
|
return parsedContent.output; |
|
|
} |
|
|
|
|
|
if (parsedContent.content && typeof parsedContent.content === 'string') { |
|
|
|
|
|
const toolResultMatch = parsedContent.content.match( |
|
|
/<tool_result>\s*<(?:execute-command|check-command-output)>([\s\S]*?)<\/(?:execute-command|check-command-output)>\s*<\/tool_result>/, |
|
|
); |
|
|
if (toolResultMatch) { |
|
|
return toolResultMatch[1].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
const outputMatch = parsedContent.content.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (outputMatch) { |
|
|
return outputMatch[1]; |
|
|
} |
|
|
|
|
|
|
|
|
return parsedContent.content; |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof parsedContent === 'string') { |
|
|
return parsedContent; |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
const toolResultMatch = contentStr.match( |
|
|
/<tool_result>\s*<(?:execute-command|check-command-output)>([\s\S]*?)<\/(?:execute-command|check-command-output)>\s*<\/tool_result>/, |
|
|
); |
|
|
if (toolResultMatch) { |
|
|
return toolResultMatch[1].trim(); |
|
|
} |
|
|
|
|
|
const outputMatch = contentStr.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (outputMatch) { |
|
|
return outputMatch[1]; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!contentStr.startsWith('<') && !contentStr.includes('ToolResult')) { |
|
|
return contentStr; |
|
|
} |
|
|
} |
|
|
|
|
|
return contentStr; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractExitCode(content: string | object | undefined | null): number | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
const exitCodeMatch = contentStr.match(/exit_code=(\d+)/); |
|
|
if (exitCodeMatch && exitCodeMatch[1]) { |
|
|
return parseInt(exitCodeMatch[1], 10); |
|
|
} |
|
|
return 0; |
|
|
} catch (e) { |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function extractFilePath(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
if (parsedContent.content) { |
|
|
|
|
|
if (isNewXmlFormat(parsedContent.content)) { |
|
|
const toolCalls = parseXmlToolCalls(parsedContent.content); |
|
|
if (toolCalls.length > 0 && toolCalls[0].parameters.file_path) { |
|
|
return cleanFilePath(toolCalls[0].parameters.file_path); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const oldFormatMatch = parsedContent.content.match( |
|
|
/file_path=["']([^"']+)["']/, |
|
|
); |
|
|
if (oldFormatMatch) { |
|
|
return cleanFilePath(oldFormatMatch[1]); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (isNewXmlFormat(contentStr)) { |
|
|
const toolCalls = parseXmlToolCalls(contentStr); |
|
|
if (toolCalls.length > 0 && toolCalls[0].parameters.file_path) { |
|
|
return cleanFilePath(toolCalls[0].parameters.file_path); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const directMatch = contentStr.match(/file_path=["']([^"']+)["']/); |
|
|
if (directMatch) { |
|
|
return cleanFilePath(directMatch[1]); |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof content === 'string' && content.startsWith('"{') && content.endsWith('}"')) { |
|
|
try { |
|
|
|
|
|
const innerString = JSON.parse(content); |
|
|
|
|
|
const parsed = JSON.parse(innerString); |
|
|
if (parsed && typeof parsed === 'object') { |
|
|
if (parsed.file_path) { |
|
|
return cleanFilePath(parsed.file_path); |
|
|
} |
|
|
if (parsed.arguments && parsed.arguments.file_path) { |
|
|
return cleanFilePath(parsed.arguments.file_path); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof content === 'object' && content !== null) { |
|
|
try { |
|
|
|
|
|
if ('content' in content && typeof content.content === 'string') { |
|
|
|
|
|
const xmlFilePathMatch = |
|
|
content.content.match(/<(?:create-file|delete-file|full-file-rewrite|str-replace)[^>]*\s+file_path=["']([\s\S]*?)["']/i) || |
|
|
content.content.match(/<delete[^>]*\s+file_path=["']([\s\S]*?)["']/i) || |
|
|
content.content.match(/<delete-file[^>]*>([^<]+)<\/delete-file>/i) || |
|
|
content.content.match(/<(?:create-file|delete-file|full-file-rewrite)\s+file_path=["']([^"']+)/i); |
|
|
if (xmlFilePathMatch) { |
|
|
return cleanFilePath(xmlFilePathMatch[1]); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ('file_path' in content) { |
|
|
return cleanFilePath(content.file_path as string); |
|
|
} |
|
|
|
|
|
|
|
|
if ('arguments' in content && content.arguments && typeof content.arguments === 'object') { |
|
|
const args = content.arguments as any; |
|
|
if (args.file_path) { |
|
|
return cleanFilePath(args.file_path); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
if (parsedContent.file_path) { |
|
|
return cleanFilePath(parsedContent.file_path); |
|
|
} |
|
|
if (parsedContent.arguments && parsedContent.arguments.file_path) { |
|
|
return cleanFilePath(parsedContent.arguments.file_path); |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const filePathMatch = |
|
|
contentStr.match(/file_path=["']([\s\S]*?)["']/i) || |
|
|
contentStr.match(/target_file=["']([\s\S]*?)["']/i) || |
|
|
contentStr.match(/path=["']([\s\S]*?)["']/i); |
|
|
if (filePathMatch) { |
|
|
const path = filePathMatch[1].trim(); |
|
|
|
|
|
return cleanFilePath(path); |
|
|
} |
|
|
|
|
|
|
|
|
const xmlFilePathMatch = |
|
|
contentStr.match(/<(?:create-file|delete-file|full-file-rewrite|str-replace)[^>]*\s+file_path=["']([\s\S]*?)["']/i) || |
|
|
contentStr.match(/<delete[^>]*\s+file_path=["']([\s\S]*?)["']/i) || |
|
|
contentStr.match(/<delete-file[^>]*>([^<]+)<\/delete-file>/i) || |
|
|
|
|
|
contentStr.match(/<(?:create-file|delete-file|full-file-rewrite)\s+file_path=["']([^"']+)/i); |
|
|
if (xmlFilePathMatch) { |
|
|
return cleanFilePath(xmlFilePathMatch[1]); |
|
|
} |
|
|
|
|
|
|
|
|
if ( |
|
|
contentStr.toLowerCase().includes('delete') || |
|
|
contentStr.includes('delete-file') |
|
|
) { |
|
|
|
|
|
const deletePathMatch = contentStr.match( |
|
|
/(?:delete|remove|deleting)\s+(?:file|the file)?:?\s+["']?([\w\-./\\]+\.\w+)["']?/i, |
|
|
); |
|
|
if (deletePathMatch) return cleanFilePath(deletePathMatch[1]); |
|
|
|
|
|
|
|
|
const fileMatch = contentStr.match(/["']?([\w\-./\\]+\.\w+)["']?/); |
|
|
if (fileMatch) return cleanFilePath(fileMatch[1]); |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
function cleanFilePath(path: string): string { |
|
|
if (!path) return path; |
|
|
|
|
|
|
|
|
return path |
|
|
.replace(/\\n/g, '\n') |
|
|
.replace(/\\t/g, '\t') |
|
|
.replace(/\\r/g, '') |
|
|
.replace(/\\\\/g, '\\') |
|
|
.replace(/\\"/g, '"') |
|
|
.replace(/\\'/g, "'") |
|
|
.split('\n')[0] |
|
|
.trim(); |
|
|
} |
|
|
|
|
|
|
|
|
export function extractStrReplaceContent(content: string | object | undefined | null): { |
|
|
oldStr: string | null; |
|
|
newStr: string | null; |
|
|
} { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return { oldStr: null, newStr: null }; |
|
|
|
|
|
|
|
|
const strReplaceMatch = contentStr.match(/<str-replace[^>]*>([\s\S]*?)<\/str-replace>/); |
|
|
if (strReplaceMatch) { |
|
|
const innerContent = strReplaceMatch[1]; |
|
|
const oldMatch = innerContent.match(/<old_str>([\s\S]*?)<\/old_str>/); |
|
|
const newMatch = innerContent.match(/<new_str>([\s\S]*?)<\/new_str>/); |
|
|
|
|
|
return { |
|
|
oldStr: oldMatch ? oldMatch[1] : null, |
|
|
newStr: newMatch ? newMatch[1] : null, |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
const oldMatch = contentStr.match(/<old_str>([\s\S]*?)<\/old_str>/); |
|
|
const newMatch = contentStr.match(/<new_str>([\s\S]*?)<\/new_str>/); |
|
|
|
|
|
return { |
|
|
oldStr: oldMatch ? oldMatch[1] : null, |
|
|
newStr: newMatch ? newMatch[1] : null, |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractFileContent( |
|
|
content: string | object | undefined | null, |
|
|
toolType: 'create-file' | 'full-file-rewrite', |
|
|
): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
if (parsedContent.content) { |
|
|
|
|
|
if (isNewXmlFormat(parsedContent.content)) { |
|
|
const toolCalls = parseXmlToolCalls(parsedContent.content); |
|
|
if (toolCalls.length > 0 && toolCalls[0].parameters.file_contents) { |
|
|
return processFileContent(toolCalls[0].parameters.file_contents); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const tagName = toolType === 'create-file' ? 'create-file' : 'full-file-rewrite'; |
|
|
const fileContentMatch = parsedContent.content.match( |
|
|
new RegExp(`<${tagName}[^>]*>([\\s\\S]*?)<\\/${tagName}>`, 'i'), |
|
|
); |
|
|
if (fileContentMatch) { |
|
|
return processFileContent(fileContentMatch[1]); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (isNewXmlFormat(contentStr)) { |
|
|
const toolCalls = parseXmlToolCalls(contentStr); |
|
|
if (toolCalls.length > 0 && toolCalls[0].parameters.file_contents) { |
|
|
return processFileContent(toolCalls[0].parameters.file_contents); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const tagName = toolType === 'create-file' ? 'create-file' : 'full-file-rewrite'; |
|
|
const fileContentMatch = contentStr.match( |
|
|
new RegExp(`<${tagName}[^>]*>([\\s\\S]*?)<\\/${tagName}>`, 'i'), |
|
|
); |
|
|
if (fileContentMatch) { |
|
|
return processFileContent(fileContentMatch[1]); |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
function processFileContent(content: string | object): string { |
|
|
if (!content) return ''; |
|
|
if (typeof content === 'object') { |
|
|
return JSON.stringify(content, null, 2); |
|
|
} |
|
|
|
|
|
const trimmedContent = content.trim(); |
|
|
const isLikelyJson = (trimmedContent.startsWith('{') && trimmedContent.endsWith('}')) || |
|
|
(trimmedContent.startsWith('[') && trimmedContent.endsWith(']')); |
|
|
|
|
|
if (isLikelyJson) { |
|
|
try { |
|
|
const parsed = JSON.parse(content); |
|
|
return JSON.stringify(parsed, null, 2); |
|
|
} catch (e) { |
|
|
} |
|
|
} |
|
|
return content |
|
|
.replace(/\\n/g, '\n') |
|
|
.replace(/\\t/g, '\t') |
|
|
.replace(/\\r/g, '') |
|
|
.replace(/\\\\/g, '\\') |
|
|
.replace(/\\"/g, '"') |
|
|
.replace(/\\'/g, "'"); |
|
|
} |
|
|
|
|
|
|
|
|
export function getFileType(filePath: string): string { |
|
|
const extension = filePath.split('.').pop()?.toLowerCase() || ''; |
|
|
|
|
|
switch (extension) { |
|
|
case 'js': |
|
|
return 'JavaScript'; |
|
|
case 'ts': |
|
|
return 'TypeScript'; |
|
|
case 'jsx': |
|
|
case 'tsx': |
|
|
return 'React'; |
|
|
case 'py': |
|
|
return 'Python'; |
|
|
case 'html': |
|
|
return 'HTML'; |
|
|
case 'css': |
|
|
return 'CSS'; |
|
|
case 'json': |
|
|
return 'JSON'; |
|
|
case 'md': |
|
|
return 'Markdown'; |
|
|
default: |
|
|
return extension.toUpperCase() || 'Text'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function extractBrowserUrl(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
const urlMatch = contentStr.match(/url=["'](https?:\/\/[^"']+)["']/); |
|
|
return urlMatch ? urlMatch[1] : null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractBrowserOperation(toolName: string | undefined): string { |
|
|
if (!toolName) return 'Browser Operation'; |
|
|
|
|
|
const operation = toolName.replace('browser-', '').replace(/-/g, ' '); |
|
|
return operation.charAt(0).toUpperCase() + operation.slice(1); |
|
|
} |
|
|
|
|
|
|
|
|
export function extractSearchQuery(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
|
|
|
const toolResultMatch = contentStr.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
|
|
|
if (toolResultMatch) { |
|
|
try { |
|
|
|
|
|
const outputJson = JSON.parse(toolResultMatch[1]); |
|
|
|
|
|
|
|
|
if (outputJson.query && typeof outputJson.query === 'string') { |
|
|
return outputJson.query; |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
let contentToSearch = contentStr; |
|
|
|
|
|
|
|
|
try { |
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
|
|
|
|
|
|
if (parsedContent.query && typeof parsedContent.query === 'string') { |
|
|
return parsedContent.query; |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof parsedContent.content === 'string') { |
|
|
|
|
|
|
|
|
contentToSearch = parsedContent.content; |
|
|
|
|
|
|
|
|
if (typeof parsedContent.query === 'string') { |
|
|
return parsedContent.query; |
|
|
} |
|
|
if ( |
|
|
typeof parsedContent.arguments === 'object' && |
|
|
parsedContent.arguments !== null && |
|
|
typeof parsedContent.arguments.query === 'string' |
|
|
) { |
|
|
return parsedContent.arguments.query; |
|
|
} |
|
|
if ( |
|
|
Array.isArray(parsedContent.tool_calls) && |
|
|
parsedContent.tool_calls.length > 0 |
|
|
) { |
|
|
const toolCall = parsedContent.tool_calls[0]; |
|
|
if ( |
|
|
typeof toolCall.arguments === 'object' && |
|
|
toolCall.arguments !== null && |
|
|
typeof toolCall.arguments.query === 'string' |
|
|
) { |
|
|
return toolCall.arguments.query; |
|
|
} |
|
|
if (typeof toolCall.arguments === 'string') { |
|
|
try { |
|
|
const argsParsed = JSON.parse(toolCall.arguments); |
|
|
if (typeof argsParsed.query === 'string') { |
|
|
return argsParsed.query; |
|
|
} |
|
|
} catch {} |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const xmlQueryMatch = contentToSearch.match( |
|
|
/<web-search[^>]*\s+query=[\"']([^\"']*)["'][^>]*>/i, |
|
|
); |
|
|
if (xmlQueryMatch && xmlQueryMatch[1]) { |
|
|
return xmlQueryMatch[1].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
const simpleAttrMatch = contentToSearch.match(/query=[\"']([\s\S]*?)["']/i); |
|
|
if (simpleAttrMatch && simpleAttrMatch[1]) { |
|
|
return simpleAttrMatch[1].split(/[\"']/)[0].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractUrlsAndTitles( |
|
|
content: string, |
|
|
): Array<{ title: string; url: string; snippet?: string }> { |
|
|
const results: Array<{ title: string; url: string; snippet?: string }> = []; |
|
|
|
|
|
|
|
|
try { |
|
|
const parsed = JSON.parse(content); |
|
|
if (Array.isArray(parsed)) { |
|
|
return parsed.map(result => ({ |
|
|
title: result.title || '', |
|
|
url: result.url || '', |
|
|
snippet: result.content || result.snippet || '', |
|
|
})); |
|
|
} |
|
|
if (parsed.results && Array.isArray(parsed.results)) { |
|
|
return parsed.results.map((result: any) => ({ |
|
|
title: result.title || '', |
|
|
url: result.url || '', |
|
|
snippet: result.content || '', |
|
|
})); |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const jsonObjectPattern = /\{\s*"title"\s*:\s*"([^"]+)"\s*,\s*"url"\s*:\s*"(https?:\/\/[^"]+)"\s*(?:,\s*"content"\s*:\s*"([^"]*)")?\s*\}/g; |
|
|
let objectMatch; |
|
|
|
|
|
while ((objectMatch = jsonObjectPattern.exec(content)) !== null) { |
|
|
const title = objectMatch[1]; |
|
|
const url = objectMatch[2]; |
|
|
const snippet = objectMatch[3] || ''; |
|
|
|
|
|
if (url && title && !results.some((r) => r.url === url)) { |
|
|
results.push({ title, url, snippet }); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (results.length === 0) { |
|
|
|
|
|
const urlRegex = /https?:\/\/[^\s"<]+/g; |
|
|
let match; |
|
|
|
|
|
while ((match = urlRegex.exec(content)) !== null) { |
|
|
let url = match[0]; |
|
|
|
|
|
|
|
|
|
|
|
const protocolEndIndex = url.indexOf('://'); |
|
|
const searchStartIndex = |
|
|
protocolEndIndex !== -1 ? protocolEndIndex + 3 : 0; |
|
|
|
|
|
const newlineIndexN = url.indexOf('/n', searchStartIndex); |
|
|
const newlineIndexSlashN = url.indexOf('\\n', searchStartIndex); |
|
|
|
|
|
let firstNewlineIndex = -1; |
|
|
if (newlineIndexN !== -1 && newlineIndexSlashN !== -1) { |
|
|
firstNewlineIndex = Math.min(newlineIndexN, newlineIndexSlashN); |
|
|
} else if (newlineIndexN !== -1) { |
|
|
firstNewlineIndex = newlineIndexN; |
|
|
} else if (newlineIndexSlashN !== -1) { |
|
|
firstNewlineIndex = newlineIndexSlashN; |
|
|
} |
|
|
|
|
|
|
|
|
if (firstNewlineIndex !== -1) { |
|
|
url = url.substring(0, firstNewlineIndex); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
url = url |
|
|
.replace(/<\/?url>$/, '') |
|
|
.replace(/<\/?content>$/, '') |
|
|
.replace(/%3C$/, ''); |
|
|
|
|
|
|
|
|
|
|
|
while (/[);.,\/]$/.test(url)) { |
|
|
url = url.slice(0, -1); |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
url = decodeURIComponent(decodeURIComponent(url)); |
|
|
} catch (e) { |
|
|
try { |
|
|
|
|
|
url = decodeURIComponent(url); |
|
|
} catch (e2) { |
|
|
console.warn('Failed to decode URL component:', url, e2); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
url = url.replace(/\u2026$/, ''); |
|
|
url = url.replace(/<\/?url>$/, '').replace(/<\/?content>$/, ''); |
|
|
|
|
|
|
|
|
const urlIndex = match.index; |
|
|
const surroundingText = content.substring( |
|
|
Math.max(0, urlIndex - 100), |
|
|
urlIndex + url.length + 200, |
|
|
); |
|
|
|
|
|
|
|
|
const titleMatch = |
|
|
surroundingText.match(/title"?\s*:\s*"([^"]+)"/i) || |
|
|
surroundingText.match(/Title[:\s]+([^\n<]+)/i) || |
|
|
surroundingText.match(/\"(.*?)\"[\s\n]*?https?:\/\//); |
|
|
|
|
|
let title = cleanUrl(url); |
|
|
if (titleMatch && titleMatch[1].trim()) { |
|
|
title = titleMatch[1].trim(); |
|
|
} |
|
|
|
|
|
|
|
|
if (url && !results.some((r) => r.url === url)) { |
|
|
results.push({ |
|
|
title: title, |
|
|
url: url, |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return results; |
|
|
} |
|
|
|
|
|
|
|
|
export function cleanUrl(url: string): string { |
|
|
try { |
|
|
const urlObj = new URL(url); |
|
|
return ( |
|
|
urlObj.hostname.replace('www.', '') + |
|
|
(urlObj.pathname !== '/' ? urlObj.pathname : '') |
|
|
); |
|
|
} catch (e) { |
|
|
return url; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function extractCrawlUrl(content: string | object | undefined | null): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
if (parsedContent.content) { |
|
|
|
|
|
const urlMatch = parsedContent.content.match( |
|
|
/<(?:crawl|scrape)-webpage[^>]*\s+url=["'](https?:\/\/[^"']+)["']/i, |
|
|
); |
|
|
if (urlMatch) return urlMatch[1]; |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const urlMatch = |
|
|
contentStr.match( |
|
|
/<(?:crawl|scrape)-webpage[^>]*\s+url=["'](https?:\/\/[^"']+)["']/i, |
|
|
) || contentStr.match(/url=["'](https?:\/\/[^"']+)["']/i); |
|
|
|
|
|
return urlMatch ? urlMatch[1] : null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractWebpageContent( |
|
|
content: string | object | undefined | null, |
|
|
): { title: string; text: string } | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
try { |
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
|
|
|
|
|
|
if (parsedContent.content && typeof parsedContent.content === 'string') { |
|
|
|
|
|
const toolResultMatch = parsedContent.content.match( |
|
|
/<tool_result[^>]*>\s*<(?:crawl|scrape)-webpage[^>]*>([\s\S]*?)<\/(?:crawl|scrape)-webpage>\s*<\/tool_result>/, |
|
|
); |
|
|
if (toolResultMatch) { |
|
|
try { |
|
|
|
|
|
const rawData = toolResultMatch[1]; |
|
|
|
|
|
|
|
|
const toolResultOutputMatch = rawData.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (toolResultOutputMatch) { |
|
|
try { |
|
|
|
|
|
const outputJson = JSON.parse( |
|
|
toolResultOutputMatch[1] |
|
|
.replace(/\\\\n/g, '\\n') |
|
|
.replace(/\\\\u/g, '\\u'), |
|
|
); |
|
|
|
|
|
|
|
|
if (Array.isArray(outputJson) && outputJson.length > 0) { |
|
|
const item = outputJson[0]; |
|
|
return { |
|
|
title: item.Title || item.title || '', |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: outputJson.Title || outputJson.title || '', |
|
|
text: |
|
|
outputJson.Text || |
|
|
outputJson.text || |
|
|
outputJson.content || |
|
|
'', |
|
|
}; |
|
|
} catch (e) { |
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: toolResultOutputMatch[1], |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const crawlData = JSON.parse(rawData); |
|
|
|
|
|
|
|
|
if (Array.isArray(crawlData) && crawlData.length > 0) { |
|
|
const item = crawlData[0]; |
|
|
return { |
|
|
title: item.Title || item.title || '', |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: crawlData.Title || crawlData.title || '', |
|
|
text: crawlData.Text || crawlData.text || crawlData.content || '', |
|
|
}; |
|
|
} catch (e) { |
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: toolResultMatch[1], |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const toolResultOutputMatch = parsedContent.content.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (toolResultOutputMatch) { |
|
|
try { |
|
|
|
|
|
const outputJson = JSON.parse( |
|
|
toolResultOutputMatch[1] |
|
|
.replace(/\\\\n/g, '\\n') |
|
|
.replace(/\\\\u/g, '\\u'), |
|
|
); |
|
|
|
|
|
|
|
|
if (Array.isArray(outputJson) && outputJson.length > 0) { |
|
|
const item = outputJson[0]; |
|
|
return { |
|
|
title: item.Title || item.title || '', |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: outputJson.Title || outputJson.title || '', |
|
|
text: |
|
|
outputJson.Text || outputJson.text || outputJson.content || '', |
|
|
}; |
|
|
} catch (e) { |
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: toolResultOutputMatch[1], |
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const webpageMatch = contentStr.match( |
|
|
/<(?:crawl|scrape)-webpage[^>]*>([\s\S]*?)<\/(?:crawl|scrape)-webpage>/, |
|
|
); |
|
|
if (webpageMatch) { |
|
|
const rawData = webpageMatch[1]; |
|
|
|
|
|
|
|
|
const toolResultOutputMatch = rawData.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (toolResultOutputMatch) { |
|
|
try { |
|
|
|
|
|
const outputString = toolResultOutputMatch[1] |
|
|
.replace(/\\\\n/g, '\\n') |
|
|
.replace(/\\\\u/g, '\\u'); |
|
|
const outputJson = JSON.parse(outputString); |
|
|
|
|
|
|
|
|
if (Array.isArray(outputJson) && outputJson.length > 0) { |
|
|
const item = outputJson[0]; |
|
|
return { |
|
|
title: |
|
|
item.Title || |
|
|
item.title || |
|
|
(item.URL ? new URL(item.URL).hostname : ''), |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: outputJson.Title || outputJson.title || '', |
|
|
text: |
|
|
outputJson.Text || outputJson.text || outputJson.content || '', |
|
|
}; |
|
|
} catch (e) { |
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: toolResultOutputMatch[1], |
|
|
}; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (Array.isArray(parsedContent) && parsedContent.length > 0) { |
|
|
const item = parsedContent[0]; |
|
|
return { |
|
|
title: item.Title || item.title || '', |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
if (typeof parsedContent === 'object' && parsedContent !== null) { |
|
|
|
|
|
if ('Title' in parsedContent || 'title' in parsedContent || 'Text' in parsedContent || 'text' in parsedContent) { |
|
|
return { |
|
|
title: parsedContent.Title || parsedContent.title || 'Webpage Content', |
|
|
text: |
|
|
parsedContent.Text || |
|
|
parsedContent.text || |
|
|
parsedContent.content || |
|
|
'', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: JSON.stringify(parsedContent), |
|
|
}; |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
const toolResultMatch = contentStr.match( |
|
|
/ToolResult\(.*?output='([\s\S]*?)'.*?\)/, |
|
|
); |
|
|
if (toolResultMatch) { |
|
|
try { |
|
|
|
|
|
const outputJson = JSON.parse( |
|
|
toolResultMatch[1].replace(/\\\\n/g, '\\n').replace(/\\\\u/g, '\\u'), |
|
|
); |
|
|
|
|
|
|
|
|
if (Array.isArray(outputJson) && outputJson.length > 0) { |
|
|
const item = outputJson[0]; |
|
|
return { |
|
|
title: item.Title || item.title || '', |
|
|
text: item.Text || item.text || item.content || '', |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
title: outputJson.Title || outputJson.title || '', |
|
|
text: outputJson.Text || outputJson.text || outputJson.content || '', |
|
|
}; |
|
|
} catch (e) { |
|
|
|
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: toolResultMatch[1], |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (contentStr) { |
|
|
return { |
|
|
title: 'Webpage Content', |
|
|
text: contentStr, |
|
|
}; |
|
|
} |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractSearchResults( |
|
|
content: string | object | undefined | null, |
|
|
): Array<{ title: string; url: string; snippet?: string }> { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return []; |
|
|
|
|
|
try { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const resultsPattern = /"results":\s*\[([^\]]*(?:\[[^\]]*\][^\]]*)*)\]/; |
|
|
const resultsMatch = contentStr.match(resultsPattern); |
|
|
|
|
|
if (resultsMatch) { |
|
|
try { |
|
|
|
|
|
const resultsArrayStr = '[' + resultsMatch[1] + ']'; |
|
|
const results = JSON.parse(resultsArrayStr); |
|
|
|
|
|
if (Array.isArray(results)) { |
|
|
return results.map(result => ({ |
|
|
title: result.title || '', |
|
|
url: result.url || '', |
|
|
snippet: result.content || '', |
|
|
})); |
|
|
} |
|
|
} catch (e) { |
|
|
console.warn('Failed to parse results array:', e); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const resultObjectPattern = /\{\s*"url":\s*"([^"]+)"\s*,\s*"title":\s*"([^"]+)"\s*,\s*"content":\s*"([^"]*)"[^}]*\}/g; |
|
|
const results = []; |
|
|
let match; |
|
|
|
|
|
while ((match = resultObjectPattern.exec(contentStr)) !== null) { |
|
|
results.push({ |
|
|
url: match[1], |
|
|
title: match[2], |
|
|
snippet: match[3], |
|
|
}); |
|
|
} |
|
|
|
|
|
if (results.length > 0) { |
|
|
return results; |
|
|
} |
|
|
|
|
|
|
|
|
const parsedContent = JSON.parse(contentStr); |
|
|
|
|
|
|
|
|
if (parsedContent.results && Array.isArray(parsedContent.results)) { |
|
|
return parsedContent.results.map((result: any) => ({ |
|
|
title: result.title || '', |
|
|
url: result.url || '', |
|
|
snippet: result.content || '', |
|
|
})); |
|
|
} |
|
|
|
|
|
|
|
|
if (parsedContent.content && typeof parsedContent.content === 'string') { |
|
|
|
|
|
const toolResultTagMatch = parsedContent.content.match( |
|
|
/<tool_result[^>]*>\s*<web-search[^>]*>([\s\S]*?)<\/web-search>\s*<\/tool_result>/, |
|
|
); |
|
|
if (toolResultTagMatch) { |
|
|
|
|
|
try { |
|
|
return JSON.parse(toolResultTagMatch[1]); |
|
|
} catch (e) { |
|
|
|
|
|
return extractUrlsAndTitles(toolResultTagMatch[1]); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const jsonArrayMatch = parsedContent.content.match(/\[\s*{[\s\S]*}\s*\]/); |
|
|
if (jsonArrayMatch) { |
|
|
try { |
|
|
return JSON.parse(jsonArrayMatch[0]); |
|
|
} catch (e) { |
|
|
return extractUrlsAndTitles(parsedContent.content); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return extractUrlsAndTitles(parsedContent.content); |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
return extractUrlsAndTitles(contentStr); |
|
|
} |
|
|
|
|
|
|
|
|
return extractUrlsAndTitles(contentStr); |
|
|
} |
|
|
|
|
|
|
|
|
export function getToolComponent(toolName: string): string { |
|
|
if (!toolName) return 'GenericToolView'; |
|
|
|
|
|
const normalizedName = toolName.toLowerCase(); |
|
|
|
|
|
|
|
|
switch (normalizedName) { |
|
|
|
|
|
case 'browser-navigate': |
|
|
case 'browser-click': |
|
|
case 'browser-extract': |
|
|
case 'browser-fill': |
|
|
case 'browser-wait': |
|
|
case 'browser-screenshot': |
|
|
return 'BrowserToolView'; |
|
|
|
|
|
|
|
|
case 'execute-command': |
|
|
return 'CommandToolView'; |
|
|
|
|
|
|
|
|
case 'create-file': |
|
|
case 'delete-file': |
|
|
case 'full-file-rewrite': |
|
|
case 'read-file': |
|
|
return 'FileOperationToolView'; |
|
|
|
|
|
|
|
|
case 'str-replace': |
|
|
return 'StrReplaceToolView'; |
|
|
|
|
|
|
|
|
case 'web-search': |
|
|
return 'WebSearchToolView'; |
|
|
case 'crawl-webpage': |
|
|
return 'WebCrawlToolView'; |
|
|
case 'scrape-webpage': |
|
|
return 'WebScrapeToolView'; |
|
|
|
|
|
|
|
|
case 'execute-data-provider-call': |
|
|
case 'get-data-provider-endpoints': |
|
|
return 'DataProviderToolView'; |
|
|
|
|
|
|
|
|
|
|
|
case 'deploy': |
|
|
return 'DeployToolView'; |
|
|
|
|
|
|
|
|
default: |
|
|
return 'GenericToolView'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export function normalizeContentToString(content: string | object | undefined | null): string | null { |
|
|
if (!content) return null; |
|
|
|
|
|
if (typeof content === 'string') { |
|
|
|
|
|
if (content.startsWith('"{') && content.endsWith('}"')) { |
|
|
try { |
|
|
|
|
|
const innerString = JSON.parse(content); |
|
|
|
|
|
const parsed = JSON.parse(innerString); |
|
|
|
|
|
if (parsed && typeof parsed === 'object' && 'content' in parsed) { |
|
|
return parsed.content; |
|
|
} |
|
|
|
|
|
return JSON.stringify(parsed); |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
return content; |
|
|
} |
|
|
|
|
|
if (typeof content === 'object' && content !== null) { |
|
|
try { |
|
|
|
|
|
if ('content' in content && typeof content.content === 'string') { |
|
|
return content.content; |
|
|
} |
|
|
|
|
|
else if ('content' in content && typeof content.content === 'object' && content.content !== null) { |
|
|
|
|
|
if ('content' in content.content && typeof content.content.content === 'string') { |
|
|
return content.content.content; |
|
|
} |
|
|
|
|
|
return JSON.stringify(content.content); |
|
|
} |
|
|
|
|
|
else if ('role' in content && 'content' in content && typeof content.content === 'string') { |
|
|
return content.content; |
|
|
} |
|
|
|
|
|
else if ('role' in content && 'content' in content && typeof content.content === 'object' && content.content !== null) { |
|
|
if ('content' in content.content && typeof content.content.content === 'string') { |
|
|
return content.content.content; |
|
|
} |
|
|
|
|
|
return JSON.stringify(content.content); |
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
const stringified = JSON.stringify(content); |
|
|
|
|
|
if (stringified.includes('<') || stringified.includes('file_path') || stringified.includes('command')) { |
|
|
return stringified; |
|
|
} |
|
|
|
|
|
return stringified; |
|
|
} |
|
|
} catch (e) { |
|
|
console.error('Error in normalizeContentToString:', e, 'Content:', content); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
|
|
|
export function extractStreamingFileContent( |
|
|
content: string | object | undefined | null, |
|
|
toolType: 'create-file' | 'full-file-rewrite', |
|
|
): string | null { |
|
|
const contentStr = normalizeContentToString(content); |
|
|
if (!contentStr) return null; |
|
|
|
|
|
const tagName = toolType === 'create-file' ? 'create-file' : 'full-file-rewrite'; |
|
|
|
|
|
|
|
|
if (typeof content === 'object' && content !== null) { |
|
|
try { |
|
|
if ('content' in content && typeof content.content === 'string') { |
|
|
|
|
|
const openTagMatch = content.content.match(new RegExp(`<${tagName}[^>]*>`, 'i')); |
|
|
if (openTagMatch) { |
|
|
|
|
|
const tagEndIndex = content.content.indexOf(openTagMatch[0]) + openTagMatch[0].length; |
|
|
|
|
|
const afterTag = content.content.substring(tagEndIndex); |
|
|
|
|
|
|
|
|
const closeTagMatch = afterTag.match(new RegExp(`<\\/${tagName}>`, 'i')); |
|
|
if (closeTagMatch) { |
|
|
|
|
|
return processFileContent(afterTag.substring(0, closeTagMatch.index)); |
|
|
} else { |
|
|
|
|
|
return processFileContent(afterTag); |
|
|
} |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const openTagMatch = contentStr.match(new RegExp(`<${tagName}[^>]*>`, 'i')); |
|
|
if (openTagMatch) { |
|
|
|
|
|
const tagEndIndex = contentStr.indexOf(openTagMatch[0]) + openTagMatch[0].length; |
|
|
|
|
|
const afterTag = contentStr.substring(tagEndIndex); |
|
|
|
|
|
|
|
|
const closeTagMatch = afterTag.match(new RegExp(`<\\/${tagName}>`, 'i')); |
|
|
if (closeTagMatch) { |
|
|
|
|
|
return processFileContent(afterTag.substring(0, closeTagMatch.index)); |
|
|
} else { |
|
|
|
|
|
return processFileContent(afterTag); |
|
|
} |
|
|
} |
|
|
|
|
|
return null; |
|
|
} |
|
|
|
|
|
export const getFileIconAndColor = (filename: string) => { |
|
|
const ext = filename.split('.').pop()?.toLowerCase(); |
|
|
|
|
|
switch (ext) { |
|
|
case 'js': |
|
|
case 'jsx': |
|
|
case 'ts': |
|
|
case 'tsx': |
|
|
return { |
|
|
icon: FileCode, |
|
|
color: 'text-yellow-500 dark:text-yellow-400', |
|
|
bgColor: 'bg-gradient-to-br from-yellow-500/20 to-yellow-600/10 border border-yellow-500/20' |
|
|
}; |
|
|
case 'py': |
|
|
return { |
|
|
icon: FileCode, |
|
|
color: 'text-blue-500 dark:text-blue-400', |
|
|
bgColor: 'bg-gradient-to-br from-blue-500/20 to-blue-600/10 border border-blue-500/20' |
|
|
}; |
|
|
case 'html': |
|
|
case 'css': |
|
|
case 'scss': |
|
|
return { |
|
|
icon: FileCode, |
|
|
color: 'text-orange-500 dark:text-orange-400', |
|
|
bgColor: 'bg-gradient-to-br from-orange-500/20 to-orange-600/10 border border-orange-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
case 'json': |
|
|
return { |
|
|
icon: FileJson, |
|
|
color: 'text-green-500 dark:text-green-400', |
|
|
bgColor: 'bg-gradient-to-br from-green-500/20 to-green-600/10 border border-green-500/20' |
|
|
}; |
|
|
case 'csv': |
|
|
return { |
|
|
icon: Table, |
|
|
color: 'text-emerald-500 dark:text-emerald-400', |
|
|
bgColor: 'bg-gradient-to-br from-emerald-500/20 to-emerald-600/10 border border-emerald-500/20' |
|
|
}; |
|
|
case 'xml': |
|
|
case 'yaml': |
|
|
case 'yml': |
|
|
return { |
|
|
icon: FileCode, |
|
|
color: 'text-purple-500 dark:text-purple-400', |
|
|
bgColor: 'bg-gradient-to-br from-purple-500/20 to-purple-600/10 border border-purple-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
case 'jpg': |
|
|
case 'jpeg': |
|
|
case 'png': |
|
|
case 'gif': |
|
|
case 'svg': |
|
|
case 'webp': |
|
|
return { |
|
|
icon: FileImage, |
|
|
color: 'text-pink-500 dark:text-pink-400', |
|
|
bgColor: 'bg-gradient-to-br from-pink-500/20 to-pink-600/10 border border-pink-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
case 'md': |
|
|
case 'mdx': |
|
|
return { |
|
|
icon: FileText, |
|
|
color: 'text-slate-500 dark:text-slate-400', |
|
|
bgColor: 'bg-gradient-to-br from-slate-500/20 to-slate-600/10 border border-slate-500/20' |
|
|
}; |
|
|
case 'txt': |
|
|
return { |
|
|
icon: FileText, |
|
|
color: 'text-zinc-500 dark:text-zinc-400', |
|
|
bgColor: 'bg-gradient-to-br from-zinc-500/20 to-zinc-600/10 border border-zinc-500/20' |
|
|
}; |
|
|
case 'pdf': |
|
|
return { |
|
|
icon: FileType, |
|
|
color: 'text-red-500 dark:text-red-400', |
|
|
bgColor: 'bg-gradient-to-br from-red-500/20 to-red-600/10 border border-red-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
case 'mp4': |
|
|
case 'avi': |
|
|
case 'mov': |
|
|
return { |
|
|
icon: FileVideo, |
|
|
color: 'text-indigo-500 dark:text-indigo-400', |
|
|
bgColor: 'bg-gradient-to-br from-indigo-500/20 to-indigo-600/10 border border-indigo-500/20' |
|
|
}; |
|
|
case 'mp3': |
|
|
case 'wav': |
|
|
case 'ogg': |
|
|
return { |
|
|
icon: FileAudio, |
|
|
color: 'text-teal-500 dark:text-teal-400', |
|
|
bgColor: 'bg-gradient-to-br from-teal-500/20 to-teal-600/10 border border-teal-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
case 'zip': |
|
|
case 'tar': |
|
|
case 'gz': |
|
|
case 'rar': |
|
|
return { |
|
|
icon: FileArchive, |
|
|
color: 'text-amber-500 dark:text-amber-400', |
|
|
bgColor: 'bg-gradient-to-br from-amber-500/20 to-amber-600/10 border border-amber-500/20' |
|
|
}; |
|
|
|
|
|
|
|
|
default: |
|
|
if (!ext || filename.includes('/')) { |
|
|
return { |
|
|
icon: FolderOpen, |
|
|
color: 'text-blue-500 dark:text-blue-400', |
|
|
bgColor: 'bg-gradient-to-br from-blue-500/20 to-blue-600/10 border border-blue-500/20' |
|
|
}; |
|
|
} |
|
|
return { |
|
|
icon: File, |
|
|
color: 'text-zinc-500 dark:text-zinc-400', |
|
|
bgColor: 'bg-gradient-to-br from-zinc-500/20 to-zinc-600/10 border border-zinc-500/20' |
|
|
}; |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function extractToolData(content: any): { |
|
|
toolResult: ParsedToolResult | null; |
|
|
arguments: Record<string, any>; |
|
|
filePath: string | null; |
|
|
fileContent: string | null; |
|
|
command: string | null; |
|
|
url: string | null; |
|
|
query: string | null; |
|
|
} { |
|
|
const toolResult = parseToolResult(content); |
|
|
|
|
|
if (toolResult) { |
|
|
const args = toolResult.arguments || {}; |
|
|
return { |
|
|
toolResult, |
|
|
arguments: args, |
|
|
filePath: args.file_path || args.path || null, |
|
|
fileContent: args.file_contents || args.content || null, |
|
|
command: args.command || null, |
|
|
url: args.url || null, |
|
|
query: args.query || null, |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
return { |
|
|
toolResult: null, |
|
|
arguments: {}, |
|
|
filePath: null, |
|
|
fileContent: null, |
|
|
command: null, |
|
|
url: null, |
|
|
query: null, |
|
|
}; |
|
|
} |