register_rewrite_rules(); add_filter( 'query_vars', array( $this, 'add_query_vars' ) ); add_action( 'template_redirect', array( $this, 'handle_request' ) ); } /** * Register rewrite rules. */ public function register_rewrite_rules() { if ( ! agentshop()->is_connected() ) { return; } add_rewrite_rule( '^llms\.txt$', 'index.php?agentshop_llms_txt=standard', 'top' ); add_rewrite_rule( '^\.well-known/llms\.txt$', 'index.php?agentshop_llms_txt=standard', 'top' ); add_rewrite_rule( '^llms-full\.txt$', 'index.php?agentshop_llms_txt=full', 'top' ); } /** * Add query vars. * * @param array $vars Query vars. * @return array */ public function add_query_vars( $vars ) { $vars[] = 'agentshop_llms_txt'; return $vars; } /** * Handle request. */ public function handle_request() { $version = get_query_var( 'agentshop_llms_txt' ); if ( empty( $version ) ) { return; } if ( ! agentshop()->is_connected() ) { $this->send_error( 503, 'Service Unavailable' ); return; } $content = $this->get_content( $version ); if ( false === $content ) { $this->send_error( 500, 'Error generating llms.txt' ); return; } $this->send_response( $content ); } /** * Get content (cached or fresh). * * @param string $version Version. * @return string|false */ public function get_content( $version = 'standard' ) { $cache_key = self::CACHE_PREFIX . $version; $cached = get_transient( $cache_key ); if ( false !== $cached ) { return $cached; } $content = $this->fetch_content( $version ); if ( false !== $content ) { set_transient( $cache_key, $content, self::CACHE_TTL ); } return $content; } /** * Fetch content from backend. * * @param string $version Version. * @return string|false */ private function fetch_content( $version = 'standard' ) { $result = agentshop()->api->fetch_llms_txt( $version ); if ( is_wp_error( $result ) ) { agentshop()->log( 'llms.txt fetch failed: ' . $result->get_error_message(), 'error' ); return false; } $body = $result['data']['content'] ?? ''; if ( empty( $body ) && isset( $result['data'] ) && is_string( $result['data'] ) ) { $body = $result['data']; } return $body ?: false; } /** * Clear cache. */ public function clear_cache() { delete_transient( self::CACHE_PREFIX . 'standard' ); delete_transient( self::CACHE_PREFIX . 'full' ); } /** * Send response. * * @param string $content Content. */ private function send_response( $content ) { status_header( 200 ); header( 'Content-Type: text/plain; charset=utf-8' ); header( 'Cache-Control: public, max-age=3600' ); header( 'X-Robots-Tag: noindex' ); header( 'X-Content-Type-Options: nosniff' ); echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped exit; } /** * Send error. * * @param int $code Status code. * @param string $message Message. */ private function send_error( $code, $message ) { status_header( $code ); header( 'Content-Type: text/plain; charset=utf-8' ); echo esc_html( $message ); exit; } }