| 开发者 | numinix |
|---|---|
| 更新时间 | 2026年9月23日 02:43 |
| PHP版本: | 8.0 及以上 |
| WordPress版本: | 7.1 |
| 版权: | GPLv2 or later |
| 版权网址: | 版权信息 |
pre_get_posts +
posts_pre_query. Auto-detects public Custom Post Types so blog posts,
pages, docs, and your custom CPTs all participate in the same ranked
result set.woocommerce_product_query + posts_search. Indexes products plus
variations, stock state, price, SKU, and attributes. Auto-on when
WooCommerce is detected; dormant otherwise.WP_Query (or WC_Query for products) inside the
same request when the platform is unreachable. Visitors never see an
empty results page. A per-tenant circuit breaker prevents repeated
timeouts from compounding.wp seekmodo index --full to seed your tenant
immediately. (The plugin will otherwise reindex incrementally on
its own.)shadow to active in the connector
settings once you've spot-checked a few searches.wp plugin install seekmodo --activate
wp seekmodo connect --tenant=<tenant_id> --secret=<shared_secret>
wp seekmodo index --full
wp option update seekmodo_wp_mode shadowYes. Seekmodo Search is the WordPress connector for the Seekmodo hosted AI search platform; the plugin itself is free and GPLv2, but it needs a paired Seekmodo tenant to do its job. Every plan ships with a 14-day free trial at $0 with full features and unlimited searches. After the trial, plans start at $29/month. Pricing details: https://seekmodo.com/pricing.
No. The content vertical works on any WordPress site (posts, pages, public custom post types). The commerce vertical activates automatically when WooCommerce is detected, and idles otherwise.
Every gateway-talking entry point falls through to native WP_Query
(or WC_Query for products) inside the same request. Visitors never
see an empty results page. A per-tenant circuit breaker prevents
repeated timeouts from compounding, and Seekmodo health metrics
auto-demote the connector back to native search if our gateway is
unhealthy.
Same as the previous answer. The plugin stays installed, the gateway-talking entry points start returning "no results" (since the tenant is gated), and every entry point falls back to native search in the same request. Your site keeps working; you just stop getting AI relevance. Re-pairing with a paid tenant restores full functionality.
Yes — Seekmodo locks each tenant to a canonical storefront host. Staging / dev hosts are auto-detected and return no-op from every gateway-talking entry point, so your production search index is never overwritten by a non-production deploy.
In wp_options (seekmodo_wp_tenant_id, seekmodo_wp_shared_secret).
wp seekmodo connect can be re-run any time to rotate the pair.
Indexable post / product content goes to mcp.seekmodo.com (the
gateway). Visitor search queries and clicks go to mcp.seekmodo.com
as telemetry. The update check (12 h cadence) goes to
seekmodo.com/api/plugins/seekmodo. Configuration pull (5 min
cadence) goes to mcp.seekmodo.com/v1/tenant/snapshot. All
gateway-bound requests carry an HMAC signature derived from the
pairing secret. Full details in
https://seekmodo.com/legal/privacy.
The admin UI is localised via the seekmodo text domain
and /languages/ directory. We ship English (en_US) as the source
locale; contributions for other locales are welcome.
The production Suggest bundle
(assets/suggest/seekmodo-suggest.global.js) is a minified IIFE built
from @seekmodo/web-components. Readable TypeScript for that bundle
(and for the Ask IIFE / full web-components ESM) ships inside the
plugin at assets/suggest/src/. See assets/suggest/SOURCE.md for
the entry → file map, rebuild steps (npm install + npm run build
in the web-components package), and notes on the inlined SDK copy
under assets/suggest/src/vendor/seekmodo-sdk/. Ask and full ESM
pointers: assets/ask/SOURCE.md, assets/web-components/README.md.
post_type=any is no longer treated as a literal local-index type filter (that returned zero hits on unscoped SERPs). Opt-in Enhanced Native / Relevanssi-compat only.spawn_cron() is skipped until WP_CRON_LOCK_TIMEOUT is defined, avoiding fatals during early MU-plugin activation.seekmodo_result_group / result_group context on seekmodo_local_index_results for request-scoped section filters.wp seekmodo local_index build remains synchronous for operators./seekmodo/v1/images is intentional public read-only image hydrate; remove mark_dirty (no unauthenticated post meta writes). Dirty reindex remains via product-save hooks and gateway pending_image_refresh_ids./seekmodo/v1/images is intentional public read-only image hydrate; remove mark_dirty (no unauthenticated post meta writes). Dirty reindex remains via product-save hooks and gateway pending_image_refresh_ids./seekmodo/v1/images, Suggest images-hydrate-url, schedule commerce index cron, drain gateway pending_image_refresh_ids.$_SERVER (and related) inputs early via ServerInput helper; DOCUMENT_ROOT path guards; SearchSwap GET numeric casts sanitized.Tested up to in readme.txt only; ship Suggest/Ask TS under assets/suggest/src/; remove static ABSPATH .well-known writes (PHP discovery only).over_quota sticky while trial is still active.enabled_verticals=commerce) now claim unscoped /?s= / post_type=any main searches so search_event_id reaches the click beacon./?s= without post_type=product) now stamps search_event_id so click beacons are accepted; also supports Rank Math /search/{q}/ keyword detection.wp_schedule_event() was called with a recurrence name that was never registered via the cron_schedules filter, so WordPress silently refused to schedule it while searches kept working fine — the underlying cause of fleet monitoring flagging sites with real traffic but zero recorded clicks. Registering the plugin now also self-heals existing installs immediately (no need to deactivate/reactivate).@seekmodo/web-components ESM under assets/web-components/ (no
default CDN load; override via seekmodo_web_components_src).Tested up to: 7.0. Health dashboard widget requires
manage_options. Suggested privacy-policy guide text added.support_count across up
to 10 anchors.pdp-cascade, cart) via
/wp-json/seekmodo/v1/recommendations with cross-section de-dupe and
in-cart exclude_doc_ids, matching the AKS cascade contract.@seekmodo/web-components
on those templates even without Gutenberg blocks.Search results: "" after the
gateway search swap clears the query s var.@seekmodo/web-components suggest bundle.search_event_id from /v1/suggest
meta for LTR click attribution.@seekmodo/web-components suggest bundle (split-rail
mobile divider, native title tooltips, anchor performance fixes).<seekmodo-suggest> custom element (~7 KB gzip, lives at
assets/suggest/seekmodo-suggest.global.js) instead of the legacy
flat-row dropdown. The web component renders the rich /v1/suggest
envelope from the gateway (WS-2): recent + trending + keywords +
products + categories + did-you-mean + "View all N results" CTA,
all from one server round-trip. UX is shared 1:1 with the
BigCommerce / Zen Cart / AKS / WordPress connectors so a tenant
with multiple storefronts gets one suggestions UI everywhere.seekmodo_wp_suggest_legacy per-site option
(Settings → Seekmodo → "Use legacy flat-row typeahead instead of the
universal suggest widget") flips back to the v0.5.x dropdown for one
major-version cycle. The two surfaces are mutually exclusive —
Frontend\TypeaheadUI::isEnabled() returns false when the flag is
off, preventing double-attaching dropdowns to the same input.POST /wp-json/seekmodo/v1/browser-token
route returns {token, expires_at, session_id} so the in-bundle SDK
can refresh a 5-min JWT without a full page reload. Cached behind
the existing per-tenant transient in Gateway\BrowserToken so a
long-running tab amounts to ~one mint per ~4 min regardless of how
many keystrokes the shopper triggers.seekmodo_suggest_selectors — CSS-selector list the autoboot
walks (default input[type="search"], input[name="s"],
input[data-seekmodo-suggest], input[data-seekmodo-typeahead]).seekmodo_suggest_blocks — CSV of blocks the widget should
render in order (default recent,did_you_mean,keywords,trending, products,categories). Drop any block by leaving it out.seekmodo_suggest_view_all_href — URL template for the CTA.
Default /?s={q} (or /?s={q}&post_type=product on Woo sites).seekmodo_suggest_extra_attrs — assoc map of additional
attributes (e.g. min-length, debounce-ms, limit,
cache-size, lang) forwarded to every spawned element.seekmodo_suggest_config — last-resort filter on the localized
JS config.--seekmodo-suggest-bg,
--seekmodo-suggest-row-hover, --seekmodo-suggest-thumb-size,
etc.) so child themes can theme the dropdown without forking
its markup.<slot name="recent|keywords|trending|products|categories|empty| header|footer"> lets a theme drop bespoke HTML inside an
element and override any one block's rendering wholesale.wp seekmodo index --full now reindexes every
gateway-enabled vertical for the tenant (content + commerce, in
series), and accepts a new --vertical=content|commerce|all flag
for surgical per-vertical reindexes. Pre-0.5.3 the command only
invoked Content/Indexer::reindexFull(); commerce-only operators
(and dual-vertical operators chasing a stale product index) had to
drop into wp eval '\Numinix\SeekmodoWordPress\Commerce\Indexer::reindexFull();'.
Verticals the gateway hasn't enabled for this tenant — or whose
runtime dependency is missing (e.g. WooCommerce inactive) — are
skipped silently with an info-level log line; the command reports
the per-vertical totals separately so the operator can tell at a
glance whether each vertical actually executed.DomainLock::canIndex() and surfaced in wp seekmodo health /
wp seekmodo diagnose as a new can_index row. Used by every
gateway-mutating helper (numinix_seekmodo_run_bulk_upsert,
numinix_seekmodo_mirror_click) plus the cron-tick early-out in
both Content/Indexer::tick() and Commerce/Indexer::tick().
Failure modes the new gate catches that the looser
DomainLock::matches() did NOT:dev.example.com
seeds its seekmodo_wp_storefront_host to its own home_url() at
activation; matches() returns true (current==locked); pre-0.5.3
indexer cron would happily push every product save to the prod
tenant's Typesense collection, with dev.example.com URLs
poisoning the customer-visible search results. Exact PM-303
regression.tenant.snapshot pull yet:
pre-0.5.3 matches() returned true (no lock → tentative allow).
0.5.3 fails closed.numinix_seekmodo_enabled() so an
operator smoke-testing search behavior on a staging clone still
gets results — only writes are tightened.seekmodo_disable_storefront_host_lock (existing, since v0.1.0)
— read AND write; bypasses every check. Used by blue/green CDN
setups where the request reaches WP under a different Host
header than the visitor sees.seekmodo_allow_nonprod_indexing (NEW, v0.5.3) — write only;
consenting non-prod indexer for installs paired to a separate
*-dev tenant. Defaults false.wp seekmodo health now prints current_host,
locked_host, host_lock_match, can_index, and
looks_like_nonprod instead of the older single host_lock_match
row, so an operator chasing "why isn't dev indexing?" can see
every input the gate consults at a glance.SEEKMODO_WP_VERSION constant
to the last-seen value stored in wp_options on every request,
and on a mismatch fans out cache invalidations to every page-cache
/ minify / object-cache / OPCache plugin we know about. Closes the
"I rsync'd v0.5.0 onto the server but visitors still load v0.4.0"
regression that surfaced on POCO after the v0.5.0 push: WP Rocket
was serving the cached v0.4.0 minified JS for 1 year because it
only invalidates on upgrader_process_complete (which rsync
deploys never fire).opcache.validate_timestamps=0 (typical on shared hosting
for performance) silently kept executing the old PHP class
bytecode after rsync. The reset is wrapped in a function-exists
guard so PHP builds without OPCache compiled in stay safe.seekmodo_skip_upgrade_flush
filter, or extend it with extra do_action keys via
seekmodo_upgrade_flush_actions.mcp.seekmodo.com/v1/typeahead
directly from the storefront browser using a short-lived browser
token minted server-side at page render. Skips the WordPress REST
hop (and the ~1.5–2 s WordPress full-stack boot it pays on a heavy
WooCommerce + Wordfence install) on every keystroke.
Per-keystroke latency on POCO observed dropping from ~2,250 ms to
~80–150 ms.(vertical, max, q) prefix. Backspace + retype on a
previously-fetched prefix renders synchronously from in-memory
cache, no network round-trip. Cache size is operator-tunable via
the seekmodo_typeahead_config filter.Cache-Control: public, s-maxage=30, must-revalidate + Vary: X-Seekmodo-Tenant on gateway typeahead
responses lets Cloudflare / Varnish absorb keystroke storms on
hot prefixes without leaking rows across tenants.seekmodo_typeahead_gateway_direct filter. Defaults
true. Site can __return_false to force every typeahead call
back to the WordPress REST endpoint — useful for incident-response
/ debugging without a connector update.Gateway/BrowserToken.php — caches the per-tenant
browser-token mint in a WordPress transient (refresh window
honored, hard-cap 240 s). The mint failure mode silently falls
back to the REST hop, so the typeahead never breaks regardless
of token availability.Client::typeahead()
POSTed /v1/typeahead against the gateway, the gateway returned
404 tool_not_found, and the connector fell back to a native
WP_Query LIKE search on every keystroke (~2.3 s on POCO,
~1.1 s on Trek.ca). The new gateway-side TypeaheadTool (SM-602
on the platform side) is what makes the gateway-direct path
return real Typesense rows in ~80 ms.assets/typeahead/seekmodo-typeahead.{js,css} plus
src/Frontend/TypeaheadUI.php — a vanilla-JS dropdown that
auto-binds to common search inputs (input[type="search"],
input[name="s"], input[data-seekmodo-typeahead]), debounces
input at 150ms, and renders rows from the existing
/wp-json/seekmodo/v1/typeahead REST endpoint. Two row shapes:
content rows (title + post-type meta + excerpt + thumbnail) and
commerce rows (product card with price / sale / stock badges).seekmodo_show_branding filter +
OPT_SHOW_BRANDING option. Default OFF — partner sites flip it on
via the Settings page or have it pushed down by the gateway's
tenant-snapshot config pull. White-label by default.inc/seekmodo-search.php) doesn't double-render.seekmodo_enqueue_typeahead,
seekmodo_typeahead_selectors, and seekmodo_typeahead_config
filters so themes can suppress or customise the connector UI
without forking the JS.assets/typeahead/seekmodo-beacon.js click
attribution picks up data-seekmodo-id / data-seekmodo-pos /
data-seekmodo-surface="typeahead" attributes the new dropdown
emits — no second beacon, no separate REST endpoint, click-through
data flows through the same EventsQueue pipeline.Search ranking priority field on
Settings → Seekmodo Search, or by setting the
seekmodo_wp_post_type_priority option directly. The
seekmodo_post_type_priority filter overrides for site-specific
mu-plugins. Default is empty -- no re-rank, gateway order flows
through untouched. Fixes the trek.ca complaint where blog posts
dominated ?s=PPC while the high-intent /services/ pages got
buried below the fold./wp-json/seekmodo/v1/connect/callback was registered behind
Bootstrap::boot()'s is_admin() gate, so the route never
appeared on the server-to-server POST from seekmodo.com (REST API
requests are not "admin" in WordPress's classification). The
admin-side Connect button worked, but the JWT callback POST
silently 404'd and pairing died at the last step. v0.3.3 lifts
ConnectFlow::register() out of the is_admin() block so the
rest_api_init hook always runs. The
admin_post_seekmodo_wp_connect hook stays registered globally
too (harmless -- it only fires on admin-post.php hits).install_token + signed-JWT protocol
(the same protocol Seekmodo for Zen Cart has used since v1.0.x).
v0.3.0 and v0.3.1 sent legacy nonce + return_to query params
that seekmodo.com rejects with "This link is incomplete" — the
redirect-back side of that protocol was an aspirational design
that was never implemented on the seekmodo.com side. Pairing
through the browser was impossible until this release.
Admin\ConnectFlow::initiate() now mints a 32-hex
install_token (10-min TTL) and sends
?install_token=<t>&callback=<rest-url> to seekmodo.com/connect.
Admin\ConnectFlow::handleCallback() is registered as a
public REST endpoint at
wp-json/seekmodo/v1/connect/callback. seekmodo.com POSTs
{token, install_token} to that URL with the
X-Seekmodo-Install-Token header.Connect\Pairing class verifies the EdDSA-signed JWT
against https://seekmodo.com/.well-known/jwks.json, validates
the standard claims (iss/aud/exp/install_token), and persists
tenant_id + shared_secret + mcp_url.Gateway\Client::verifyClaimToken() and the matching
wp seekmodo connect --token=<...> CLI flag. They both round-tripped
through a /v1/connect/verify endpoint that the gateway never
implemented; users hitting that path were getting silent failures.
The CLI pair still works via --tenant= --secret= for
headless / CI installs.sodium extension (ext-sodium) -- needed for the
JWT signature verify. It's part of core PHP since 7.2 and shipped
on default cPanel/EasyApache builds, but if it's not loaded the
pair-callback surfaces a cPanel-specific install hint
(yum install -y ea-php82-php-sodium).Admin\ConnectFlow::initiate() called
wp_safe_redirect('https://seekmodo.com/connect?...') without
whitelisting the connect host. WP's redirect validator silently
rewrote the destination back to the site root, landing the
operator on the WP admin homepage instead of the connect page —
pairing was impossible via the browser flow. The wp seekmodo connect CLI path was unaffected. Now registers a one-shot
allowed_redirect_hosts filter for the connect host (overridable
via the existing seekmodo_connect_base_url filter) for the
duration of the redirect call only.Commerce\ProductFields::buildDoc() now emits
gateway-canonical field names — name, url, in_stock (bool),
category_id[], category_breadcrumbs[], image_url,
description (combined excerpt + content_plain, HTML-stripped, 4
KB cap), last_indexed_at, plus optional on_sale, sale_price,
currency. Removes pre-v0.3.0 WP-shaped names (title,
permalink, stock_status string, regular_price, excerpt,
content_plain, attributes, variations[], categories,
post_type, extra) that the gateway's strict-schema Typesense
was rejecting on every upsert.Options::OPT_COMMERCE_SCHEMA_VERSION (commerce_schema_version
in wp_options) is consulted by Commerce\Indexer::tick() and
triggers a one-shot full reindex on upgrade — same trick as the
v0.1.1 content-side bump.dev|staging|stage|test|qa|preview|sandbox) and the canonical
lock matches that host. One-click POST to the gateway's new
tenant.domain.promote MCP tool flips locked_domain to the
selected production host. Within 5 min the dev plugin's next
tenant.snapshot pull sees the new lock and goes silent
(DomainLock mismatch), so dev / staging mirrors stop polluting
production search analytics automatically. Closes the operator
round-trip the v0.2.x flow required.Gateway\Client::promoteCanonicalDomain() thin HTTP
helper for the new tool. Same HMAC envelope as every other
gateway call.tenant-settings-form.tsx now shows
a "Promote dev to production" hint band + one-click action when
the tenant is locked to a non-prod host and a prod-looking
connector has authenticated. Pre-fills the locked-domain field
with the most-recently-seen prod host; operator clicks Save to
apply.Update URI: https://seekmodo.com/plugins/seekmodo
(WP 5.8+) routes update checks to our marketing-site endpoint, so
the plugin lists at https://wordpress.org/plugins/seekmodo/ for
initial install + searchability while all subsequent
version-checks and zip downloads happen against seekmodo.com.Numinix\SeekmodoWordPress\Updates\WporgUpdateClient —
hooks update_plugins_seekmodo.com (the filter key WP core
synthesises from our Update URI header) and the
plugins_api → plugin_information "View details" modal. Reads
from https://seekmodo.com/api/plugins/seekmodo, cached 12 h on
success and 5 min on failure. Endpoint is filterable via
seekmodo_wporg_update_endpoint for staging.Tested up to: 6.6 added to plugin header + readme so
the WP-admin compatibility filter and the w.org directory listing
reflect the latest tested WordPress release.tools/build_release.py --auto-pr now writes a manifest
entry whose schema matches the live platforms.zen_cart entry
exactly (sha256 / sig / sig_kid / signed_at / signed_with
/ released_at / release_notes_url) instead of the orphan
sha256_url / signature_url / released_at_utc shape it
produced before. Also replaces a Unix-only
subprocess.check_output(["date", ...]) with portable
datetime.now(timezone.utc).numinix_seekmodo_show_branding() and numinix_seekmodo_brand_url()
for themes, with seekmodo_show_branding and seekmodo_brand_url
filters.AutoPromoter substate FSM is now actually wired up. Previous
versions never advanced the active+shadow → active+enforce
substate, and the SearchSwaps consulted the headline mode instead
of the effective mode, so auto_promote=true was decorative.
MODE=active now correctly bakes into enforce on healthy metrics
and demotes back to shadow on a breach.MODE=active + auto_promote=false now correctly collapses
to a hard enforce (operator opt-out of the safety hop).content collection schema. Previous releases would
have 400'd on every index.upsert.Options::SCHEMA_VERSION to force a full reindex on
upgrade.