- Added phpcs:ignore for class file naming convention (79 violations)
WordPress.Files.FileName.InvalidClassFileName is a known architectural
issue — class files use short names without prautoblogger- prefix
- Added phpcs:ignore for ->prepare() table interpolation (56 violations)
WordPress.DB.PreparedSQL.InterpolatedNotPrepared: dynamic table names
(prab_generation_logs, prab_cost_logs) require interpolation; ->prepare()
does not support table name placeholders, only column values
- Fixed file-level docblock positioning (2 violations)
prautoblogger.php and uninstall.php: moved declare(strict_types=1) before
file docblock per PHP best practices
Run phpcbf --standard=WordPress-Core across includes/, prautoblogger.php,
and uninstall.php. Fixed 1,362 formatting violations in 75 files:
- Short array syntax: [] → array()
- Line breaking for long function calls
- Consistent indentation
- Function call parameter alignment
No logic changes; purely mechanical formatting.
Enables reasoning-capable models (Grok 4.1 Fast, DeepSeek-R1, etc.)
via new admin settings: Enable Reasoning toggle and Reasoning Effort
selector. Provider auto-injects reasoning params when enabled; callers
can override per-call via options array. Response parser captures
reasoning_tokens and reasoning_content for visibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracted credential validation, request building, response parsing, and
configuration helpers from the 464-line OpenRouter provider into separate,
focused classes. This follows the pattern established by the Cloudflare
image provider and brings all files under the project's 300-line limit.
Files created:
- class-open-router-validator.php (152 lines): Handles credential validation
with HTTP probes and diagnostic messaging. Delegates from the provider.
- class-open-router-config.php (84 lines): Centralizes admin config resolution
for API base URL and cache TTL settings. Used by both provider and validator.
- class-open-router-request-builder.php (79 lines): Encapsulates request header
building and cURL auth filter registration for hosting environments that
strip Authorization headers.
- class-open-router-response-parser.php (87 lines): Handles response parsing,
error classification, and retry-decision logic.
- Tests: OpenRouterValidatorTest.php with comprehensive credential checks.
Provider shrinks from 464 to 278 lines. All classes are under 300 lines.
Dependencies remain clean: validator and config are independent, request-builder
and response-parser have no circular imports.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds two WP options: prautoblogger_ai_gateway_base_url (routes OpenRouter calls through a Cloudflare AI Gateway when set to an https:// URL; empty = direct, the default) and prautoblogger_ai_gateway_cache_ttl (seconds for cf-aig-cache-ttl response caching; 0 = off).
The gateway is a transparent OpenRouter-compatible proxy, so no consuming class, no provider abstraction, and no response-parsing path changes. The http_api_curl belt-and-suspenders Authorization filter is rescoped to the configured upstream host so it still runs under gateway.ai.cloudflare.com.
Rationale: leverages existing Cloudflare infrastructure to satisfy the CTO cost-control rules (caching, cost tracking, rate limiting, fallback) without building equivalent middleware in-plugin. Fully opt-in and reversible from the admin UI.
Docs: ARCHITECTURE.md options + integrations tables updated; key decision #15 added. Uninstall is unaffected (wildcard prautoblogger_% delete).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of the 401 was a corrupted API key: the WordPress auth
salt changed after the key was encrypted, so decryption produced
garbage instead of sk-or-... OpenRouter interprets the garbled
Bearer token as "Missing Authentication header."
Adds the same sk-or- prefix check to the credential validation
method so users see a clear "re-enter your key" message instead
of a cryptic 401 error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hostinger's hosting environment appears to strip the Authorization
header from outgoing wp_remote_post requests before they reach the
cURL transport. This caused OpenRouter to respond with HTTP 401
"Missing Authentication header" even though the header was set in
the wp_remote_post args array.
Fixes:
- Add http_api_curl filter (priority 99) that re-sets the
Authorization header directly on the cURL handle, bypassing any
intermediate header stripping. Scoped to openrouter.ai URLs only.
- Applied to all three OpenRouter call sites: send_chat_completion,
validate_credentials_detailed, and get_available_models.
- Filter is cleaned up in a finally block to avoid leaking.
Also:
- Add pre-flight key format validation (must start with "sk-or-")
to catch corrupted decryption early with a clear error message.
- Fix retry condition: was `>= 429` (incorrectly retried 430-499),
now `=== 429` (only retry rate-limit).
- Add detailed diagnostic logging on 401/403 auth failures.
- Add .env.credentials to .gitignore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite encryption with "enc:" prefix to permanently eliminate
the double-encryption bug on settings save
- Add one-time migration to clear legacy non-prefixed encrypted values
- Rewrite content analyzer prompts to request N distinct diverse topics
across 5 categories (question, complaint, comparison, news, guide)
- Pass target_count*2 from pipeline to analyzer for headroom
- Add skip_dedup flag so manual Generate Now always produces content
- Generate Now button now sends force=1 to clear stale locks
- Rename OpenRouter provider files to PSR-4 convention (hyphenated)
- Switch default models to google/gemini-2.5-flash-lite
- Clean up reddit_debug log channel to standard 'reddit'
- Bump version to 0.2.1
Cost: ~$0.003/article with Gemini 2.0 Flash, ~34s pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>