=== AI CMO Agent Connector ===
Contributors: aicmo
Tags: ai, content, autoblog, publishing, seo
Requires at least: 5.8
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.2.5
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Pull AI-generated articles from AI CMO into your site on a schedule. Paste one API key — no application password, no inbound access.

== Description ==

AI CMO Agent Connector is a lightweight, **pull-based** connector. Instead of letting an
external service push into your site (which needs an application password, an exposed REST
API, and inbound firewall access), this plugin **pulls** already-generated articles from
`api.cmo.so` over plain outbound HTTPS and publishes them locally.

**Why pull?**

* No application password, no exposed endpoints, no inbound firewall rules.
* Works behind WAFs / Cloudflare / security plugins that block inbound automation.
* The API key is **read-only and domain-scoped** — it can only fetch your own domain's
  posts, it cannot write to or administer your site.

**How it works**

1. Claim your domain on ai.cmo.so and generate an API key on the **API Access** page.
2. Install this plugin, paste the key, click **Check connection** (validates the key,
   confirms the domain matches your site, and shows your quota).
3. Choose a post status, category, author, and schedule.
4. The plugin pulls new posts on a WP-Cron schedule and publishes them.

== Security ==

* Only administrators (`manage_options`) can view or change settings; all actions are
  nonce-protected.
* The API key is stored in options and never echoed back (masked, last 4 shown).
* Outbound requests go only to a fixed host (`api.cmo.so`) with TLS verification — no
  user-supplied URLs (no SSRF).
* Delivered content is sanitized with `wp_kses_post` before insertion.

== Frequently Asked Questions ==

= Do I need an application password? =
No. That is the whole point — pull needs no application password and no inbound access.

= Where do I get the API key? =
On https://ai.cmo.so → your domain → API Access → Generate Key.

= My posts are not appearing on schedule =
WP-Cron only runs when your site receives traffic. On low-traffic sites, ask your host to
run a real system cron against wp-cron.php.

= Does it auto-update? =
Yes. Although it is not on wordpress.org, the plugin checks a self-hosted release manifest
and plugs into WordPress's normal update system — you get the usual "update available"
notice, one-click update, and per-plugin "Enable auto-updates" toggle on the Plugins screen.

= Can I build a white-label copy for my agency? =
Yes. Run `build-whitelabel.sh` with your plugin slug, display name, and `branding.json`.
It outputs a branded zip plus a `latest` manifest, and the white-label package can be
served from `https://api.cmo.so/plugin/<slug>/`.

White-label branding may include a safe HTML information block in the blue header area;
the CTA button is rendered below that block.

= Where do downloads come from? =
The canonical feed is `https://api.cmo.so/plugin/aicmo-agent-connector/latest`. White-label
builds use the same pattern with your slug, for example
`https://api.cmo.so/plugin/acme-content-engine/latest`.

== Changelog ==

= 1.2.4 =
* Fixed: the "Run now" button only ever pulled a new post — it never checked
  for a queued rewrite, so testing/forcing Auto-rewrite delivery by clicking
  it appeared to do nothing even when a rewrite was actually waiting to be
  delivered. It now checks for both on every click, same as the scheduled
  WP-Cron run already did.

= 1.2.3 =
* Fixed: a rewrite always landed as ONE monolithic Custom HTML block instead
  of native paragraph/heading/list blocks, unlike a normal new post. The
  rewrite pipeline wraps its output in an outer `<div>`; since that made the
  whole article exactly one top-level element, the HTML→blocks converter had
  nothing else to map it to and fell back to a single `wp:html` block. The
  converter now unwraps a lone top-level wrapper `<div>` first, so a rewrite's
  actual paragraphs/headings/lists convert the same way a new post's do.
  Only past posts already delivered before this update keep the old,
  single-block markup — a future rewrite of the same post will fix it going
  forward.

= 1.2.2 =
* Hardened Auto-rewrite edge cases ahead of wider rollout:
  * The rewrite pull now also checks the local Auto-rewrite toggle before
    calling the API (previously it relied only on the server never having
    anything queued when the toggle is off).
  * The rewrite pull now respects an active daily-quota pause, instead of
    re-discovering the same 402/403 on every tick.
  * A rewrite is skipped (and logged) if the target post is no longer
    `publish` status — an owner who drafted or unpublished a post keeps
    that decision instead of it being silently overwritten.
  * Saving the Auto-rewrite setting now logs a warning if syncing it to
    AI CMO fails, instead of failing silently and leaving the local toggle
    out of sync with the server.
