@ainyc/canonry 1.29.0 → 1.30.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-LMSO32GF.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(`
@@ -5810,6 +5840,88 @@ async function wordpressSetMeta(project, body) {
5810
5840
  `);
5811
5841
  printPageDetail(result);
5812
5842
  }
5843
+ async function wordpressBulkSetMeta(project, opts) {
5844
+ const fs7 = await import("fs/promises");
5845
+ const path5 = await import("path");
5846
+ const filePath = path5.resolve(opts.from);
5847
+ let raw;
5848
+ try {
5849
+ raw = await fs7.readFile(filePath, "utf8");
5850
+ } catch {
5851
+ throw new CliError({
5852
+ code: "FILE_READ_ERROR",
5853
+ message: `Cannot read file: ${filePath}`,
5854
+ displayMessage: `Error: cannot read file "${opts.from}". Check the path and permissions.`,
5855
+ details: { path: filePath }
5856
+ });
5857
+ }
5858
+ let parsed;
5859
+ try {
5860
+ parsed = JSON.parse(raw);
5861
+ } catch {
5862
+ throw new CliError({
5863
+ code: "INVALID_JSON",
5864
+ message: `File is not valid JSON: ${filePath}`,
5865
+ displayMessage: `Error: "${opts.from}" is not valid JSON.`,
5866
+ details: { path: filePath }
5867
+ });
5868
+ }
5869
+ const entries = Object.entries(parsed).map(([slug, meta]) => ({
5870
+ slug,
5871
+ title: meta.title,
5872
+ description: meta.description,
5873
+ noindex: meta.noindex
5874
+ }));
5875
+ if (entries.length === 0) {
5876
+ throw new CliError({
5877
+ code: "EMPTY_META_FILE",
5878
+ message: "Meta file contains no entries",
5879
+ displayMessage: `Error: "${opts.from}" contains no entries. Expected JSON object keyed by slug.`,
5880
+ details: { path: filePath }
5881
+ });
5882
+ }
5883
+ const client = getClient17();
5884
+ const result = await client.wordpressBulkSetMeta(project, { entries, env: opts.env });
5885
+ if (opts.format === "json") {
5886
+ printJson(result);
5887
+ return;
5888
+ }
5889
+ const applied = result.results.filter((r) => r.status === "applied");
5890
+ const skipped = result.results.filter((r) => r.status === "skipped");
5891
+ const manual = result.results.filter((r) => r.status === "manual");
5892
+ console.log(`Bulk SEO meta update (${result.env}, strategy: ${result.strategy}):
5893
+ `);
5894
+ if (applied.length > 0) {
5895
+ console.log(` Applied (${applied.length}):`);
5896
+ for (const r of applied) {
5897
+ console.log(` ${r.slug}`);
5898
+ }
5899
+ }
5900
+ if (skipped.length > 0) {
5901
+ console.log(`
5902
+ Skipped (${skipped.length}):`);
5903
+ for (const r of skipped) {
5904
+ console.log(` ${r.slug}: ${r.error ?? "unknown reason"}`);
5905
+ }
5906
+ }
5907
+ if (manual.length > 0) {
5908
+ console.log(`
5909
+ Manual action required (${manual.length}):`);
5910
+ console.log(" No SEO plugin with REST-writable meta fields was detected.");
5911
+ console.log(" Install Yoast SEO, Rank Math, or AIOSEO, or update these pages manually:\n");
5912
+ for (const r of manual) {
5913
+ if (r.manualAssist) {
5914
+ console.log(` ${r.slug}:`);
5915
+ console.log(` Admin: ${r.manualAssist.adminUrl ?? "-"}`);
5916
+ console.log(` Values: ${r.manualAssist.content}`);
5917
+ } else {
5918
+ console.log(` ${r.slug}`);
5919
+ }
5920
+ }
5921
+ }
5922
+ console.log(`
5923
+ Total: ${applied.length} applied, ${skipped.length} skipped, ${manual.length} manual`);
5924
+ }
5813
5925
  async function wordpressSchema(project, slug, opts) {
5814
5926
  const client = getClient17();
5815
5927
  const result = await client.wordpressSchema(project, slug, opts.env);
@@ -5830,6 +5942,168 @@ async function wordpressSetSchema(project, body) {
5830
5942
  }
5831
5943
  printManualAssist(`Schema update for "${body.slug}"`, result);
5832
5944
  }
