PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / full-site-import / Abilities / AgentCapabilities.php

AgentCapabilities.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/full-site-import/Abilities/AgentCapabilities.php

109 lines 4.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Full Site Import's agent-facing capabilities (spec 045).
4 *
5 * Five capabilities — inspect a pack, start an import, advance/poll it, retry it,
6 * revert it — contributed to the shared registry in `modules/mcp-core/`. Whatever
7 * transport is serving (the built-in server in `modules/mcp-server/`, the
8 * WordPress Abilities API, the mcp-adapter plugin) picks them up from there; none
9 * of them is named here and none of them needs to know FSI exists.
10 *
11 * ## Why these live inside full-site-import and not in an MCP module
12 *
13 * They are FSI code. They import `Utils\SessionData`, drive the frozen
14 * `wp_ajax_templately_pack_*` action contract, parse FSI's own SSE event stream
15 * (`Support\FsiStatusNormalizer`), and hook `templately_fsi_before_import`. A
16 * change to any of that breaks them, so they belong in the same module and the
17 * same review as the thing they wrap.
18 *
19 * That is the general rule, not an exception carved for FSI: **a feature module
20 * owns its agent capabilities whenever it is genuinely coupled to them.**
21 * `modules/mcp-abilities/` keeps the nine template-library capabilities precisely
22 * because they are NOT coupled — they reach their features by route string
23 * through `McpCore\Support\RestDispatcher` and import nothing but
24 * `Templately\Utils\*`, so they are a standalone pack with no owning feature to
25 * live in.
26 *
27 * @package Templately\Modules\FullSiteImport\Abilities
28 */
29
30 namespace Templately\Modules\FullSiteImport\Abilities;
31
32 use Templately\Modules\McpCore\Registry\ToolRegistry;
33
34 class AgentCapabilities {
35
36 /**
37 * The ONE list. Adding an FSI capability means adding its class here and
38 * nothing else (FR-011).
39 *
40 * @var string[]
41 */
42 const ABILITY_CLASSES = [
43 InspectFullSitePackAbility::class,
44 StartFullSiteImportAbility::class,
45 FullSiteImportStatusAbility::class,
46 RetryFullSiteImportAbility::class,
47 RevertFullSiteImportAbility::class,
48 ];
49
50 /**
51 * PHP time limit (seconds) imposed on an FSI import slice driven through the
52 * MCP admin-ajax loopback. Chosen so the importer's own
53 * `Helper::fsi_should_exit()` yields the runner slice (at ~max_time minus its
54 * built-in 20% delay, i.e. ~32s here) comfortably under common 60s gateway/
55 * proxy read timeouts, keeping every `full-site-import-status` poll responsive.
56 */
57 const LOOPBACK_TIME_LIMIT = 40;
58
59 public static function register(): void {
60 // Hooked, not called directly, so the registry can rebuild itself from
61 // every contributor at any time — see ToolRegistry::COLLECT_ACTION.
62 add_action( ToolRegistry::COLLECT_ACTION, [ self::class, 'declare_capabilities' ] );
63
64 // Bound the slice duration for imports driven through the loopback so each
65 // slice returns before an upstream gateway/proxy timeout. Done entirely
66 // through the importer's EXISTING `templately_fsi_before_import` action —
67 // no runner code is modified. The browser flow carries no marker and is
68 // untouched.
69 add_action( 'templately_fsi_before_import', [ self::class, 'cap_loopback_slice_time' ] );
70 }
71
72 /**
73 * Hands over CLASS NAMES only — descriptors resolve lazily on first use.
74 * Resolving them eagerly would call each descriptor's `__()` during
75 * `plugins_loaded`, i.e. before `init` loads the textdomain, which makes
76 * WP 6.7+ emit a "textdomain triggered too early" notice. Under
77 * WP_DEBUG_DISPLAY that notice is echoed during bootstrap and every later
78 * REST error status collapses to 200, because headers are already sent.
79 * See ToolRegistry::$pending_classes.
80 *
81 * @param ToolRegistry $registry
82 * @return void
83 */
84 public static function declare_capabilities( $registry ): void {
85 $registry->register_classes( self::ABILITY_CLASSES );
86 }
87
88 /**
89 * For an FSI import driven by the loopback (marked by
90 * `McpCore\Support\AjaxLoopbackDispatcher` via `templately_mcp_driven`),
91 * impose a finite PHP time limit.
92 *
93 * FSI runs under `set_time_limit(0)`, which makes `Helper::fsi_should_exit()`'s
94 * max_execution_time check a no-op; restoring a finite limit here re-enables
95 * that existing per-item time-box so a heavy runner splits into short slices.
96 * Hooked after the importer's own `set_time_limit(0)` and before the runner
97 * loop.
98 *
99 * @return void
100 */
101 public static function cap_loopback_slice_time(): void {
102 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- the underlying ajax handler already verifies the nonce; this only reads a boolean routing flag.
103 if ( ! empty( $_REQUEST['templately_mcp_driven'] ) ) {
104 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- set_time_limit may be disabled on some hosts; a warning there is harmless and the cap is best-effort.
105 @set_time_limit( self::LOOPBACK_TIME_LIMIT );
106 }
107 }
108 }
109