@ainyc/canonry 1.29.0 → 1.31.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.
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  setGoogleAuthConfig,
22
22
  showFirstRunNotice,
23
23
  trackEvent
24
- } from "./chunk-IWUQVYU3.js";
24
+ } from "./chunk-YW4IZ34Z.js";
25
25
 
26
26
  // src/cli.ts
27
27
  import { pathToFileURL } from "url";
@@ -510,6 +510,9 @@ var ApiClient = class {
510
510
  async wordpressSetMeta(project, body) {
511
511
  return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/page/meta`, body);
512
512
  }
513
+ async wordpressBulkSetMeta(project, body) {
514
+ return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/pages/meta/bulk`, body);
515
+ }
513
516
  async wordpressSchema(project, slug, env) {
514
517
  const params = new URLSearchParams({ slug });
515
518
  if (env) params.set("env", env);
@@ -518,6 +521,18 @@ var ApiClient = class {
518
521
  async wordpressSetSchema(project, body) {
519
522
  return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/manual`, body);
520
523
  }
524
+ async wordpressSchemaDeploy(project, body) {
525
+ return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/schema/deploy`, body);
526
+ }
527
+ async wordpressSchemaStatus(project, env) {
528
+ const params = new URLSearchParams();
529
+ if (env) params.set("env", env);
530
+ const qs = params.toString();
531
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/schema/status${qs ? `?${qs}` : ""}`);
532
+ }
533
+ async wordpressOnboard(project, body) {
534
+ return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/onboard`, body);
535
+ }
521
536
  async wordpressLlmsTxt(project, env) {
522
537
  const qs = env ? `?env=${encodeURIComponent(env)}` : "";
523
538
  return this.request("GET", `/projects/${encodeURIComponent(project)}/wordpress/llms-txt${qs}`);
@@ -1342,7 +1357,8 @@ async function gaSync(project, opts) {
1342
1357
  return;
1343
1358
  }
1344
1359
  console.log(`GA4 sync complete for "${project}".`);
1345
- console.log(` Rows synced: ${result.rowCount}`);
1360
+ console.log(` Page rows: ${result.rowCount}`);
1361
+ console.log(` AI rows: ${result.aiReferralCount}`);
1346
1362
  console.log(` Period: ${result.days} days`);
1347
1363
  console.log(` Synced at: ${result.syncedAt}`);
1348
1364
  }
@@ -1355,7 +1371,7 @@ async function gaTraffic(project, opts) {
1355
1371
  console.log(JSON.stringify(result, null, 2));
1356
1372
  return;
1357
1373
  }