5945
+ async function wordpressSchemaDeploy(project, opts) {
5946
+ const fs7 = await import("fs/promises");
5947
+ const path5 = await import("path");
5948
+ const yaml = await import("yaml").catch(() => null);
5949
+ const filePath = path5.resolve(opts.profile);
5950
+ let raw;
5951
+ try {
5952
+ raw = await fs7.readFile(filePath, "utf8");
5953
+ } catch {
5954
+ throw new CliError({
5955
+ code: "FILE_READ_ERROR",
5956
+ message: `Cannot read file: ${filePath}`,
5957
+ displayMessage: `Error: cannot read file "${opts.profile}". Check the path and permissions.`,
5958
+ details: { path: filePath }
5959
+ });
5960
+ }
5961
+ let parsed;
5962
+ try {
5963
+ if (yaml?.parse) {
5964
+ parsed = yaml.parse(raw);
5965
+ } else {
5966
+ parsed = JSON.parse(raw);
5967
+ }
5968
+ } catch {
5969
+ throw new CliError({
5970
+ code: "INVALID_PROFILE",
5971
+ message: `File is not valid YAML or JSON: ${filePath}`,
5972
+ displayMessage: `Error: "${opts.profile}" is not valid YAML or JSON.`,
5973
+ details: { path: filePath }
5974
+ });
5975
+ }
5976
+ const profile = parsed;
5977
+ if (!profile?.business?.name || !profile?.pages || Object.keys(profile.pages).length === 0) {
5978
+ throw new CliError({
5979
+ code: "INVALID_PROFILE",
5980
+ message: "Profile must have business.name and non-empty pages",
5981
+ displayMessage: "Error: profile file must contain business.name and at least one page entry.",
5982
+ details: { path: filePath }
5983
+ });
5984
+ }
5985
+ const client = getClient17();
5986
+ const result = await client.wordpressSchemaDeploy(project, { profile: parsed, env: opts.env });
5987
+ if (opts.format === "json") {
5988
+ printJson(result);
5989
+ return;
5990
+ }
5991
+ console.log(`Schema deploy (${result.env}):
5992
+ `);
5993
+ for (const r of result.results) {
5994
+ const types = r.schemasInjected?.join(", ") ?? "";
5995
+ switch (r.status) {
5996
+ case "deployed":
5997
+ console.log(` ${r.slug}: deployed (${types})`);
5998
+ break;
5999
+ case "stripped":
6000
+ console.log(` ${r.slug}: STRIPPED \u2014 WordPress removed <script> tags. Manual action required.`);
6001
+ if (r.manualAssist) {
6002
+ console.log(` Admin: ${r.manualAssist.adminUrl ?? "-"}`);
6003
+ for (const step of r.manualAssist.nextSteps) {
6004
+ console.log(` - ${step}`);
6005
+ }
6006
+ }
6007
+ break;
6008
+ case "skipped":
6009
+ console.log(` ${r.slug}: skipped \u2014 ${r.error ?? "unknown"}`);
6010
+ break;
6011
+ case "failed":
6012
+ console.log(` ${r.slug}: FAILED \u2014 ${r.error ?? "unknown"}`);
6013
+ break;
6014
+ }
6015
+ }
6016
+ const deployed = result.results.filter((r) => r.status === "deployed").length;
6017
+ const stripped = result.results.filter((r) => r.status === "stripped").length;
6018
+ const skipped = result.results.filter((r) => r.status === "skipped").length;
6019
+ const failed = result.results.filter((r) => r.status === "failed").length;
6020
+ console.log(`
6021
+ Total: ${deployed} deployed, ${stripped} stripped, ${skipped} skipped, ${failed} failed`);
6022
+ }
6023
+ async function wordpressSchemaStatus(project, opts) {
6024
+ const client = getClient17();
6025
+ const result = await client.wordpressSchemaStatus(project, opts.env);
6026
+ if (opts.format === "json") {
6027
+ printJson(result);
6028
+ return;
6029
+ }
6030
+ console.log(`Schema status (${result.env}):
6031
+ `);
6032
+ if (result.pages.length === 0) {
6033
+ console.log(" No pages found.");
6034
+ return;
6035
+ }
6036
+ const slugWidth = Math.max(4, ...result.pages.map((p) => p.slug.length));
6037
+ console.log(` ${"SLUG".padEnd(slugWidth)} CANONRY THIRD-PARTY`);
6038
+ console.log(` ${"\u2500".repeat(slugWidth)} ${"\u2500".repeat(17)} ${"\u2500".repeat(20)}`);
6039
+ for (const page of result.pages) {
6040
+ const canonry = page.canonrySchemas.length > 0 ? page.canonrySchemas.join(", ") : "-";
6041
+ const thirdParty = page.thirdPartySchemas.length > 0 ? page.thirdPartySchemas.join(", ") : "-";
6042
+ console.log(` ${page.slug.padEnd(slugWidth)} ${canonry.padEnd(17)} ${thirdParty}`);
6043
+ }
6044
+ }
6045
+ async function wordpressOnboard(project, opts) {
6046
+ const appPassword = opts.appPassword ?? await promptForAppPassword();
6047
+ if (!appPassword) {
6048
+ throw new CliError({
6049
+ code: "WORDPRESS_APP_PASSWORD_REQUIRED",
6050
+ message: "WordPress Application Password is required",
6051
+ displayMessage: "Error: WordPress Application Password is required (pass --app-password or enter interactively).",
6052
+ details: { project }
6053
+ });
6054
+ }
6055
+ let profileData;
6056
+ if (opts.profile) {
6057
+ const fs7 = await import("fs/promises");
6058
+ const path5 = await import("path");
6059
+ const yaml = await import("yaml").catch(() => null);
6060
+ const filePath = path5.resolve(opts.profile);
6061
+ let raw;
6062
+ try {
6063
+ raw = await fs7.readFile(filePath, "utf8");
6064
+ } catch {
6065
+ throw new CliError({
6066
+ code: "FILE_READ_ERROR",
6067
+ message: `Cannot read file: ${filePath}`,
6068
+ displayMessage: `Error: cannot read file "${opts.profile}".`,
6069
+ details: { path: filePath }
6070
+ });
6071
+ }
6072
+ try {
6073
+ profileData = yaml?.parse ? yaml.parse(raw) : JSON.parse(raw);
6074
+ } catch {
6075
+ throw new CliError({
6076
+ code: "INVALID_PROFILE",
6077
+ message: `File is not valid YAML or JSON: ${filePath}`,
6078
+ displayMessage: `Error: "${opts.profile}" is not valid YAML or JSON.`,
6079
+ details: { path: filePath }
6080
+ });
6081
+ }
6082
+ }
6083
+ const client = getClient17();
6084
+ const result = await client.wordpressOnboard(project, {
6085
+ url: opts.url,
6086
+ username: opts.user,
6087
+ appPassword,
6088
+ stagingUrl: opts.stagingUrl,
6089
+ defaultEnv: opts.defaultEnv,
6090
+ profile: profileData,
6091
+ skipSchema: opts.skipSchema,
6092
+ skipSubmit: opts.skipSubmit
6093
+ });
6094
+ if (opts.format === "json") {
6095
+ printJson(result);
6096
+ return;
6097
+ }
6098
+ console.log(`WordPress onboarding for "${project}":
6099
+ `);
6100
+ for (const step of result.steps) {
6101
+ const icon = step.status === "completed" ? "+" : step.status === "skipped" ? "-" : "x";
6102
+ console.log(` [${icon}] ${step.name}: ${step.status}`);
6103
+ if (step.summary) console.log(` ${step.summary}`);
6104
+ if (step.error) console.log(` Error: ${step.error}`);
6105
+ }
6106
+ }
5833
6107
  async function wordpressLlmsTxt(project, opts) {
5834
6108
  const client = getClient17();
5835
6109
  const result = await client.wordpressLlmsTxt(project, opts.env);
@@ -6107,15 +6381,27 @@ var WORDPRESS_CLI_COMMANDS = [
6107
6381
  },
6108
6382
  {
6109
6383
  path: ["wordpress", "set-meta"],
6110
- usage: "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--live|--staging] [--format json]",
6384
+ usage: "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--from <file>] [--live|--staging] [--format json]",
6111
6385
  options: {
6112
6386
  title: stringOption(),
6113
6387
  description: stringOption(),
6114
6388
  noindex: { type: "boolean", default: false },
6115
6389
  index: { type: "boolean", default: false },
6390
+ from: stringOption(),
6116
6391
  ...envOptions
6117
6392
  },
6118
6393
  run: async (input) => {
6394
+ const fromFile = getString(input.values, "from");
6395
+ if (fromFile) {
6396
+ const usage2 = "canonry wordpress set-meta <project> --from <file> [--live|--staging] [--format json]";
6397
+ const project2 = requireProject(input, "wordpress.set-meta", usage2);
6398
+ await wordpressBulkSetMeta(project2, {
6399
+ from: fromFile,
6400
+ env: resolveEnv(input, "wordpress.set-meta", usage2),
6401
+ format: input.format
6402
+ });
6403
+ return;
6404
+ }
6119
6405
  const usage = "canonry wordpress set-meta <project> <slug> [--title <title>] [--description <text>] [--noindex|--index] [--live|--staging] [--format json]";
6120
6406
  const project = requireProject(input, "wordpress.set-meta", usage);
6121
6407
  const slug = requirePositional(input, 1, {
@@ -6133,6 +6419,41 @@ var WORDPRESS_CLI_COMMANDS = [
6133
6419
  });
6134
6420
  }
6135
6421
  },
6422
+ {
6423
+ path: ["wordpress", "schema", "deploy"],
6424
+ usage: "canonry wordpress schema deploy <project> --profile <file> [--live|--staging] [--format json]",
6425
+ options: {
6426
+ profile: stringOption(),
6427
+ ...envOptions
6428
+ },
6429
+ run: async (input) => {
6430
+ const usage = "canonry wordpress schema deploy <project> --profile <file> [--live|--staging] [--format json]";
6431
+ const project = requireProject(input, "wordpress.schema.deploy", usage);
6432
+ const profile = requireStringOption(input, "profile", {
6433
+ message: "--profile is required",
6434
+ command: "wordpress.schema.deploy",
6435
+ usage
6436
+ });
6437
+ await wordpressSchemaDeploy(project, {
6438
+ profile,
6439
+ env: resolveEnv(input, "wordpress.schema.deploy", usage),
6440
+ format: input.format
6441
+ });
6442
+ }
6443
+ },
6444
+ {
6445
+ path: ["wordpress", "schema", "status"],
6446
+ usage: "canonry wordpress schema status <project> [--live|--staging] [--format json]",
6447
+ options: envOptions,
6448
+ run: async (input) => {
6449
+ const usage = "canonry wordpress schema status <project> [--live|--staging] [--format json]";
6450
+ const project = requireProject(input, "wordpress.schema.status", usage);
6451
+ await wordpressSchemaStatus(project, {
6452
+ env: resolveEnv(input, "wordpress.schema.status", usage),
6453
+ format: input.format
6454
+ });
6455
+ }
6456
+ },
6136
6457
  {
6137
6458
  path: ["wordpress", "schema"],
6138
6459
  usage: "canonry wordpress schema <project> <slug> [--live|--staging] [--format json]",
@@ -6214,6 +6535,45 @@ var WORDPRESS_CLI_COMMANDS = [
6214
6535
  });
6215
6536
  }
6216
6537
  },
