PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.7.1
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.7.1
3.7.2 3.7.1 3.7.0 3.6.9 3.6.8 3.6.7 3.6.6 3.6.4 3.6.5 3.6.3 3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / common / rest.php
ai-engine / common Last commit date
admin.php 1 month ago helpers.php 7 months ago issues.php 9 months ago news.php 1 month ago ratings.php 1 month ago releases.txt 2 years ago rest.php 1 week ago
rest.php
309 lines
1 <?php
2
3 class MeowKit_MWAI_Rest {
4 private $namespace = 'meow-common/v1';
5 public static $instance = null;
6
7 public static function init_once() {
8 if ( !MeowKit_MWAI_Rest::$instance ) {
9 MeowKit_MWAI_Rest::$instance = new self();
10 }
11 }
12
13 private function __construct() {
14 add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
15 }
16
17 /**
18 * Capability gate for plugin admin endpoints. Defaults to manage_options but
19 * honours a per-plugin filter so a site can grant access to other roles
20 * without re-implementing every permission_callback. The filter name is
21 * derived from the class name (`MeowKit_<PREFIX>_Rest` => `<prefix>_allow_setup`),
22 * so each plugin gets its own filter automatically after Nekofy substitution.
23 */
24 private function can_setup() {
25 static $filter = null;
26 if ( $filter === null ) {
27 $parts = explode( '_', __CLASS__ );
28 $prefix = isset( $parts[1] ) ? strtolower( $parts[1] ) : '';
29 $filter = $prefix !== '' ? $prefix . '_allow_setup' : '';
30 }
31 $default = current_user_can( 'manage_options' );
32 return $filter !== '' ? apply_filters( $filter, $default ) : $default;
33 }
34
35 public function rest_api_init() {
36 if ( !$this->can_setup() ) {
37 return;
38 }
39 $permission = function () {
40 return $this->can_setup();
41 };
42 register_rest_route( $this->namespace, '/empty_request/', [
43 'methods' => 'POST',
44 'permission_callback' => $permission,
45 'callback' => [ $this, 'empty_request' ]
46 ] );
47 register_rest_route( $this->namespace, '/file_operation/', [
48 'methods' => 'POST',
49 'permission_callback' => $permission,
50 'callback' => [ $this, 'file_operation' ]
51 ] );
52 register_rest_route( $this->namespace, '/sql_request/', [
53 'methods' => 'POST',
54 'permission_callback' => $permission,
55 'callback' => [ $this, 'sql_request' ]
56 ] );
57 register_rest_route( $this->namespace, '/error_logs/', [
58 'methods' => 'POST',
59 'permission_callback' => $permission,
60 'callback' => [ $this, 'rest_error_logs' ]
61 ] );
62 register_rest_route( $this->namespace, '/all_settings/', [
63 'methods' => 'POST',
64 'permission_callback' => $permission,
65 'callback' => [ $this, 'rest_all_settings' ]
66 ] );
67 register_rest_route( $this->namespace, '/update_option/', [
68 'methods' => 'POST',
69 'permission_callback' => $permission,
70 'callback' => [ $this, 'rest_update_option' ]
71 ] );
72 register_rest_route( $this->namespace, '/installed_plugins/', [
73 'methods' => 'POST',
74 'permission_callback' => $permission,
75 'callback' => [ $this, 'rest_installed_plugins' ]
76 ] );
77 // The analysis needs PHP for one reason: $mwai is a PHP global, so the AI
78 // call cannot be made from the dashboard's JavaScript. Everything it
79 // analyses is gathered in the browser and posted here.
80 register_rest_route( $this->namespace, '/analysis_status/', [
81 'methods' => 'POST',
82 'permission_callback' => $permission,
83 'callback' => [ $this, 'rest_analysis_status' ]
84 ] );
85 register_rest_route( $this->namespace, '/analysis_run/', [
86 'methods' => 'POST',
87 'permission_callback' => $permission,
88 'callback' => [ $this, 'rest_analysis_run' ]
89 ] );
90 register_rest_route( $this->namespace, '/analysis_forget/', [
91 'methods' => 'POST',
92 'permission_callback' => $permission,
93 'callback' => [ $this, 'rest_analysis_forget' ]
94 ] );
95 }
96
97 /**
98 * Throw away the stored analysis and the consent that went with it.
99 *
100 * The analysis writes a verdict about this site into the options table and
101 * keeps it. That is what makes it survive a reload, but it also means a
102 * description of your server's weaknesses — written by a third-party model —
103 * sits there indefinitely with nothing to remove it. Somebody should be able
104 * to take it back, and clearing the consent means the panel explaining what
105 * gets sent is shown again before anything is sent a second time.
106 */
107 public function rest_analysis_forget() {
108 delete_option( 'meowapps_analysis_last' );
109 delete_option( 'meowapps_analysis_consented' );
110 return new WP_REST_Response( [ 'success' => true ], 200 );
111 }
112
113 /**
114 * Whether an analysis can be run, and the last one if there is one.
115 *
116 * Three states matter and they are not the same: AI Engine absent, AI Engine
117 * present but with no API key configured, and ready. Telling the second from
118 * the first is what lets the dashboard say "finish setting it up" rather than
119 * "install this", which would be wrong and mildly insulting.
120 */
121 public function rest_analysis_status() {
122 global $mwai;
123 $installed = !empty( $mwai );
124 $ready = $installed && method_exists( $mwai, 'hasAI' ) && $mwai->hasAI();
125 return new WP_REST_Response( [ 'success' => true, 'data' => [
126 'installed' => $installed,
127 'ready' => $ready,
128 'consented' => (bool) get_option( 'meowapps_analysis_consented', false ),
129 'last' => get_option( 'meowapps_analysis_last', null ),
130 ] ], 200 );
131 }
132
133 public function rest_analysis_run( $request ) {
134 global $mwai;
135 if ( empty( $mwai ) || !method_exists( $mwai, 'simpleJsonQuery' ) ) {
136 return new WP_REST_Response( [ 'success' => false,
137 'message' => 'AI Engine is not available on this site.' ], 200 );
138 }
139 if ( !method_exists( $mwai, 'hasAI' ) || !$mwai->hasAI() ) {
140 return new WP_REST_Response( [ 'success' => false,
141 'message' => 'AI Engine has no AI environment configured yet.' ], 200 );
142 }
143
144 $params = $request->get_json_params();
145 $facts = isset( $params['facts'] ) ? $params['facts'] : [];
146
147 // Consent is recorded when a run is actually asked for, so the panel is
148 // shown once rather than on every visit.
149 update_option( 'meowapps_analysis_consented', '1' );
150
151 try {
152 $reply = $mwai->simpleJsonQuery( $this->analysis_prompt( $facts ) );
153 }
154 catch ( Exception $e ) {
155 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 200 );
156 }
157
158 $verdict = is_string( $reply ) ? json_decode( $reply, true ) : $reply;
159 if ( empty( $verdict ) || !is_array( $verdict ) ) {
160 return new WP_REST_Response( [ 'success' => false,
161 'message' => 'The AI reply could not be read as JSON.' ], 200 );
162 }
163
164 $verdict['ranAt'] = current_time( 'mysql' );
165 update_option( 'meowapps_analysis_last', $verdict );
166
167 return new WP_REST_Response( [ 'success' => true, 'data' => $verdict ], 200 );
168 }
169
170 /**
171 * Written for the person who owns the site, not for a developer: somebody who
172 * installed a plugin, not somebody who knows what max_execution_time is.
173 *
174 * Meow Apps plugins may be named as a remedy, but only where one genuinely
175 * fixes the finding. An analysis that recommends its own author's plugins for
176 * everything is an advert, and would be read as one.
177 */
178 private function analysis_prompt( $facts ) {
179 $json = wp_json_encode( $facts, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
180 return "You are reviewing a WordPress site's health for the person who owns it.\n"
181 . "They are not a developer: explain what something means and why it matters before saying "
182 . "what to do, and never assume they know what a PHP directive is.\n\n"
183 . "Here is what was measured on their site:\n\n$json\n\n"
184 . "Reply with JSON in exactly this shape:\n"
185 . "{\n"
186 . ' "areas": [ { "area": "Speed|Environment|Errors", "rating": 1-5, '
187 . '"label": "a two or three word verdict", "summary": "one plain sentence" } ],' . "\n"
188 . ' "findings": [ { "area": "Speed|Environment|Errors", "severity": "high|medium|low", '
189 . '"title": "the recommendation itself, plain, under 60 characters", '
190 . '"detail": "at most two short sentences on what it means and why it matters", '
191 . '"action": "one sentence saying what to do", '
192 . '"plugin": "meow plugin slug or null" } ],' . "\n"
193 . ' "conclusion": "two short sentences tying it together"' . "\n"
194 . "}\n\n"
195 . "Rules:\n"
196 . "- Rate each of the three areas from 1 (bad) to 5 (good). Do not invent an overall score.\n"
197 . "- Only report findings genuinely worth acting on. An empty findings list is a perfectly "
198 . "good answer for a healthy site — do not manufacture problems.\n"
199 . "- Order findings by how much they matter, worst first.\n"
200 . "- Be brief. The reader sees the titles first and opens the ones they care about, so a "
201 . "title has to work on its own and the detail must not repeat it. No preamble, no restating "
202 . "the question, no filler.\n"
203 . "- Set \"plugin\" to one of these Meow Apps slugs ONLY when that plugin genuinely fixes the "
204 . "finding, otherwise null: ai-engine, code-engine, contact-form-block, database-cleaner, "
205 . "media-cleaner, media-file-renamer, meow-gallery, meow-lightbox, meow-mailer, seo-engine, "
206 . "social-engine, wp-retina-2x, wplr-sync.\n"
207 . "- If an error in the log comes from a specific plugin, say which one.\n"
208 . "- Write in the language of this site: " . get_bloginfo( 'language' ) . ".";
209 }
210
211 public function file_rand( $filesize ) {
212 // Write the benchmark file inside a dedicated subfolder of the uploads
213 // directory (created on demand), then remove it. wp.org forbids writing to
214 // the plugin folder or the uploads root — only a sanctioned subfolder.
215 $upload = wp_upload_dir();
216 if ( !empty( $upload['error'] ) || empty( $upload['basedir'] ) ) { return; }
217 $dir = trailingslashit( $upload['basedir'] ) . 'meowapps';
218 if ( !wp_mkdir_p( $dir ) ) { return; }
219 $path = trailingslashit( $dir ) . 'speedtest-' . wp_generate_password( 12, false ) . '.tmp';
220 $fh = @fopen( $path, 'wb' );
221 if ( $fh === false ) { return; }
222 fseek( $fh, $filesize - 1, SEEK_CUR );
223 fwrite( $fh, 'a' );
224 fclose( $fh );
225 @unlink( $path );
226 }
227
228 public function empty_request() {
229 return new WP_REST_Response( [ 'success' => true ], 200 );
230 }
231
232 public function file_operation() {
233 $this->file_rand( 1024 * 10 );
234 return new WP_REST_Response( [ 'success' => true ], 200 );
235 }
236
237 public function sql_request() {
238 global $wpdb;
239 $count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts}" );
240 return new WP_REST_Response( [ 'success' => true, 'data' => $count ], 200 );
241 }
242
243 // List all the options with their default values.
244 public function list_options() {
245 return [
246 'meowapps_hide_meowapps' => false,
247 'force_sslverify' => false
248 ];
249 }
250
251 public function get_all_options() {
252 $options = $this->list_options();
253 $current_options = [];
254 foreach ( $options as $option => $default ) {
255 $current_options[$option] = get_option( $option, $default );
256 }
257 return $current_options;
258 }
259
260 public function rest_all_settings() {
261 return new WP_REST_Response( [ 'success' => true, 'data' => $this->get_all_options() ], 200 );
262 }
263
264 public function rest_installed_plugins() {
265 if ( !function_exists( 'get_plugins' ) ) {
266 require_once ABSPATH . 'wp-admin/includes/plugin.php';
267 }
268 $all_plugins = get_plugins();
269 $result = [];
270 foreach ( $all_plugins as $plugin_file => $plugin_data ) {
271 // Plugin file looks like "ai-engine/ai-engine.php" — the slug is the
272 // first path segment. Some plugins live at the root (single file),
273 // those we just skip; we only care about Meow Apps directories anyway.
274 $parts = explode( '/', $plugin_file );
275 if ( count( $parts ) < 2 ) {
276 continue;
277 }
278 $slug = $parts[0];
279 $result[ $slug ] = is_plugin_active( $plugin_file ) ? 'active' : 'inactive';
280 }
281 return new WP_REST_Response( [ 'success' => true, 'data' => $result ], 200 );
282 }
283
284 public function rest_update_option( $request ) {
285 $params = $request->get_json_params();
286 try {
287 $name = $params['name'];
288 $options = $this->list_options();
289 if ( !array_key_exists( $name, $options ) ) {
290 return new WP_REST_Response( [ 'success' => false, 'message' => 'This option does not exist.' ], 200 );
291 }
292 $value = is_bool( $params['value'] ) ? ( $params['value'] ? '1' : '' ) : $params['value'];
293 $success = update_option( $name, $value );
294 if ( !$success ) {
295 return new WP_REST_Response( [ 'success' => false, 'message' => 'Could not update option.' ], 200 );
296 }
297 return new WP_REST_Response( [ 'success' => true, 'data' => $value ], 200 );
298 }
299 catch ( Exception $e ) {
300 return new WP_REST_Response( [ 'success' => false, 'message' => $e->getMessage() ], 500 );
301 }
302 }
303
304 public function rest_error_logs( $request ) {
305 return new WP_REST_Response( [ 'success' => true, 'data' => MeowKit_MWAI_Helpers::php_error_logs() ], 200 );
306 }
307
308 }
309