1358
- if (result.topPages.length === 0) {
1374
+ if (result.topPages.length === 0 && result.aiReferrals.length === 0) {
1359
1375
  console.log('No GA4 traffic data. Run "canonry ga sync <project>" first.');
1360
1376
  return;
1361
1377
  }
@@ -1365,14 +1381,28 @@ async function gaTraffic(project, opts) {
1365
1381
  console.log(` Organic Sessions: ${result.totalOrganicSessions}`);
1366
1382
  console.log(` Total Users: ${result.totalUsers}`);
1367
1383
  console.log();
1368
- const pageWidth = Math.min(60, Math.max(15, ...result.topPages.map((r) => r.landingPage.length)));
1369
- console.log(` ${"LANDING PAGE".padEnd(pageWidth)} ${"SESSIONS".padEnd(10)}${"ORGANIC".padEnd(10)}${"USERS".padEnd(8)}`);
1370
- console.log(` ${"\u2500".repeat(pageWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
1371
- for (const row of result.topPages) {
1372
- const page = row.landingPage.length > pageWidth ? row.landingPage.slice(0, pageWidth - 3) + "..." : row.landingPage;
1373
- console.log(
1374
- ` ${page.padEnd(pageWidth)} ${String(row.sessions).padEnd(10)}${String(row.organicSessions).padEnd(10)}${String(row.users).padEnd(8)}`
1375
- );
1384
+ if (result.aiReferrals.length > 0) {
1385
+ console.log(" AI REFERRAL SOURCES");
1386
+ console.log(` ${"SOURCE".padEnd(25)} ${"MEDIUM".padEnd(15)} ${"SESSIONS".padEnd(10)}${"USERS".padEnd(8)}`);
1387
+ console.log(` ${"\u2500".repeat(25)} ${"\u2500".repeat(15)} ${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
1388
+ for (const ref of result.aiReferrals) {
1389
+ console.log(
1390
+ ` ${ref.source.padEnd(25)} ${ref.medium.padEnd(15)} ${String(ref.sessions).padEnd(10)}${String(ref.users).padEnd(8)}`
1391
+ );
1392
+ }
1393
+ console.log();
1394
+ }
1395
+ if (result.topPages.length > 0) {
1396
+ const pageWidth = Math.min(60, Math.max(15, ...result.topPages.map((r) => r.landingPage.length)));
1397
+ console.log(` TOP LANDING PAGES`);
1398
+ console.log(` ${"PAGE".padEnd(pageWidth)} ${"SESSIONS".padEnd(10)}${"ORGANIC".padEnd(10)}${"USERS".padEnd(8)}`);
1399
+ console.log(` ${"\u2500".repeat(pageWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
1400
+ for (const row of result.topPages) {
1401
+ const page = row.landingPage.length > pageWidth ? row.landingPage.slice(0, pageWidth - 3) + "..." : row.landingPage;
1402
+ console.log(
1403
+ ` ${page.padEnd(pageWidth)} ${String(row.sessions).padEnd(10)}${String(row.organicSessions).padEnd(10)}${String(row.users).padEnd(8)}`
1404
+ );
1405
+ }
1376
1406
  }
1377
1407
  if (result.lastSyncedAt) {
1378
1408
  console.log(`
@@ -3762,11 +3792,10 @@ function printRunDetail(run) {
3762
3792
  if (run.finishedAt) console.log(` Finished: ${run.finishedAt}`);
3763
3793
  if (run.createdAt) console.log(` Created: ${run.createdAt}`);
3764
3794
  if (run.error) console.log(` Error: ${run.error}`);
3765
- const snapshots = run.snapshots;
3766
- if (snapshots && snapshots.length > 0) {
3795
+ if (run.snapshots && run.snapshots.length > 0) {
3767
3796
  console.log(`
3768
- Snapshots: ${snapshots.length}`);
3769
- for (const s of snapshots) {
3797
+ Snapshots: ${run.snapshots.length}`);
3798
+ for (const s of run.snapshots) {
3770
3799
  const state = s.citationState === "cited" ? " cited " : " not-cited";
3771
3800
  const modelLabel = s.model ? ` (${s.model})` : "";
3772
3801
  console.log(` ${state} ${s.provider}${modelLabel} ${s.keyword}`);
@@ -5810,6 +5839,88 @@ async function wordpressSetMeta(project, body) {
5810
5839
  `);
5811
5840
  printPageDetail(result);
5812
5841
  }
5842
+ async function wordpressBulkSetMeta(project, opts) {
5843
+ const fs7 = await import("fs/promises");
5844
+ const path5 = await import("path");
5845
+ const filePath = path5.resolve(opts.from);
5846
+ let raw;
5847
+ try {
5848
+ raw = await fs7.readFile(filePath, "utf8");
5849
+ } catch {
5850
+ throw new CliError({
5851
+ code: "FILE_READ_ERROR",
5852
+ message: `Cannot read file: ${filePath}`,
5853
+ displayMessage: `Error: cannot read file "${opts.from}". Check the path and permissions.`,
5854
+ details: { path: filePath }
5855
+ });
5856
+ }
5857
+ let parsed;
5858
+ try {
5859
+ parsed = JSON.parse(raw);
5860
+ } catch {
5861
+ throw new CliError({
5862
+ code: "INVALID_JSON",
5863
+ message: `File is not valid JSON: ${filePath}`,
5864
+ displayMessage: `Error: "${opts.from}" is not valid JSON.`,
5865
+ details: { path: filePath }
5866
+ });
5867
+ }
5868
+ const entries = Object.entries(parsed).map(([slug, meta]) => ({
5869
+ slug,
5870
+ title: meta.title,
5871
+ description: meta.description,
5872
+ noindex: meta.noindex
5873
+ }));
5874
+ if (entries.length === 0) {
5875
+ throw new CliError({
5876
+ code: "EMPTY_META_FILE",
5877
+ message: "Meta file contains no entries",
5878
+ displayMessage: `Error: "${opts.from}" contains no entries. Expected JSON object keyed by slug.`,
5879
+ details: { path: filePath }
5880
+ });
5881
+ }
5882
+ const client = getClient17();
5883
+ const result = await client.wordpressBulkSetMeta(project, { entries, env: opts.env });
5884
+ if (opts.format === "json") {
5885
+ printJson(result);
5886
+ return;
5887
+ }
5888
+ const applied = result.results.filter((r) => r.status === "applied");
5889
+ const skipped = result.results.filter((r) => r.status === "skipped");
5890
+ const manual = result.results.filter((r) => r.status === "manual");
5891
+ console.log(`Bulk SEO meta update (${result.env}, strategy: ${result.strategy}):
5892
+ `);
5893
+ if (applied.length > 0) {
5894
+ console.log(` Applied (${applied.length}):`);
5895
+ for (const r of applied) {
5896
+ console.log(` ${r.slug}`);
5897
+ }
5898
+ }
5899
+ if (skipped.length > 0) {
5900
+ console.log(`
5901
+ Skipped (${skipped.length}):`);
5902
+ for (const r of skipped) {
5903
+ console.log(` ${r.slug}: ${r.error ?? "unknown reason"}`);
5904
+ }
5905
+ }
5906
+ if (manual.length > 0) {
5907
+ console.log(`
5908
+ Manual action required (${manual.length}):`);
5909
+ console.log(" No SEO plugin with REST-writable meta fields was detected.");
5910
+ console.log(" Install Yoast SEO, Rank Math, or AIOSEO, or update these pages manually:\n");
5911
+ for (const r of manual) {
5912
+ if (r.manualAssist) {
5913
+ console.log(` ${r.slug}:`);
5914
+ console.log(` Admin: ${r.manualAssist.adminUrl ?? "-"}`);
5915
+ console.log(` Values: ${r.manualAssist.content}`);
5916
+ } else {
5917
+ console.log(` ${r.slug}`);
5918
+ }
5919
+ }
5920
+ }
5921
+ console.log(`
5922
+ Total: ${applied.length} applied, ${skipped.length} skipped, ${manual.length} manual`);
5923
+ }
5813
5924
  async function wordpressSchema(project, slug, opts) {
5814
5925
  const client = getClient17();
5815
5926
  const result = await client.wordpressSchema(project, slug, opts.env);
@@ -5830,6 +5941,168 @@ async function wordpressSetSchema(project, body) {
5830
5941
  }
5831
5942
  printManualAssist(`Schema update for "${body.slug}"`, result);
5832
5943
  }
5944
+ async function wordpressSchemaDeploy(project, opts) {
5945
+ const fs7 = await import("fs/promises");
5946
+ const path5 = await import("path");
5947
+ const yaml = await import("yaml").catch(() => null);
5948
+ const filePath = path5.resolve(opts.profile);
5949
+ let raw;
5950
+ try {
5951
+ raw = await fs7.readFile(filePath, "utf8");
5952
+ } catch {
5953
+ throw new CliError({
5954
+ code: "FILE_READ_ERROR",
5955
+ message: `Cannot read file: ${filePath}`,
5956
+ displayMessage: `Error: cannot read file "${opts.profile}". Check the path and permissions.`,
5957
+ details: { path: filePath }
5958
+ });
5959
+ }
5960
+ let parsed;
5961
+ try {
5962
+ if (yaml?.parse) {
5963
+ parsed = yaml.parse(raw);
5964
+ } else {
5965
+ parsed = JSON.parse(raw);
5966
+ }
5967
+ } catch {
5968
+ throw new CliError({
5969
+ code: "INVALID_PROFILE",
5970
+ message: `File is not valid YAML or JSON: ${filePath}`,
5971
+ displayMessage: `Error: "${opts.profile}" is not valid YAML or JSON.`,
5972
+ details: { path: filePath }
5973
+ });
5974
+ }
5975
+ const profile = parsed;
5976
+ if (!profile?.business?.name || !profile?.pages || Object.keys(profile.pages).length === 0) {
5977
+ throw new CliError({
5978
+ code: "INVALID_PROFILE",
5979
+ message: "Profile must have business.name and non-empty pages",
5980
+ displayMessage: "Error: profile file must contain business.name and at least one page entry.",
5981
+ details: { path: filePath }
5982
+ });
5983
+ }
5984
+ const client = getClient17();
5985
+ const result = await client.wordpressSchemaDeploy(project, { profile: parsed, env: opts.env });
5986
+ if (opts.format === "json") {
5987
+ printJson(result);
5988
+ return;
5989
+ }
5990
+ console.log(`Schema deploy (${result.env}):
5991
+ `);
5992
+ for (const r of result.results) {
5993
+ const types = r.schemasInjected?.join(", ") ?? "";
5994
+ switch (r.status) {
5995
+ case "deployed":
5996
+ console.log(` ${r.slug}: deployed (${types})`);
5997
+ break;
5998
+ case "stripped":
5999
+ console.log(` ${r.slug}: STRIPPED \u2014 WordPress removed <script> tags. Manual action required.`);
6000
+ if (r.manualAssist) {
6001
+ console.log(` Admin: ${r.manualAssist.adminUrl ?? "-"}`);
6002
+ for (const step of r.manualAssist.nextSteps) {
6003
+ console.log(` - ${step}`);
6004
+ }
6005
+ }
6006
+ break;
6007
+ case "skipped":
6008
+ console.log(` ${r.slug}: skipped \u2014 ${r.error ?? "unknown"}`);
6009
+ break;
6010
+ case "failed":
6011
+ console.log(` ${r.slug}: FAILED \u2014 ${r.error ?? "unknown"}`);
6012
+ break;
6013
+ }
6014
+ }
6015
+ const deployed = result.results.filter((r) => r.status === "deployed").length;
6016
+ const stripped = result.results.filter((r) => r.status === "stripped").length;
6017
+ const skipped = result.results.filter((r) => r.status === "skipped").length;
6018
+ const failed = result.results.filter((r) => r.status === "failed").length;
6019
+ console.log(`
6020
+ Total: ${deployed} deployed, ${stripped} stripped, ${skipped} skipped, ${failed} failed`);
6021
+ }
6022
+ async function wordpressSchemaStatus(project, opts) {
6023
+ const client = getClient17();
6024
+ const result = await client.wordpressSchemaStatus(project, opts.env);
6025
+ if (opts.format === "json") {
6026
+ printJson(result);
6027
+ return;
6028
+ }
6029
+ console.log(`Schema status (${result.env}):
6030
+ `);
6031
+ if (result.pages.length === 0) {
6032
+ console.log(" No pages found.");
6033
+ return;
6034
+ }
6035
+ const slugWidth = Math.max(4, ...result.pages.map((p) => p.slug.length));
6036
+ console.log(` ${"SLUG".padEnd(slugWidth)} CANONRY THIRD-PARTY`);
6037
+ console.log(` ${"\u2500".repeat(slugWidth)} ${"\u2500".repeat(17)} ${"\u2500".repeat(20)}`);
6038
+ for (const page of result.pages) {
6039
+ const canonry = page.canonrySchemas.length > 0 ? page.canonrySchemas.join(", ") : "-";
6040
+ const thirdParty = page.thirdPartySchemas.length > 0 ? page.thirdPartySchemas.join(", ") : "-";
6041
+ console.log(` ${page.slug.padEnd(slugWidth)} ${canonry.padEnd(17)} ${thirdParty}`);
6042
+ }
6043
+ }
6044
+ async function wordpressOnboard(project, opts) {
6045
+ const appPassword = opts.appPassword ?? await promptForAppPassword();
6046
+ if (!appPassword) {
6047
+ throw new CliError({
6048
+ code: "WORDPRESS_APP_PASSWORD_REQUIRED",
6049
+ message: "WordPress Application Password is required",
6050
+ displayMessage: "Error: WordPress Application Password is required (pass --app-password or enter interactively).",
6051
+ details: { project }
6052
+ });
6053
+ }
6054
+ let profileData;
6055
+ if (opts.profile) {
6056
+ const fs7 = await import("fs/promises");
6057
+ const path5 = await import("path");
6058
+ const yaml = await import("yaml").catch(() => null);
6059
+ const filePath = path5.resolve(opts.profile);
6060
+ let raw;
6061
+ try {
6062
+ raw = await fs7.readFile(filePath, "utf8");
6063
+ } catch {
6064
+ throw new CliError({
6065
+ code: "FILE_READ_ERROR",
6066
+ message: `Cannot read file: ${filePath}`,
6067
+ displayMessage: `Error: cannot read file "${opts.profile}".`,
6068
+ details: { path: filePath }
6069
+ });
6070
+ }
6071
+ try {
6072
+ profileData = yaml?.parse ? yaml.parse(raw) : JSON.parse(raw);
6073
+ } catch {
6074
+ throw new CliError({
6075
+ code: "INVALID_PROFILE",
6076
+ message: `File is not valid YAML or JSON: ${filePath}`,
6077
+ displayMessage: `Error: "${opts.profile}" is not valid YAML or JSON.`,
6078
+ details: { path: filePath }
6079
+ });
6080
+ }
6081
+ }
6082
+ const client = getClient17();
6083
+ const result = await client.wordpressOnboard(project, {
6084
+ url: opts.url,
6085
+ username: opts.user,
6086
+ appPassword,
6087
+ stagingUrl: opts.stagingUrl,
6088
+ defaultEnv: opts.defaultEnv,
6089
+ profile: profileData,
6090
+ skipSchema: opts.skipSchema,
6091
+ skipSubmit: opts.skipSubmit
6092
+ });
6093
+ if (opts.format === "json") {
6094
+ printJson(result);
6095
+ return;
6096
+ }
6097
+ console.log(`WordPress onboarding for "${project}":
6098
+ `);
6099
+ for (const step of result.steps) {
6100
+ const icon = step.status === "completed" ? "+" : step.status === "skipped" ? "-" : "x";
6101
+ console.log(` [${icon}] ${step.name}: ${step.status}`);
6102
+ if (step.summary) console.log(` ${step.summary}`);
6103
+ if (step.error) console.log(` Error: ${step.error}`);
6104
+ }
6105
+ }
5833
6106
  async function wordpressLlmsTxt(project, opts) {
5834
6107
  const client = getClient17();
5835
6108
  const result = await client.wordpressLlmsTxt(project, opts.env);
@@ -6107,15 +6380,27 @@ var WORDPRESS_CLI_COMMANDS = [
6107
6380
  },
6108
6381
  {
6109
6382
  path: ["wordpress", "set-meta"],
6110
- usage: "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--live|--staging] [--format json]",
6383
+ usage: "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--from <file>] [--live|--staging] [--format json]",
6111
6384
  options: {
6112
6385
  title: stringOption(),
6113
6386
  description: stringOption(),
6114
6387
  noindex: { type: "boolean", default: false },
6115
6388
  index: { type: "boolean", default: false },
6389
+ from: stringOption(),
6116
6390
  ...envOptions
6117
6391
  },
6118
6392
  run: async (input) => {
6393
+ const fromFile = getString(input.values, "from");
6394
+ if (fromFile) {
6395
+ const usage2 = "canonry wordpress set-meta <project> --from <file> [--live|--staging] [--format json]";
6396
+ const project2 = requireProject(input, "wordpress.set-meta", usage2);
6397
+ await wordpressBulkSetMeta(project2, {
6398
+ from: fromFile,
6399
+ env: resolveEnv(input, "wordpress.set-meta", usage2),
6400
+ format: input.format
6401
+ });
6402
+ return;
6403
+ }
6119
6404
  const usage = "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--live|--staging] [--format json]";
6120
6405
  const project = requireProject(input, "wordpress.set-meta", usage);
6121
6406
  const slug = requirePositional(input, 1, {
@@ -6133,6 +6418,41 @@ var WORDPRESS_CLI_COMMANDS = [
6133
6418
  });
6134
6419
  }
6135
6420
  },
6421
+ {
6422
+ path: ["wordpress", "schema", "deploy"],
6423
+ usage: "canonry wordpress schema deploy <project> --profile <file> [--live|--staging] [--format json]",
6424
+ options: {
6425
+ profile: stringOption(),
6426
+ ...envOptions
6427
+ },
6428
+ run: async (input) => {
6429
+ const usage = "canonry wordpress schema deploy <project> --profile <file> [--live|--staging] [--format json]";
6430
+ const project = requireProject(input, "wordpress.schema.deploy", usage);
6431
+ const profile = requireStringOption(input, "profile", {
6432
+ message: "--profile is required",
6433
+ command: "wordpress.schema.deploy",
6434
+ usage
6435
+ });
6436
+ await wordpressSchemaDeploy(project, {
6437
+ profile,
6438
+ env: resolveEnv(input, "wordpress.schema.deploy", usage),
6439
+ format: input.format
6440
+ });
6441
+ }
6442
+ },
6443
+ {
6444
+ path: ["wordpress", "schema", "status"],
6445
+ usage: "canonry wordpress schema status <project> [--live|--staging] [--format json]",
6446
+ options: envOptions,
6447
+ run: async (input) => {
6448
+ const usage = "canonry wordpress schema status <project> [--live|--staging] [--format json]";
6449
+ const project = requireProject(input, "wordpress.schema.status", usage);
6450
+ await wordpressSchemaStatus(project, {
6451
+ env: resolveEnv(input, "wordpress.schema.status", usage),
6452
+ format: input.format
6453
+ });
6454
+ }
6455
+ },
6136
6456
  {
6137
6457
  path: ["wordpress", "schema"],
6138
6458
  usage: "canonry wordpress schema <project> <slug> [--live|--staging] [--format json]",
@@ -6214,6 +6534,45 @@ var WORDPRESS_CLI_COMMANDS = [
6214
6534
  });
6215
6535
  }
6216
6536
  },
6537
+ {
6538
+ path: ["wordpress", "onboard"],
6539
+ usage: "canonry wordpress onboard <project> --url <url> --user <user> [--app-password <pw>] [--profile <file>] [--skip-schema] [--skip-submit] [--live|--staging] [--format json]",
6540
+ options: {
6541
+ url: stringOption(),
6542
+ user: stringOption(),
6543
+ "app-password": stringOption(),
6544
+ "staging-url": stringOption(),
6545
+ profile: stringOption(),
6546
+ "skip-schema": { type: "boolean", default: false },
6547
+ "skip-submit": { type: "boolean", default: false },
6548
+ ...envOptions
6549
+ },
6550
+ run: async (input) => {
6551
+ const usage = "canonry wordpress onboard <project> --url <url> --user <user> [--app-password <pw>] [--profile <file>] [--format json]";
6552
+ const project = requireProject(input, "wordpress.onboard", usage);
6553
+ const url = requireStringOption(input, "url", {
6554
+ message: "--url is required",
6555
+ command: "wordpress.onboard",
6556
+ usage
6557
+ });
6558
+ const user = requireStringOption(input, "user", {
6559
+ message: "--user is required",
6560
+ command: "wordpress.onboard",
6561
+ usage
6562
+ });
6563
+ await wordpressOnboard(project, {
6564
+ url,
6565
+ user,
6566
+ appPassword: getString(input.values, "app-password"),
6567
+ stagingUrl: getString(input.values, "staging-url"),
6568
+ defaultEnv: resolveEnv(input, "wordpress.onboard", usage),
6569
+ profile: getString(input.values, "profile"),
6570
+ skipSchema: getBoolean(input.values, "skip-schema"),
6571
+ skipSubmit: getBoolean(input.values, "skip-submit"),
6572
+ format: input.format
6573
+ });
6574
+ }
6575
+ },
6217
6576
  {
6218
6577
  path: ["wordpress", "audit"],
6219
6578
  usage: "canonry wordpress audit <project> [--live|--staging] [--format json]",
@@ -6392,8 +6751,12 @@ Usage:
6392
6751
  canonry wordpress create-page <project> Create a WordPress page (--title, --slug, --content/--content-file)
6393
6752
  canonry wordpress update-page <project> <slug> Update a WordPress page (--content/--content-file)
6394
6753
  canonry wordpress set-meta <project> <slug> Update REST-exposed SEO meta
6754
+ canonry wordpress set-meta <project> --from <file> Bulk update SEO meta from JSON file
6395
6755
  canonry wordpress schema <project> <slug> Read rendered JSON-LD schema
6756
+ canonry wordpress schema deploy <project> --profile <file> Deploy JSON-LD schema to pages
6757
+ canonry wordpress schema status <project> Show schema status per page
6396
6758
  canonry wordpress set-schema <project> <slug> Generate manual schema handoff
6759
+ canonry wordpress onboard <project> --url <url> --user <user> Full onboarding workflow
6397
6760
  canonry wordpress llms-txt <project> Read /llms.txt
6398
6761
  canonry wordpress set-llms-txt <project> Generate manual llms.txt handoff
6399
6762
  canonry wordpress audit <project> Audit WordPress pages for SEO/content issues
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-IWUQVYU3.js";
4
+ } from "./chunk-YW4IZ34Z.js";
5
5
  export {
6
6
  createServer,
7
7
  loadConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "1.29.0",
3
+ "version": "1.31.0",
4
4
  "type": "module",
5
5
  "description": "The ultimate open-source AEO monitoring tool - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -54,19 +54,19 @@
54
54
  "@types/node-cron": "^3.0.11",
55
55
  "tsup": "^8.5.1",
56
56
  "tsx": "^4.19.0",
57
- "@ainyc/canonry-config": "0.0.0",
58
- "@ainyc/canonry-contracts": "0.0.0",
59
57
  "@ainyc/canonry-api-routes": "0.0.0",
60
- "@ainyc/canonry-integration-google": "0.0.0",
61
- "@ainyc/canonry-integration-wordpress": "0.0.0",
62
- "@ainyc/canonry-integration-bing": "0.0.0",
58
+ "@ainyc/canonry-contracts": "0.0.0",
59
+ "@ainyc/canonry-config": "0.0.0",
63
60
  "@ainyc/canonry-db": "0.0.0",
61
+ "@ainyc/canonry-integration-bing": "0.0.0",
62
+ "@ainyc/canonry-integration-google": "0.0.0",
64
63
  "@ainyc/canonry-provider-cdp": "0.0.0",
64
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
65
65
  "@ainyc/canonry-provider-claude": "0.0.0",
66
- "@ainyc/canonry-provider-gemini": "0.0.0",
67
66
  "@ainyc/canonry-provider-local": "0.0.0",
68
- "@ainyc/canonry-provider-perplexity": "0.0.0",
69
- "@ainyc/canonry-provider-openai": "0.0.0"
67
+ "@ainyc/canonry-provider-gemini": "0.0.0",
68
+ "@ainyc/canonry-provider-openai": "0.0.0",
69
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "tsup && tsx build-web.ts",