@respira/wordpress-mcp-server 7.2.18 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/acf-tools.d.ts +0 -10
  2. package/dist/acf-tools.d.ts.map +1 -1
  3. package/dist/acf-tools.js +0 -15
  4. package/dist/acf-tools.js.map +1 -1
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +31 -36
  7. package/dist/config.js.map +1 -1
  8. package/dist/server.d.ts +0 -47
  9. package/dist/server.d.ts.map +1 -1
  10. package/dist/server.js +1155 -605
  11. package/dist/server.js.map +1 -1
  12. package/dist/wordpress-client.d.ts +61 -29
  13. package/dist/wordpress-client.d.ts.map +1 -1
  14. package/dist/wordpress-client.js +271 -148
  15. package/dist/wordpress-client.js.map +1 -1
  16. package/package.json +2 -2
  17. package/skills/activity-report-composer/README.md +38 -0
  18. package/skills/activity-report-composer/SKILL.md +167 -0
  19. package/skills/activity-report-composer/metadata.json +42 -0
  20. package/skills/brand-voice-synthesizer/README.md +43 -0
  21. package/skills/brand-voice-synthesizer/SKILL.md +259 -0
  22. package/skills/brand-voice-synthesizer/metadata.json +46 -0
  23. package/skills/build-oxygen6-page/SKILL.md +59 -0
  24. package/skills/conversion-audit/README.md +37 -0
  25. package/skills/conversion-audit/SKILL.md +185 -0
  26. package/skills/conversion-audit/metadata.json +46 -0
  27. package/skills/custom-post-type-architect/README.md +34 -0
  28. package/skills/custom-post-type-architect/SKILL.md +158 -0
  29. package/skills/custom-post-type-architect/metadata.json +39 -0
  30. package/skills/design-system-synthesizer/README.md +46 -0
  31. package/skills/design-system-synthesizer/SKILL.md +395 -0
  32. package/skills/design-system-synthesizer/metadata.json +57 -0
  33. package/skills/figma-to-elementor/SKILL.md +204 -0
  34. package/skills/html-to-bricks/README.md +43 -0
  35. package/skills/html-to-bricks/SKILL.md +135 -0
  36. package/skills/html-to-bricks/metadata.json +41 -0
  37. package/skills/internal-link-builder/SKILL.md +258 -0
  38. package/skills/internal-link-builder/metadata.json +46 -0
  39. package/skills/migrate-beaver-builder-to-bricks/SKILL.md +251 -0
  40. package/skills/migrate-beaver-builder-to-gutenberg/SKILL.md +248 -0
  41. package/skills/migrate-brizy-to-gutenberg/SKILL.md +248 -0
  42. package/skills/migrate-divi-to-breakdance/SKILL.md +312 -0
  43. package/skills/migrate-divi-to-bricks/SKILL.md +321 -0
  44. package/skills/migrate-divi-to-gutenberg/SKILL.md +328 -0
  45. package/skills/migrate-elementor-to-breakdance/SKILL.md +287 -0
  46. package/skills/migrate-elementor-to-bricks/SKILL.md +289 -0
  47. package/skills/migrate-elementor-to-gutenberg/SKILL.md +327 -0
  48. package/skills/migrate-elementor-to-oxygen/SKILL.md +300 -0
  49. package/skills/migrate-oxygen-to-breakdance/SKILL.md +235 -0
  50. package/skills/migrate-oxygen-to-bricks/SKILL.md +237 -0
  51. package/skills/migrate-thrive-architect-to-gutenberg/SKILL.md +257 -0
  52. package/skills/migrate-visual-composer-to-gutenberg/SKILL.md +261 -0
  53. package/skills/migrate-wpbakery-to-bricks/SKILL.md +336 -0
  54. package/skills/migrate-wpbakery-to-gutenberg/SKILL.md +345 -0
  55. package/skills/mobile-experience-report/SKILL.md +719 -0
  56. package/skills/mobile-experience-report/telemetry.ts +137 -0
  57. package/skills/page-template-library/README.md +41 -0
  58. package/skills/page-template-library/SKILL.md +175 -0
  59. package/skills/page-template-library/metadata.json +44 -0
  60. package/skills/prime-the-agent/README.md +49 -0
  61. package/skills/prime-the-agent/SKILL.md +225 -0
  62. package/skills/prime-the-agent/metadata.json +46 -0
  63. package/skills/respira-setup-assistant/SKILL.md +386 -0
  64. package/skills/respira-setup-assistant/metadata.json +36 -0
  65. package/skills/seo-aeo-amplifier/README.md +61 -0
  66. package/skills/seo-aeo-amplifier/SKILL.md +277 -0
  67. package/skills/seo-aeo-amplifier/metadata.json +62 -0
  68. package/skills/stale-content-detector/README.md +41 -0
  69. package/skills/stale-content-detector/SKILL.md +156 -0
  70. package/skills/stale-content-detector/metadata.json +43 -0
  71. package/skills/technical-debt-audit/SKILL.md +691 -0
  72. package/skills/technical-debt-audit/telemetry.ts +142 -0
  73. package/skills/woocommerce-health-check/SKILL.md +719 -0
  74. package/skills/woocommerce-health-check/telemetry.ts +137 -0
  75. package/skills/wordpress-ai-image-optimizer/README.md +49 -0
  76. package/skills/wordpress-ai-image-optimizer/SKILL.md +232 -0
  77. package/skills/wordpress-ai-image-optimizer/metadata.json +63 -0
  78. package/skills/wordpress-site-dna/README.md +81 -0
  79. package/skills/wordpress-site-dna/SKILL.md +669 -0
  80. package/skills/wordpress-site-dna/telemetry.ts +137 -0
  81. package/dist/__tests__/acf-tool-routing.test.d.ts +0 -2
  82. package/dist/__tests__/acf-tool-routing.test.d.ts.map +0 -1
  83. package/dist/__tests__/acf-tool-routing.test.js +0 -37
  84. package/dist/__tests__/acf-tool-routing.test.js.map +0 -1
  85. package/dist/__tests__/cowork-bootstrap.test.d.ts +0 -2
  86. package/dist/__tests__/cowork-bootstrap.test.d.ts.map +0 -1
  87. package/dist/__tests__/cowork-bootstrap.test.js +0 -61
  88. package/dist/__tests__/cowork-bootstrap.test.js.map +0 -1
