PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.3
1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
← All changes | includes/modules/Settings/SettingsModule.php +65 -0 1.1.71.3.3 View file →
@@ -77,8 +77,9 @@
77 77 array(
78 78 'name' => 'xspeed settings',
79 79 'callback' => array( $this, 'cli_handler' ),
80 80 'shortdesc' => 'List modules, or read/update a module\'s settings.',
81 + 'ai_hint' => 'Read or write any xSpeed module\'s settings directly, and export/import the whole configuration. Use for bulk changes or replicating one site\'s setup onto another.',
81 82 'synopsis' => array(
82 83 // No 'options' constraint on `action`: WP_CLI validates a
83 84 // positional's options list against every positional it
84 85 // receives, so `settings get nosuchmodule` failed on the
@@ -300,8 +301,72 @@
300 301 \WP_CLI::error(
301 302 sprintf(
302 303 'Credential fields (%s) cannot be set from the CLI or MCP — they are stripped on this path by design. Set them in the xSpeed dashboard instead.',
303 304 implode( ', ', $secret_fields )
305 + )
306 + );
307 + }
308 +
309 + // Pro licence write gate — the CLI writes through
310 + // Settings_Manager::update() and so never reaches
311 + // Module::update_settings(), where the gate lives. Without this a
312 + // `wp xspeed settings update <pro-module>` turns a Pro feature on with
313 + // no licence, exactly as the MCP handler did. (#185)
314 + $module_object = \XSpeed\Module_Registry::get( $module );
315 + if ( $module_object && $module_object->is_license_locked() ) {
316 + \XSpeed\Activity_Log::record(
317 + 'license_write_refused',
318 + sprintf(
319 + /* translators: %s: module slug. */
320 + __( 'Refused a CLI settings write to the Pro module "%s" — no valid license.', 'xspeed' ),
321 + $module
322 + ),
323 + \XSpeed\Activity_Log::WARN
324 + );
325 + \WP_CLI::error(
326 + sprintf(
327 + '"%s" is a Pro module and this site has no active license, so the write was refused. Nothing was changed.',
328 + $module
329 + )
330 + );
331 + }
332 +
333 + // Refuse rather than report success over a write that won't happen.
334 + // update() walks the schema, so an out-of-schema key is never written
335 + // and never mentioned; an in-schema key with an invalid value is
336 + // dropped back to the stored value just as quietly. Both used to exit
337 + // 0 with "Success", which an agent — or a human script — cannot tell
338 + // apart from a real write. (#206)
339 + $report = Settings_Manager::inspect_input( $module, $values );
340 + if ( ! empty( $report['unknown'] ) || ! empty( $report['invalid'] ) ) {
341 + $lines = array();
342 + foreach ( $report['unknown'] as $key ) {
343 + $line = sprintf( ' %s — not a setting of module "%s"', $key, $module );
344 + $hint = Settings_Manager::hint_for_unknown_key( $key );
345 + if ( '' !== $hint ) {
346 + $line .= "\n " . $hint;
347 + } else {
348 + $near = Settings_Manager::did_you_mean( $module, $key );
349 + if ( ! empty( $near ) ) {
350 + $line .= "\n did you mean: " . implode( ', ', $near ) . '?';
351 + }
352 + }
353 + $lines[] = $line;
354 + }
355 + foreach ( $report['invalid'] as $key ) {
356 + $lines[] = sprintf( ' %s — value rejected by the schema (wrong type, or outside the allowed range/options)', $key );
357 + }
358 +
359 + $applied = empty( $report['applied'] )
360 + ? 'Nothing was written.'
361 + : sprintf( 'Nothing was written — the valid keys (%s) were not applied either, so the whole payload can be corrected and re-sent.', implode( ', ', $report['applied'] ) );
362 +
363 + \WP_CLI::error(
364 + sprintf(
365 + "Refused to update %s:\n%s\n\n%s",
366 + $module,
367 + implode( "\n", $lines ),
368 + $applied
304 369 )
305 370 );
306 371 }
307 372