* Note: new-post pulling and rewrite pulling were already independent —
  an empty rewrite queue never blocked or delayed the regular new-post
  pull, and still doesn't.

= 1.2.1 =
* Fixed: the Auto-rewrite ratio/cooldown rows (and the whole Auto-rewrite row
  when Auto-publish is off) stayed visible even though they were correctly
  marked `hidden` in the markup — the settings-row CSS unconditionally set
  `display: grid`, which always wins over the browser's default
  `[hidden] { display: none }` regardless of attribute presence. Added the
  same explicit `[hidden] { display: none }` override already used for the
  plugin's modals.

= 1.2.0 =
* New: the connector now also pulls REWRITES of posts it has already
  published — a refreshed title, meta description, target keyword and
  content for an existing post, at the SAME URL, rides the same scheduled
  pull as new posts. Applied via the post's stored AI CMO content id, never
  by matching slug/URL, so a manually renamed permalink can't misroute an
  update.
* IndexNow (Bing/Yandex/etc.) now re-notifies on a rewrite, not just on
  first publish.
* New "Auto-rewrite" toggle below Auto-publish: turn it on to reserve a share
  of your daily quota for rewrites, and set a cooldown (minimum age) before a
  post becomes eligible again. Same setting as the ai.cmo.so dashboard's
  ratio slider — either one updates the other.

= 1.1.62 =
* The plugin no longer requires full administrator rights. It now checks its own
  `manage_aicmo_connector` capability, so access can be granted to custom roles.
* New "Access" page (AI CMO → Access) where an administrator chooses which roles
  may use the plugin. The Administrator role is always allowed and cannot be
  unchecked, so a site owner cannot lock themselves out.
* Only administrators can open the Access page itself — a granted role cannot
  widen its own access.
* Scheduled publishing is unchanged: it runs without a signed-in user and never
  depended on these permissions.
* Note: this capability is privileged. A granted role can change the API
  connection, run a pull immediately, publish the fetched articles, and connect
  Google Search Console.

= 1.1.60 =
* Added a one-click "Backfill all" task for importing legacy AI CMO posts into the
  Trackable inventory.
* Backfill runs in background batches with retry-safe progress tracking and shows
  processed progress beside the Posts pagination.

= 1.1.54 =
* Settings: removed the "Show" button for a saved API key, along with the endpoint
  that returned it — the stored key can no longer be revealed from the admin.
* Settings: the saved key now displays as `aicmo_XXXX*****XXXX` so two keys are
  still easy to tell apart.

= 1.1.53 =
* AI Visibility: fixed the missing prompt input on the "Check a prompt" card — the
  field was being stripped before it reached the page, leaving only the button.

= 1.1.2 =
* Added agency-configurable HTML information content in the branded blue header area.
* Added agency-configurable social channel links for branded plugin builds.
* Moved the branded CTA below the configurable information content.
* Bumped the plugin version so WordPress detects rebuilt packages as updates.

= 1.1.1 =
* All Posts: the plugin's AI posts (tagged "hidden") are now kept out of the default
  Posts list, with a "AI CMO (hidden)" view link to reveal them.
* Fixed the "Run now" button icon alignment and modal button icons.
* Coding-standards cleanup (i18n comments, sanitization, file helpers) and "Tested up to"
  updated for WordPress 7.0.

= 1.1.0 =
* SEO mapping: pulled posts now set the category (from the topic), tags (keywords + a
  "hidden" tag), excerpt/meta description, and a featured image with alt text.
* Featured images: import Unsplash sources reliably (download + sideload), de-duplicate by
  source so the same image is reused, avoid repeating an image across the last 24 posts, and
  store an optimized, cropped size.
* Content: convert delivered HTML into native Gutenberg blocks on WordPress 5.0+.
* New "Posts" admin page: list, search, sort (views / date / title), relative dates, a
  per-post view counter, and one-click hide / make-visible.
* "hidden"-tag visibility: AI posts stay indexable but out of the public blog feed by
  default, with a toggle to show them.
* Redesigned Setup wizard (Welcome → Publishing & Schedule), connection auto-check, Save
  button, Run-now confirmation, and a polished status card.

= 1.0.0 =
* Initial release: key check + domain match + quota, scheduled pull, dedupe, recovery,
  featured-image side-load, native WordPress admin UI.