@@ -6,7 +6,7 @@
6
6
  import axios from 'axios';
7
7
  import FormData from 'form-data';
8
8
  import { readFileSync, existsSync, statSync } from 'node:fs';
9
- import { dirname, resolve, extname, basename } from 'node:path';
9
+ import { dirname, resolve, extname } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import { Agent as HttpAgent } from 'node:http';
12
12
  import { getRespiraAgentHeaders } from './agent-signature.js';
@@ -37,15 +37,6 @@ export class WordPressClient {
37
37
  insecureTLS = false;
38
38
  negotiatedApiVersion = null;
39
39
  negotiationPromise = null;
40
- /**
41
- * The real error from the last failed `/wp-json/respira/v2/status` probe.
42
- * Bug 8d2c3331: getApiVersion() used to swallow this entirely, so a 401
43
- * (missing/invalid API key) or 403 (missing v2 scope) — both of which prove
44
- * the route IS live and reachable — looked identical to a genuine 404 /
45
- * network failure. ensureV2() reads this to surface the real cause instead
46
- * of a misleading "not available" boilerplate.
47
- */
48
- negotiationError = null;
49
40
  // Version compatibility check — populated lazily on first API call.
50
41
  compatibilityChecked = false;
51
42
  compatibilityPromise = null;
@@ -555,20 +546,11 @@ export class WordPressClient {
555
546
  const response = await this.rootClient.get('/wp-json/respira/v2/status');
556
547
  if (response.status >= 200 && response.status < 300) {
557
548
  this.negotiatedApiVersion = 'v2';
558
- this.negotiationError = null;
559
549
  return 'v2';
560
550
  }
561
551
  }
562
- catch (err) {
563
- // Bug 8d2c3331: capture the real reason instead of discarding it.
564
- // The response interceptor (errorInterceptor -> handleError) has
565
- // already turned this into a descriptive Error by this point (e.g.
566
- // "Authentication failed: No API key provided..." for a 401, or a
567
- // scope-denied message for a 403) — both of which mean the v2 route
568
- // IS live and reachable, which is the opposite of what
569
- // ensureV2()'s old generic message implied. Fall back to v1 either
570
- // way (so genuinely v1-only sites keep working) but remember why.
571
- this.negotiationError = err instanceof Error ? err : new Error(String(err));
552
+ catch {
553
+ // Ignore and fall back to v1.
572
554
  }
573
555
  this.negotiatedApiVersion = 'v1';
574
556
  return 'v1';
@@ -588,29 +570,7 @@ export class WordPressClient {
588
570
  async ensureV2() {
589
571
  const version = await this.getApiVersion();
590
572
  if (version !== 'v2') {
591
- const err = this.negotiationError;
592
- const errName = err instanceof Error ? err.name : undefined;
593
- // Bug 8d2c3331: a 401/403 from the v2 status probe means the route IS
594
- // live and reachable — WordPress itself rejected the request's
595
- // credentials/scope. That is a completely different failure than
596
- // "route not found" (404, HTML-instead-of-JSON, DNS/network errors),
597
- // so it needs a different message: none of the permalinks/WAF/plugin-
598
- // version causes below apply, and repeating them just sends the next
599
- // person chasing the wrong fix.
600
- const looksLikeAuthOrScopeRejection = typeof errName === 'string' &&
601
- (errName.startsWith('respira_') || errName === 'http_401' || errName === 'http_403');
602
- if (err && looksLikeAuthOrScopeRejection) {
603
- throw new Error(`respira/v2 rejected this request's credentials: ${err.message}\n\n` +
604
- 'The v2 route is live and reachable on this site — this is an authentication or ' +
605
- 'permission problem, not a permalinks/WAF/plugin-version problem. v1 tools may still ' +
606
- 'work because some v1 routes have looser permission checks.\n\n' +
607
- `To confirm: open ${this.siteConfig.url}/wp-json/respira/v2/status in a browser with no ` +
608
- 'headers. A JSON 401/403 response (not a 404 or an HTML page) confirms the route works ' +
609
- 'and this is an auth/scope issue — check that the configured API key is correct, active, ' +
610
- 'and has write permission for this site.');
611
- }
612
573
  throw new Error('respira/v2 is not available on this site.\n\n' +
613
- (err ? `Underlying error from the v2 status probe: ${err.message}\n\n` : '') +
614
574
  'Common causes:\n' +
615
575
  '• A security plugin (Wordfence, Sucuri, iThemes Security) is blocking the /wp-json/respira/v2/ path — whitelist that path in the plugin settings.\n' +
616
576
  '• WordPress permalinks are set to "Plain" — go to Settings → Permalinks and choose any option except Plain.\n' +
@@ -805,13 +765,7 @@ export class WordPressClient {
805
765
  return new Error(`Site not reachable: ${this.siteConfig.url} returned a ${status} response from the web server (not WordPress).\n\n${hint}`);
806
766
  }
807
767
  const reason = data?.message || data?.code || 'Invalid API key';
808
- const authError = new Error(`Authentication failed: ${reason}`);
809
- // Bug 8d2c3331: stamp the WP_Error code (e.g. respira_missing_api_key)
810
- // onto .name so callers like ensureV2() can tell "the route rejected my
811
- // auth" (the route IS live) apart from "the route doesn't exist"
812
- // instead of collapsing both into the same generic message.
813
- authError.name = typeof data?.code === 'string' && data.code ? data.code.slice(0, 80) : 'http_401';
814
- return authError;
768
+ return new Error(`Authentication failed: ${reason}`);
815
769
  }
816
770
  else if (status === 429) {
817
771
  return new Error(`Rate limit exceeded. Please wait before making more requests.`);
@@ -906,6 +860,13 @@ export class WordPressClient {
906
860
  if (detail) {
907
861
  apiError.message = `${apiError.message} [attempted: ${detail}]`;
908
862
  }
863
+ // Woo add-on v3.0 added many routes (brands, variations, coupons,
864
+ // customers, refunds, ai-readiness). A missing /woocommerce/ route
865
+ // on a licensed site almost always means the installed add-on
866
+ // predates the tool.
867
+ if (reqUrl.includes('/woocommerce/')) {
868
+ apiError.message = `${apiError.message} This tool may require a newer Respira WooCommerce Add-on (v3.0+). Update it in wp-admin under Plugins.`;
869
+ }
909
870
  }
910
871
  return apiError;
911
872
  }
@@ -1558,19 +1519,10 @@ export class WordPressClient {
1558
1519
  clearTimeout(killer);
1559
1520
  }
1560
1521
  }
1561
- else if (file.startsWith('/') ||
1562
- file.startsWith('./') ||
1563
- file.startsWith('../') ||
1564
- file.startsWith('~') ||
1565
- /^[A-Za-z]:[\\/]/.test(file) || // Windows drive path: C:\... or C:/...
1566
- file.startsWith('\\\\') // Windows UNC path: \\server\share\...
1567
- ) {
1568
- // Local file path — preflight size before reading the whole file into memory.
1569
- // Without the Windows branches above, a path like C:\Users\me\img.jpeg fell
1570
- // through to the base64 branch below and was decoded into garbage bytes,
1571
- // surfacing to the agent as "Could not connect — no response received".
1522
+ else if (file.startsWith('/') || file.startsWith('./') || file.startsWith('../') || file.startsWith('~')) {
1523
+ // Local file path — preflight size before reading the whole file into memory
1572
1524
  const resolvedPath = file.startsWith('~')
1573
- ? file.replace(/^~/, process.env.HOME || process.env.USERPROFILE || '')
1525
+ ? file.replace(/^~/, process.env.HOME || '')
1574
1526
  : resolve(file);
1575
1527
  if (!existsSync(resolvedPath)) {
1576
1528
  throw new Error(`File not found: ${resolvedPath}`);
@@ -1592,11 +1544,6 @@ export class WordPressClient {
1592
1544
  catch (err) {
1593
1545
  throw new Error(`Failed to read ${resolvedPath}: ${err?.message || String(err)}`);
1594
1546
  }
1595
- if (!filename) {
1596
- // Caller passed only a file path (e.g. upload_media({ file: "C:\\...\\img.jpg" }))
1597
- // with no separate filename — derive it from the path basename.
1598
- filename = basename(resolvedPath);
1599
- }
1600
1547
  if (!mimeType) {
1601
1548
  const ext = extname(resolvedPath).toLowerCase();
1602
1549
  const mimeMap = {
@@ -1960,7 +1907,7 @@ export class WordPressClient {
1960
1907
  /**
1961
1908
  * Inject page builder content
1962
1909
  */
1963
- async injectBuilderContent(builder, pageId, content, diviVersion, editTarget, mode, confirmReplace, targetPath, position) {
1910
+ async injectBuilderContent(builder, pageId, content, diviVersion, editTarget, mode, confirmReplace) {
1964
1911
  if (builder.toLowerCase() === 'divi') {
1965
1912
  if (!diviVersion) {
1966
1913
  const existing = await this.extractBuilderContent('divi', pageId);
@@ -2002,46 +1949,13 @@ export class WordPressClient {
2002
1949
  // here so every downstream caller sees the same shape: pass the inner
2003
1950
  // array when the caller wrapped it, otherwise pass through verbatim.
2004
1951
  const normalisedContent = this.unwrapBuilderContent(content);
2005
- // inject is the heaviest write Respira makes (full complexify tree-walk +
2006
- // CSS regen + a render-validation loopback), so on a large page behind a
2007
- // host/edge request-time limit it can run well past the 30s default and
2008
- // hang to the MCP client's multi-minute ceiling. Match the restoreSnapshot
2009
- // precedent: allow a 90s window, and on timeout surface an actionable error
2010
- // (the write may have landed server-side) instead of a bare hang. Reported
2011
- // on a WP Engine + Cloudflare site, a 12-section Elementor inject (9a8fc2fd).
2012
- try {
2013
- const response = await this.client.post(`/builder/${builder}/inject/${pageId}`, {
2014
- content: normalisedContent,
2015
- editTarget,
2016
- mode: mode || 'replace',
2017
- confirm_replace: confirmReplace === true,
2018
- // Chunked build: when target_path is given, the plugin inserts the
2019
- // content as children of the container at that bracket path
2020
- // (e.g. "sections[0].rows[1].columns[0]") instead of at page top
2021
- // level. Lets an agent build a large section in small calls when the
2022
- // whole tree exceeds the AI client's per-tool-call payload cap.
2023
- // Only forwarded when set, so default behaviour is unchanged.
2024
- ...(targetPath && targetPath.trim() !== ''
2025
- ? { target_path: targetPath.trim(), position: typeof position === 'number' ? position : -1 }
2026
- : {}),
2027
- }, { timeout: 90_000 });
2028
- return response.data;
2029
- }
2030
- catch (err) {
2031
- const isTimeout = err?.code === 'ECONNABORTED' ||
2032
- err?.code === 'ETIMEDOUT' ||
2033
- /timeout/i.test(String(err?.message || ''));
2034
- if (!isTimeout) {
2035
- throw err;
2036
- }
2037
- throw new Error(`inject_builder_content exceeded 90s on the "${builder}" page ${pageId}. This is almost ` +
2038
- `always a large page hitting a host or edge request-time limit (for example Cloudflare or ` +
2039
- `WP Engine), not a Respira error, and the write may have completed on the server — verify with ` +
2040
- `extract_builder_content before retrying. To stay under the limit, build the page in smaller ` +
2041
- `pieces: send the first section with mode:"append", then append the rest one or two sections ` +
2042
- `per call (or use add_section / add_html). If it must go in one call, ask the host to confirm ` +
2043
- `/wp-json/respira/* is not CDN-cached or WAF-challenged and to raise PHP max_execution_time.`);
2044
- }
1952
+ const response = await this.client.post(`/builder/${builder}/inject/${pageId}`, {
1953
+ content: normalisedContent,
1954
+ editTarget,
1955
+ mode: mode || 'replace',
1956
+ confirm_replace: confirmReplace === true,
1957
+ });
1958
+ return response.data;
2045
1959
  }
2046
1960
  /**
2047
1961
  * Unwrap the common `{content: array}` shape that round-tripped extracts
@@ -2134,24 +2048,17 @@ export class WordPressClient {
2134
2048
  /**
2135
2049
  * Update a specific module in a page builder page
2136
2050
  */
2137
- async updateModule(builder, pageId, moduleIdentifier, updates, editTarget, confirmLiveEdit) {
2051
+ async updateModule(builder, pageId, moduleIdentifier, updates, editTarget) {
2138
2052
  try {
2139
2053
  // Increase timeout for large content updates
2140
2054
  const timeout = updates.content && updates.content.length > 50000
2141
2055
  ? 60000 // 60 seconds for large content
2142
2056
  : 30000; // 30 seconds default
2143
- const payload = {
2057
+ const response = await this.client.put(`/builder/${builder}/modules/${pageId}`, {
2144
2058
  module_identifier: moduleIdentifier,
2145
2059
  updates,
2146
2060
  editTarget,
2147
- };
2148
- // e402506e (Christian): forward the live-edit confirmation so the
2149
- // confirmation_required handshake completes through MCP. snake_case to
2150
- // match the plugin's element/module write gate.
2151
- if (confirmLiveEdit !== undefined) {
2152
- payload.confirm_live_edit = confirmLiveEdit;
2153
- }
2154
- const response = await this.client.put(`/builder/${builder}/modules/${pageId}`, payload, { timeout });
2061
+ }, { timeout });
2155
2062
  return response.data;
2156
2063
  }
2157
2064
  catch (error) {
@@ -2279,7 +2186,7 @@ export class WordPressClient {
2279
2186
  /**
2280
2187
  * Apply v2 builder patch operations.
2281
2188
  */
2282
- async applyBuilderPatch(builder, postId, operations, include, editTarget, confirmLiveEdit) {
2189
+ async applyBuilderPatch(builder, postId, operations, include, editTarget) {
2283
2190
  await this.ensureV2();
2284
2191
  const payload = { operations };
2285
2192
  if (include) {
@@ -2288,10 +2195,6 @@ export class WordPressClient {
2288
2195
  if (editTarget) {
2289
2196
  payload.editTarget = editTarget;
2290
2197
  }
2291
- // e402506e (Christian): forward live-edit confirmation for the handshake.
2292
- if (confirmLiveEdit !== undefined) {
2293
- payload.confirm_live_edit = confirmLiveEdit;
2294
- }
2295
2198
  const response = await this.rootClient.post(`/wp-json/respira/v2/builder/${builder}/patch/${postId}`, payload);
2296
2199
  return response.data;
2297
2200
  }
@@ -2336,24 +2239,6 @@ export class WordPressClient {
2336
2239
  const response = await this.rootClient.request({ method: 'PATCH', url, data: { content } });
2337
2240
  return response.data;
2338
2241
  }
2339
- /**
2340
- * Tail wp-content/debug.log (or the site's configured WP_DEBUG_LOG path)
2341
- * via GET /respira/v2/debug-log. Read-only, no path parameter accepted —
2342
- * the plugin resolves the log location itself and rejects anything
2343
- * outside wp-content / the configured custom path.
2344
- */
2345
- async readDebugLog(opts = {}) {
2346
- const params = {};
2347
- if (typeof opts.lines === 'number')
2348
- params.lines = opts.lines;
2349
- if (opts.level)
2350
- params.level = opts.level;
2351
- if (opts.since)
2352
- params.since = opts.since;
2353
- if (opts.grep)
2354
- params.grep = opts.grep;
2355
- return await this.callRestV2('GET', '/debug-log', params);
2356
- }
2357
2242
  /**
2358
2243
  * Get site name (for logging)
2359
2244
  */
@@ -2366,13 +2251,6 @@ export class WordPressClient {
2366
2251
  getSiteUrl() {
2367
2252
  return this.siteConfig.url;
2368
2253
  }
2369
- /**
2370
- * Get the site's API key. Used by the account site-list refresh to pick a
2371
- * live `respira_site_` token to authenticate the refresh call with.
2372
- */
2373
- getApiKey() {
2374
- return this.siteConfig.apiKey;
2375
- }
2376
2254
  /**
2377
2255
  * Get approvals URL in WordPress admin.
2378
2256
  *
@@ -3127,6 +3005,251 @@ export class WordPressClient {
3127
3005
  const response = await this.client.get('/woocommerce/reports/sales', { params: params || {} });
3128
3006
  return response.data;
3129
3007
  }
3008
+ // WooCommerce Add-on v3.0 — brands
3009
+ async woocommerceListBrands(params) {
3010
+ const response = await this.client.get('/woocommerce/brands', { params: params || {} });
3011
+ return response.data;
3012
+ }
3013
+ async woocommerceGetBrand(id) {
3014
+ const response = await this.client.get(`/woocommerce/brands/${id}`);
3015
+ return response.data;
3016
+ }
3017
+ async woocommerceCreateBrand(data) {
3018
+ const response = await this.client.post('/woocommerce/brands', data);
3019
+ return response.data;
3020
+ }
3021
+ async woocommerceUpdateBrand(id, data) {
3022
+ const response = await this.client.put(`/woocommerce/brands/${id}`, data);
3023
+ return response.data;
3024
+ }
3025
+ async woocommerceDeleteBrand(id) {
3026
+ const response = await this.client.delete(`/woocommerce/brands/${id}`);
3027
+ return response.data;
3028
+ }
3029
+ // WooCommerce Add-on v3.0 — catalog operations
3030
+ async woocommerceListProductsAdvanced(params) {
3031
+ const response = await this.client.get('/woocommerce/products/advanced', { params: params || {} });
3032
+ return response.data;
3033
+ }
3034
+ async woocommerceFindProduct(params) {
3035
+ const response = await this.client.get('/woocommerce/products/search', { params });
3036
+ return response.data;
3037
+ }
3038
+ async woocommerceCatalogHealth() {
3039
+ const response = await this.client.get('/woocommerce/catalog/health');
3040
+ return response.data;
3041
+ }
3042
+ async woocommerceBulkUpdateProducts(data) {
3043
+ const response = await this.client.post('/woocommerce/products/bulk-update', data);
3044
+ return response.data;
3045
+ }
3046
+ // WooCommerce Add-on v3.0 — pricing
3047
+ async woocommerceBulkUpdatePrices(data) {
3048
+ const response = await this.client.post('/woocommerce/pricing/bulk-update', data);
3049
+ return response.data;
3050
+ }
3051
+ async woocommerceScheduleSale(data) {
3052
+ const response = await this.client.post('/woocommerce/pricing/schedule-sale', data);
3053
+ return response.data;
3054
+ }
3055
+ async woocommerceRevertPricing(data) {
3056
+ const response = await this.client.post('/woocommerce/pricing/revert', data);
3057
+ return response.data;
3058
+ }
3059
+ // WooCommerce Add-on v3.0 — inventory
3060
+ async woocommerceBulkUpdateStock(data) {
3061
+ const response = await this.client.post('/woocommerce/inventory/bulk-update', data);
3062
+ return response.data;
3063
+ }
3064
+ async woocommerceFindLowStock(params) {
3065
+ const response = await this.client.get('/woocommerce/inventory/low-stock', { params: params || {} });
3066
+ return response.data;
3067
+ }
3068
+ // WooCommerce Add-on v3.0 — storefront intelligence
3069
+ async woocommerceAnalyzeShopPage(params) {
3070
+ const response = await this.client.get('/woocommerce/storefront/analyze-shop', { params: params || {} });
3071
+ return response.data;
3072
+ }
3073
+ async woocommerceAnalyzeProductPage(params) {
3074
+ const response = await this.client.get('/woocommerce/storefront/analyze-product', { params });
3075
+ return response.data;
3076
+ }
3077
+ async woocommerceUpdateProductCardLayout(data) {
3078
+ const response = await this.client.post('/woocommerce/storefront/update-card-layout', data);
3079
+ return response.data;
3080
+ }
3081
+ async woocommerceAddLowStockBadge(data) {
3082
+ const response = await this.client.post('/woocommerce/storefront/add-low-stock-badge', data);
3083
+ return response.data;
3084
+ }
3085
+ async woocommerceAddSaleBadge(data) {
3086
+ const response = await this.client.post('/woocommerce/storefront/add-sale-badge', data);
3087
+ return response.data;
3088
+ }
3089
+ async woocommerceUpdateCheckoutLayout(data) {
3090
+ const response = await this.client.post('/woocommerce/storefront/update-checkout-layout', data);
3091
+ return response.data;
3092
+ }
3093
+ // WooCommerce Add-on v3.0 — variations and attributes
3094
+ async woocommerceSetProductAttributes(id, data) {
3095
+ const response = await this.client.post(`/woocommerce/products/${id}/attributes`, data);
3096
+ return response.data;
3097
+ }
3098
+ async woocommerceGenerateVariations(id, data) {
3099
+ const response = await this.client.post(`/woocommerce/products/${id}/variations/generate`, data);
3100
+ return response.data;
3101
+ }
3102
+ async woocommerceCreateVariation(id, data) {
3103
+ const response = await this.client.post(`/woocommerce/products/${id}/variations`, data);
3104
+ return response.data;
3105
+ }
3106
+ async woocommerceCreateGlobalAttribute(data) {
3107
+ const response = await this.client.post('/woocommerce/attributes', data);
3108
+ return response.data;
3109
+ }
3110
+ async woocommerceSetVariationGallery(id, data) {
3111
+ const response = await this.client.put(`/woocommerce/variations/${id}/gallery`, data);
3112
+ return response.data;
3113
+ }
3114
+ // WooCommerce Add-on v3.0 — coupons and customers
3115
+ async woocommerceListCoupons(params) {
3116
+ const response = await this.client.get('/woocommerce/coupons', { params: params || {} });
3117
+ return response.data;
3118
+ }
3119
+ async woocommerceCreateCoupon(data) {
3120
+ const response = await this.client.post('/woocommerce/coupons', data);
3121
+ return response.data;
3122
+ }
3123
+ async woocommerceUpdateCoupon(id, data) {
3124
+ const response = await this.client.put(`/woocommerce/coupons/${id}`, data);
3125
+ return response.data;
3126
+ }
3127
+ async woocommerceCreateCustomer(data) {
3128
+ const response = await this.client.post('/woocommerce/customers', data);
3129
+ return response.data;
3130
+ }
3131
+ async woocommerceUpdateCustomer(id, data) {
3132
+ const response = await this.client.put(`/woocommerce/customers/${id}`, data);
3133
+ return response.data;
3134
+ }
3135
+ // WooCommerce Add-on v3.0 — order notes and refunds
3136
+ async woocommerceAddOrderNote(id, data) {
3137
+ const response = await this.client.post(`/woocommerce/orders/${id}/notes`, data);
3138
+ return response.data;
3139
+ }
3140
+ async woocommerceGetOrderRefunds(id) {
3141
+ const response = await this.client.get(`/woocommerce/orders/${id}/refunds`);
3142
+ return response.data;
3143
+ }
3144
+ async woocommerceCreateOrderRefund(id, data) {
3145
+ const response = await this.client.post(`/woocommerce/orders/${id}/refunds`, data);
3146
+ return response.data;
3147
+ }
3148
+ // WooCommerce Add-on v3.1 — Subscriptions / Bookings / Memberships
3149
+ async woocommerceListSubscriptions(params) {
3150
+ const response = await this.client.get('/woocommerce/subscriptions', { params: params || {} });
3151
+ return response.data;
3152
+ }
3153
+ async woocommerceGetSubscription(id) {
3154
+ const response = await this.client.get(`/woocommerce/subscriptions/${id}`);
3155
+ return response.data;
3156
+ }
3157
+ async woocommerceUpdateSubscriptionStatus(id, data) {
3158
+ const response = await this.client.put(`/woocommerce/subscriptions/${id}/status`, data || {});
3159
+ return response.data;
3160
+ }
3161
+ async woocommerceUpdateSubscriptionDates(id, data) {
3162
+ const response = await this.client.put(`/woocommerce/subscriptions/${id}/dates`, data || {});
3163
+ return response.data;
3164
+ }
3165
+ async woocommerceAddSubscriptionNote(id, data) {
3166
+ const response = await this.client.post(`/woocommerce/subscriptions/${id}/notes`, data || {});
3167
+ return response.data;
3168
+ }
3169
+ async woocommerceListBookings(params) {
3170
+ const response = await this.client.get('/woocommerce/bookings', { params: params || {} });
3171
+ return response.data;
3172
+ }
3173
+ async woocommerceGetBooking(id) {
3174
+ const response = await this.client.get(`/woocommerce/bookings/${id}`);
3175
+ return response.data;
3176
+ }
3177
+ async woocommerceUpdateBookingStatus(id, data) {
3178
+ const response = await this.client.put(`/woocommerce/bookings/${id}/status`, data || {});
3179
+ return response.data;
3180
+ }
3181
+ async woocommerceRescheduleBooking(id, data) {
3182
+ const response = await this.client.put(`/woocommerce/bookings/${id}/reschedule`, data || {});
3183
+ return response.data;
3184
+ }
3185
+ async woocommerceListMembershipPlans(params) {
3186
+ const response = await this.client.get('/woocommerce/membership-plans', { params: params || {} });
3187
+ return response.data;
3188
+ }
3189
+ async woocommerceListMemberships(params) {
3190
+ const response = await this.client.get('/woocommerce/memberships', { params: params || {} });
3191
+ return response.data;
3192
+ }
3193
+ async woocommerceGetMembership(id) {
3194
+ const response = await this.client.get(`/woocommerce/memberships/${id}`);
3195
+ return response.data;
3196
+ }
3197
+ async woocommerceUpdateMembershipStatus(id, data) {
3198
+ const response = await this.client.put(`/woocommerce/memberships/${id}/status`, data || {});
3199
+ return response.data;
3200
+ }
3201
+ async woocommerceSetMembershipEndDate(id, data) {
3202
+ const response = await this.client.put(`/woocommerce/memberships/${id}/end-date`, data || {});
3203
+ return response.data;
3204
+ }
3205
+ // WooCommerce Add-on v3.1 — assisted checkout
3206
+ async woocommerceCreateCartLink(data) {
3207
+ const response = await this.client.post('/woocommerce/cart-links', data);
3208
+ return response.data;
3209
+ }
3210
+ async woocommerceAgentOrdersReport(params) {
3211
+ const response = await this.client.get('/woocommerce/reports/agent-orders', { params: params || {} });
3212
+ return response.data;
3213
+ }
3214
+ // WooCommerce Add-on v3.1 — readiness autofix
3215
+ async woocommerceReadinessFixlist(params) {
3216
+ const response = await this.client.get('/woocommerce/catalog/readiness-fixlist', { params: params || {} });
3217
+ return response.data;
3218
+ }
3219
+ async woocommerceSetImageAlt(id, data) {
3220
+ const response = await this.client.post(`/woocommerce/products/${id}/image-alt`, data);
3221
+ return response.data;
3222
+ }
3223
+ // WooCommerce Add-on v3.1 — product feeds
3224
+ async woocommerceConfigureFeed(data) {
3225
+ const response = await this.client.post('/woocommerce/feeds/config', data || {});
3226
+ return response.data;
3227
+ }
3228
+ async woocommerceGenerateFeed(data) {
3229
+ const response = await this.client.post('/woocommerce/feeds/generate', data || {});
3230
+ return response.data;
3231
+ }
3232
+ async woocommerceGetFeedStatus() {
3233
+ const response = await this.client.get('/woocommerce/feeds/status');
3234
+ return response.data;
3235
+ }
3236
+ async woocommerceValidateFeed(params) {
3237
+ const response = await this.client.get('/woocommerce/feeds/validate', { params: params || {} });
3238
+ return response.data;
3239
+ }
3240
+ async woocommerceSetFeedCategoryMapping(data) {
3241
+ const response = await this.client.post('/woocommerce/feeds/category-mapping', data || {});
3242
+ return response.data;
3243
+ }
3244
+ // WooCommerce Add-on v3.0 — AI readiness
3245
+ async woocommerceProductAiReadiness(id) {
3246
+ const response = await this.client.get(`/woocommerce/products/${id}/ai-readiness`);
3247
+ return response.data;
3248
+ }
3249
+ async woocommerceCatalogAiReadiness(params) {
3250
+ const response = await this.client.get('/woocommerce/catalog/ai-readiness', { params: params || {} });
3251
+ return response.data;
3252
+ }
3130
3253
  // --- Accessibility ---
3131
3254
  async listAccessibilityScans(opts) {
3132
3255
  const response = await this.client.get('/accessibility/scans', {