* @author Maximum Software Inc * @license GPL-3.0-only * @link https://agenistrator.com/ * * SPDX-FileCopyrightText: 2025-2026 Alex Ruban * SPDX-FileCopyrightText: 2025-2026 Maximum Software Inc * SPDX-License-Identifier: GPL-3.0-only */ namespace Agenistrator; // Direct access prevention if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Read_Debug_Log_File_Tool class. */ class Read_Debug_Log_File_Tool extends Abstract_File_Reader_Tool { /** * Get the tool ID. * * @return string The tool ID. * @since 1.0.0 */ public function get_id() { return 'read_debug_log_file'; } /** * Get the tool name. * * @return string The tool name. * @since 1.0.0 */ public function get_name() { return __( 'Read Debug Log File', 'agenistrator' ); } /** * Get the tool description. * * @return string The tool description. * @since 1.0.0 */ public function get_description() { return 'Read contents of an error log file.'; } /** * Get additional description instructions for the LLM. * * @return string Additional instructions appended to description. * @since 1.0.0 */ public function get_description_instructions() { return 'Use `get_debug_log_info` first to discover available log files.'; } /** * Get usage instructions for the LLM. * * @return string Instructions for using this tool. * @since 1.0.0 */ public function get_instructions() { return parent::get_instructions() . ' The log_file parameter must be an absolute path from `get_debug_log_info` results.'; } /** * Get the parameters schema for this tool. * * @return array Parameters schema in JSON Schema format. * @since 1.0.0 */ public function get_parameters() { $params = parent::get_parameters(); // Replace 'file' with 'log_file' for clarity unset( $params['properties']['file'] ); $params['properties'] = array_merge( array( 'log_file' => array( 'type' => 'string', 'description' => 'Absolute path to the log file (from get_debug_log_info results).', ), ), $params['properties'] ); $params['required'] = array( 'log_file' ); return $params; } /** * Get the default enabled status for this tool. * * @return bool True if enabled by default, false otherwise. * @since 1.0.0 */ public function get_default_enabled_status() { return true; } /** * Execute the tool with the given arguments. * * @param array $args The tool arguments. * * @return array The tool execution result. * @since 1.0.0 */ protected function execute( $args ) { if ( ! isset( $args['log_file'] ) || empty( trim( $args['log_file'] ) ) ) { return $this->error_response( 'log_file parameter is required' ); } $requested_path = $args['log_file']; // Validate the requested path against discovered log files $discovered_logs = WordPress_Utils::discover_log_files(); if ( empty( $discovered_logs ) ) { return $this->error_response( 'No log files discovered on this system' ); } // Canonicalize paths for security comparison $real_requested = realpath( $requested_path ); if ( false === $real_requested ) { return $this->error_response( 'Log file does not exist or is not accessible' ); } // Build array of canonicalized discovered paths $valid_paths = array(); foreach ( $discovered_logs as $log_path ) { $real_log = realpath( $log_path ); if ( false !== $real_log ) { $valid_paths[] = $real_log; } } if ( ! in_array( $real_requested, $valid_paths, true ) ) { return $this->error_response( 'Access denied: path is not a recognized log file' ); } if ( ! is_readable( $real_requested ) ) { return $this->error_response( 'Log file is not readable' ); } // Map log_file to file for parent class compatibility $args['file'] = $real_requested; try { $data = $this->execute_mode( $real_requested, $args, array( 'log_file' => $real_requested, 'modified' => gmdate( 'Y-m-d H:i:s e', filemtime( $real_requested ) ), ) ); } catch ( \Throwable $e ) { return $this->error_response( $e->getMessage() ); } return $this->success_response( 'Log file read', $data ); } }