6538
+ {
6539
+ path: ["wordpress", "onboard"],
6540
+ usage: "canonry wordpress onboard <project> --url <url> --user <user> [--app-password <pw>] [--profile <file>] [--skip-schema] [--skip-submit] [--live|--staging] [--format json]",
6541
+ options: {
6542
+ url: stringOption(),
6543
+ user: stringOption(),
6544
+ "app-password": stringOption(),
6545
+ "staging-url": stringOption(),
6546
+ profile: stringOption(),
6547
+ "skip-schema": { type: "boolean", default: false },
6548
+ "skip-submit": { type: "boolean", default: false },
6549
+ ...envOptions
6550
+ },
6551
+ run: async (input) => {
6552
+ const usage = "canonry wordpress onboard <project> --url <url> --user <user> [--app-password <pw>] [--profile <file>] [--format json]";
6553
+ const project = requireProject(input, "wordpress.onboard", usage);
6554
+ const url = requireStringOption(input, "url", {
6555
+ message: "--url is required",
6556
+ command: "wordpress.onboard",
6557
+ usage
6558
+ });
6559
+ const user = requireStringOption(input, "user", {
6560
+ message: "--user is required",
6561
+ command: "wordpress.onboard",
6562
+ usage
6563
+ });
6564
+ await wordpressOnboard(project, {
6565
+ url,
6566
+ user,
6567
+ appPassword: getString(input.values, "app-password"),
6568
+ stagingUrl: getString(input.values, "staging-url"),
6569
+ defaultEnv: resolveEnv(input, "wordpress.onboard", usage),
6570
+ profile: getString(input.values, "profile"),
6571
+ skipSchema: getBoolean(input.values, "skip-schema"),
6572
+ skipSubmit: getBoolean(input.values, "skip-submit"),
6573
+ format: input.format
6574
+ });
6575
+ }
6576
+ },
6217
6577
  {
6218
6578
  path: ["wordpress", "audit"],
6219
6579
  usage: "canonry wordpress audit <project> [--live|--staging] [--format json]",
@@ -6392,8 +6752,12 @@ Usage:
6392
6752
  canonry wordpress create-page <project> Create a WordPress page (--title, --slug, --content/--content-file)
6393
6753
  canonry wordpress update-page <project> <slug> Update a WordPress page (--content/--content-file)
6394
6754
  canonry wordpress set-meta <project> <slug> Update REST-exposed SEO meta
6755
+ canonry wordpress set-meta <project> --from <file> Bulk update SEO meta from JSON file
6395
6756
  canonry wordpress schema <project> <slug> Read rendered JSON-LD schema
6757
+ canonry wordpress schema deploy <project> --profile <file> Deploy JSON-LD schema to pages
6758
+ canonry wordpress schema status <project> Show schema status per page
6396
6759
  canonry wordpress set-schema <project> <slug> Generate manual schema handoff
6760
+ canonry wordpress onboard <project> --url <url> --user <user> Full onboarding workflow
6397
6761
  canonry wordpress llms-txt <project> Read /llms.txt
6398
6762
  canonry wordpress set-llms-txt <project> Generate manual llms.txt handoff
6399
6763
  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-LMSO32GF.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.30.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",
58
+ "@ainyc/canonry-config": "0.0.0",
60
59
  "@ainyc/canonry-integration-google": "0.0.0",
60
+ "@ainyc/canonry-contracts": "0.0.0",
61
61
  "@ainyc/canonry-integration-wordpress": "0.0.0",
62
- "@ainyc/canonry-integration-bing": "0.0.0",
63
62
  "@ainyc/canonry-db": "0.0.0",
64
63
  "@ainyc/canonry-provider-cdp": "0.0.0",
65
64
  "@ainyc/canonry-provider-claude": "0.0.0",
66
65
  "@ainyc/canonry-provider-gemini": "0.0.0",
67
- "@ainyc/canonry-provider-local": "0.0.0",
66
+ "@ainyc/canonry-integration-bing": "0.0.0",
67
+ "@ainyc/canonry-provider-openai": "0.0.0",
68
68
  "@ainyc/canonry-provider-perplexity": "0.0.0",
69
- "@ainyc/canonry-provider-openai": "0.0.0"
69
+ "@ainyc/canonry-provider-local": "0.0.0"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "tsup && tsx build-web.ts",