@canonry/canonry 4.155.0 → 4.156.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 (35) hide show
  1. package/assets/agent-workspace/skills/aero/references/orchestration.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +14 -2
  4. package/assets/assets/{AuditHistoryPanel-BlbMBxOY.js → AuditHistoryPanel-gmJMVlXX.js} +1 -1
  5. package/assets/assets/{BacklinksPage-DA9EaV9q.js → BacklinksPage-Bv2sTCyN.js} +1 -1
  6. package/assets/assets/{HistoryPage-BTrIHjMQ.js → HistoryPage-13q7GkiI.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-CN6o5xFg.js → MeasurementPropertyPage-BgBqXT1p.js} +1 -1
  8. package/assets/assets/ProjectPage-EsGwKhot.js +15 -0
  9. package/assets/assets/{RunRow-ch_OlCPs.js → RunRow-bPzapIGJ.js} +1 -1
  10. package/assets/assets/{RunsPage-T89Y_yS8.js → RunsPage-Dqfz18lH.js} +1 -1
  11. package/assets/assets/{SettingsPage-DnmEq3F9.js → SettingsPage-_wMecn-V.js} +1 -1
  12. package/assets/assets/{TrafficPage-CW1UhaJt.js → TrafficPage-RYBdsOLB.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-r4Ji3wJm.js → TrafficSourceDetailPage-DpeMMyKU.js} +1 -1
  14. package/assets/assets/{extract-error-message-a8EErBoZ.js → extract-error-message-DUarAlyD.js} +1 -1
  15. package/assets/assets/index-BxegEwic.css +1 -0
  16. package/assets/assets/{index-BKq6k5Of.js → index-DP-fT2uI.js} +29 -29
  17. package/assets/assets/react-sigma_core.esm.min-DnypJl5w.js +312 -0
  18. package/assets/assets/vendor-lucide-BtYdosYk.js +1 -0
  19. package/assets/assets/{vendor-markdown-CpDJch4G.js → vendor-markdown-D274_wsI.js} +1 -1
  20. package/assets/assets/{vendor-radix-DnzzbllM.js → vendor-radix-BxMXSwbC.js} +1 -1
  21. package/assets/assets/{vendor-recharts-33ACLxsD.js → vendor-recharts-C4ktw_aE.js} +1 -1
  22. package/assets/assets/{vendor-tanstack-BjS7iY3g.js → vendor-tanstack-pFxw0eGP.js} +1 -1
  23. package/assets/index.html +7 -7
  24. package/dist/{chunk-VOL2BSM5.js → chunk-ABNN2ZVR.js} +1332 -29
  25. package/dist/{chunk-VIKPFI6F.js → chunk-AMPT2EP7.js} +328 -1
  26. package/dist/{chunk-OSHRDV36.js → chunk-CPASKGUE.js} +247 -1
  27. package/dist/{chunk-YSESZNVT.js → chunk-ZWESOFA5.js} +711 -235
  28. package/dist/cli.js +427 -5
  29. package/dist/index.js +4 -4
  30. package/dist/{intelligence-service-G4NV3X36.js → intelligence-service-WHC7B32V.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +11 -9
  33. package/assets/assets/ProjectPage-CsDbgp_0.js +0 -12
  34. package/assets/assets/index-DyL54s8x.css +0 -1
  35. package/assets/assets/vendor-lucide-ByU7vBHt.js +0 -1
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  showFirstRunNotice,
31
31
  trackCliCommandFinished,
32
32
  trackEvent
33
- } from "./chunk-YSESZNVT.js";
33
+ } from "./chunk-ZWESOFA5.js";
34
34
  import {
35
35
  CliError,
36
36
  EXIT_SYSTEM_ERROR,
@@ -50,7 +50,7 @@ import {
50
50
  saveConfigPatch,
51
51
  systemError,
52
52
  usageError
53
- } from "./chunk-OSHRDV36.js";
53
+ } from "./chunk-CPASKGUE.js";
54
54
  import {
55
55
  apiKeys,
56
56
  createClient,
@@ -58,7 +58,7 @@ import {
58
58
  projects,
59
59
  queries,
60
60
  renderReportHtml
61
- } from "./chunk-VOL2BSM5.js";
61
+ } from "./chunk-ABNN2ZVR.js";
62
62
  import {
63
63
  AdsDeliverySnapshotStatuses,
64
64
  AdsHistoricalCampaignRollupStatuses,
@@ -122,7 +122,7 @@ import {
122
122
  providerQuotaPolicySchema,
123
123
  resolveProviderInput,
124
124
  winnabilityClassSchema
125
- } from "./chunk-VIKPFI6F.js";
125
+ } from "./chunk-AMPT2EP7.js";
126
126
 
127
127
  // src/cli.ts
128
128
  import { pathToFileURL } from "url";
@@ -13849,9 +13849,120 @@ async function technicalAeoCrawl(project, opts) {
13849
13849
  if (crawl.termination) lines.push(`Stopped: ${crawl.termination}`);
13850
13850
  console.log(lines.join("\n"));
13851
13851
  }
13852
+ async function technicalAeoSubgraph(project, opts) {
13853
+ const res = await getClient27().getSiteHealthSubgraph(project, opts);
13854
+ if (isMachineFormat(opts.format)) {
13855
+ console.log(JSON.stringify(res, null, 2));
13856
+ return;
13857
+ }
13858
+ if (!res.hasCrawlData) {
13859
+ console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
13860
+ return;
13861
+ }
13862
+ if (res.state !== "ready") {
13863
+ console.log(`Site Health subgraph details are unavailable for crawl ${res.runId ?? "-"}.`);
13864
+ return;
13865
+ }
13866
+ const focus = res.focusUrl ?? res.focusNodeKey ?? "crawl root";
13867
+ const totalQualifier = res.countAccuracy === "lower-bound" ? "at least " : "";
13868
+ const lines = [
13869
+ `Site Health subgraph: ${focus} (${res.hops} hop${res.hops === 1 ? "" : "s"})`,
13870
+ `${res.nodes.length} of ${totalQualifier}${res.totalNodes} node(s) \xB7 ${res.edges.length} of ${totalQualifier}${res.totalEdges} edge(s)`,
13871
+ `Crawl: ${res.complete ? "complete" : `partial (${res.termination ?? "incomplete"})`}`
13872
+ ];
13873
+ if (res.countAccuracy === "lower-bound") lines.push("Count accuracy: lower bound; traversal reached a cap, so totals and omissions may be higher.");
13874
+ if (res.truncated) lines.push(`Truncated: at least ${res.omittedNodes} node(s) and ${res.omittedEdges} edge(s) omitted. Narrow or refocus the request.`);
13875
+ if (!res.complete) lines.push(`Partial crawl: ${res.termination ?? "incomplete"}. Results describe only persisted observations.`);
13876
+ console.log(lines.join("\n"));
13877
+ }
13878
+ async function technicalAeoPath(project, opts) {
13879
+ const res = await getClient27().getSiteHealthPath(project, opts);
13880
+ if (isMachineFormat(opts.format)) {
13881
+ console.log(JSON.stringify(res, null, 2));
13882
+ return;
13883
+ }
13884
+ if (res.state === "no-crawl") {
13885
+ console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
13886
+ return;
13887
+ }
13888
+ if (res.state === "details-unavailable") {
13889
+ console.log(`Site Health path details are unavailable for crawl ${res.runId ?? "-"}.`);
13890
+ return;
13891
+ }
13892
+ if (res.state === "unreachable") {
13893
+ const qualification = res.complete ? "" : ` in the partial crawl (${res.termination ?? "incomplete"}); this is not a site-wide conclusion`;
13894
+ console.log(`No directed followable path found${qualification} to ${res.to?.url ?? "the requested destination"} within ${res.maxDepth} hop(s).`);
13895
+ return;
13896
+ }
13897
+ if (res.state === "truncated") {
13898
+ const qualification = res.complete ? "" : ` The crawl is partial (${res.termination ?? "incomplete"}), so this is not a site-wide conclusion.`;
13899
+ console.log(`Path search reached its exploration cap before resolving ${res.to?.url ?? "the requested destination"}. Narrow the route or reduce max depth.${qualification}`);
13900
+ return;
13901
+ }
13902
+ const lines = [res.nodes.map((node) => node.url).join("\n\u2192 ")];
13903
+ if (!res.complete) lines.push("", `Partial crawl: ${res.termination ?? "incomplete"}. This path covers only persisted observations.`);
13904
+ console.log(lines.join("\n"));
13905
+ }
13906
+ async function technicalAeoChanges(project, opts) {
13907
+ const res = await getClient27().getSiteHealthChanges(project, opts);
13908
+ if (opts.format === "jsonl") {
13909
+ if (res.state === "ready") {
13910
+ const { changes, ...header } = res;
13911
+ emitJsonl([{ kind: "site-health-changes-header", ...header }]);
13912
+ emitJsonl(changes.map((record) => ({ project, fromRunId: res.fromRunId, toRunId: res.toRunId, ...record })));
13913
+ } else {
13914
+ emitJsonl([{ kind: "site-health-changes-header", ...res }]);
13915
+ }
13916
+ return;
13917
+ }
13918
+ if (isMachineFormat(opts.format)) {
13919
+ console.log(JSON.stringify(res, null, 2));
13920
+ return;
13921
+ }
13922
+ if (res.state === "unavailable") {
13923
+ console.log(`Site Health changes unavailable: ${res.reason.replaceAll("-", " ")}.`);
13924
+ return;
13925
+ }
13926
+ if (res.state === "incompatible") {
13927
+ console.log(`Site Health changes unavailable: snapshots use incompatible ${res.mismatchedVersions.join(", ")} versions.`);
13928
+ return;
13929
+ }
13930
+ const lines = [
13931
+ `Site Health changes: ${res.fromRunId} \u2192 ${res.toRunId}`,
13932
+ `Filters: ${res.filters.scope} \xB7 ${res.filters.change}`
13933
+ ];
13934
+ if (res.summaryState === "exact" && res.summary !== null && res.total !== null) {
13935
+ lines.push(`Pages: +${res.summary.pages.added} / -${res.summary.pages.removed} / ${res.summary.pages.changed} changed`);
13936
+ lines.push(`Links: +${res.summary.links.added} / -${res.summary.links.removed} / ${res.summary.links.changed}`);
13937
+ lines.push(`${res.changes.length} of ${res.total} change(s)${res.nextCursor ? " (more available)" : ""}`);
13938
+ } else {
13939
+ lines.push(`Summary: omitted on continuation; ${res.changes.length} returned change(s)${res.nextCursor ? " (more available)" : ""}`);
13940
+ }
13941
+ if (res.changes.length > 0) {
13942
+ lines.push("", "Changes:");
13943
+ for (const record of res.changes) lines.push(` ${formatSiteHealthChangeRecord(record)}`);
13944
+ }
13945
+ if (res.nextCursor) lines.push(`Next cursor: ${res.nextCursor}`);
13946
+ console.log(lines.join("\n"));
13947
+ }
13948
+ function formatSiteHealthChangeRecord(record) {
13949
+ const target = record.entity === "page" ? record.after?.url ?? record.before?.url ?? record.key : (() => {
13950
+ const edge = record.after ?? record.before;
13951
+ return edge ? `${edge.sourceUrl} \u2192 ${edge.targetUrl}` : record.key;
13952
+ })();
13953
+ const fields = record.changedFields.length > 0 ? ` (${record.changedFields.join(", ")})` : "";
13954
+ return `${record.entity} ${record.change}: ${target}${fields}`;
13955
+ }
13852
13956
  async function technicalAeoCrawlPages(project, opts) {
13853
13957
  const res = await getClient27().getTechnicalAeoCrawlPages(project, opts);
13854
13958
  if (opts.format === "jsonl") {
13959
+ emitJsonl([{
13960
+ kind: "technical-aeo-crawl-pages-header",
13961
+ project,
13962
+ runId: res.runId,
13963
+ total: res.total,
13964
+ nextCursor: res.nextCursor
13965
+ }]);
13855
13966
  emitJsonl(res.pages.map((page) => ({ project, runId: res.runId, ...page })));
13856
13967
  return;
13857
13968
  }
@@ -13872,9 +13983,76 @@ async function technicalAeoCrawlPages(project, opts) {
13872
13983
  Next cursor: ${res.nextCursor}`);
13873
13984
  console.log(lines.join("\n"));
13874
13985
  }
13986
+ async function technicalAeoPageAudit(project, opts) {
13987
+ const res = await getClient27().getTechnicalAeoPageAudit(project, {
13988
+ runId: opts.runId,
13989
+ nodeKey: opts.nodeKey,
13990
+ url: opts.url
13991
+ });
13992
+ if (isMachineFormat(opts.format)) {
13993
+ console.log(JSON.stringify(res, null, 2));
13994
+ return;
13995
+ }
13996
+ if (res.state === "no-crawl") {
13997
+ console.log(`No persisted Site Health crawl for "${project}". Run \`canonry technical-aeo run ${project}\` first.`);
13998
+ return;
13999
+ }
14000
+ const provenance = [`Run: ${res.runId} \xB7 ${res.complete ? "complete" : "partial"}`];
14001
+ if (!res.complete) provenance.push(`Partial crawl: ${res.termination ?? "termination reason unavailable"}`);
14002
+ if (res.state === "details-unavailable") {
14003
+ console.log([
14004
+ "Page audit details are unavailable for this retained crawl.",
14005
+ ...provenance
14006
+ ].join("\n"));
14007
+ return;
14008
+ }
14009
+ if (res.state === "not-found") {
14010
+ console.log([
14011
+ "The selected page was not found in this crawl.",
14012
+ ...provenance
14013
+ ].join("\n"));
14014
+ return;
14015
+ }
14016
+ const lines = [
14017
+ `Page audit: ${res.auditScore == null ? "not scored" : `${res.auditScore}/100`} \xB7 ${res.url}`,
14018
+ ...provenance
14019
+ ];
14020
+ if (res.state === "not-audited") {
14021
+ lines.push(`Audit evidence unavailable: page state is ${res.auditState}.`);
14022
+ console.log(lines.join("\n"));
14023
+ return;
14024
+ }
14025
+ if (res.evidenceState === "scores-only") {
14026
+ lines.push("Evidence: scores only; this historical row predates persisted finding prose.");
14027
+ }
14028
+ if (res.criticalDefects.length > 0) {
14029
+ lines.push("", "Score-independent defects:");
14030
+ for (const defect of res.criticalDefects) {
14031
+ lines.push(` [${defect.severity}] [${defect.id}] ${defect.detail}`);
14032
+ lines.push(` Fix: ${defect.recommendation}`);
14033
+ }
14034
+ }
14035
+ if (res.factors.length > 0) {
14036
+ lines.push("", "Factors:");
14037
+ for (const factor of res.factors) {
14038
+ lines.push(` ${factor.name}: ${factor.score}/100 (${factor.status})`);
14039
+ for (const finding of factor.findings) lines.push(` [${finding.code}] ${finding.message}`);
14040
+ for (const recommendation of factor.recommendations) lines.push(` Fix: ${recommendation}`);
14041
+ }
14042
+ }
14043
+ console.log(lines.join("\n"));
14044
+ }
13875
14045
  async function technicalAeoStructure(project, opts) {
13876
14046
  const res = await getClient27().getTechnicalAeoStructure(project, opts);
13877
14047
  if (opts.format === "jsonl") {
14048
+ emitJsonl([{
14049
+ kind: "technical-aeo-structure-header",
14050
+ project,
14051
+ runId: res.runId,
14052
+ parentPath: res.parentPath,
14053
+ returned: res.children.length,
14054
+ nextCursor: res.nextCursor
14055
+ }]);
13878
14056
  emitJsonl(res.children.map((child) => ({ project, runId: res.runId, parentPath: res.parentPath, ...child })));
13879
14057
  return;
13880
14058
  }
@@ -13897,6 +14075,13 @@ Next cursor: ${res.nextCursor}`);
13897
14075
  async function technicalAeoInternalLinks(project, opts) {
13898
14076
  const res = await getClient27().getTechnicalAeoInternalLinks(project, opts);
13899
14077
  if (opts.format === "jsonl") {
14078
+ emitJsonl([{
14079
+ kind: "technical-aeo-internal-links-header",
14080
+ project,
14081
+ runId: res.runId,
14082
+ total: res.total,
14083
+ nextCursor: res.nextCursor
14084
+ }]);
13900
14085
  emitJsonl(res.edges.map((edge) => ({ project, runId: res.runId, ...edge })));
13901
14086
  return;
13902
14087
  }
@@ -14019,7 +14204,13 @@ Usage: ${config.usage}`, {
14019
14204
  details: { command: config.command, usage: config.usage, option: key, value }
14020
14205
  });
14021
14206
  }
14022
- var TECHNICAL_AEO_CLI_COMMANDS = [
14207
+ function isSiteHealthScope(value) {
14208
+ return value === "all" || value === "pages" || value === "links";
14209
+ }
14210
+ function isSiteHealthChange(value) {
14211
+ return value === "all" || value === "added" || value === "removed" || value === "changed";
14212
+ }
14213
+ var TECHNICAL_AEO_CLI_COMMANDS_BASE = [
14023
14214
  {
14024
14215
  path: ["technical-aeo", "score"],
14025
14216
  usage: "canonry technical-aeo score <project> [--run-id <id>] [--format json]",
@@ -14132,6 +14323,126 @@ var TECHNICAL_AEO_CLI_COMMANDS = [
14132
14323
  await technicalAeoCrawl(project, { runId: getString(input.values, "run-id"), format: input.format });
14133
14324
  }
14134
14325
  },
14326
+ {
14327
+ path: ["technical-aeo", "subgraph"],
14328
+ usage: "canonry technical-aeo subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]",
14329
+ options: {
14330
+ "run-id": stringOption(),
14331
+ "node-key": stringOption(),
14332
+ url: stringOption(),
14333
+ hops: stringOption(),
14334
+ "max-nodes": stringOption(),
14335
+ "max-edges": stringOption()
14336
+ },
14337
+ run: async (input) => {
14338
+ const usage = "canonry technical-aeo subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]";
14339
+ const project = requireProject(input, "technical-aeo.subgraph", usage);
14340
+ const nodeKey = getString(input.values, "node-key");
14341
+ const url = getString(input.values, "url");
14342
+ if (nodeKey && url) {
14343
+ throw usageError(`Error: --node-key and --url cannot be combined
14344
+ Usage: ${usage}`, {
14345
+ message: "--node-key and --url cannot be combined",
14346
+ details: { command: "technical-aeo.subgraph", usage }
14347
+ });
14348
+ }
14349
+ await technicalAeoSubgraph(project, {
14350
+ runId: getString(input.values, "run-id"),
14351
+ nodeKey,
14352
+ url,
14353
+ hops: parseIntegerOption(input, "hops", { command: "technical-aeo.subgraph", usage, message: "--hops must be an integer" }),
14354
+ maxNodes: parseIntegerOption(input, "max-nodes", { command: "technical-aeo.subgraph", usage, message: "--max-nodes must be an integer" }),
14355
+ maxEdges: parseIntegerOption(input, "max-edges", { command: "technical-aeo.subgraph", usage, message: "--max-edges must be an integer" }),
14356
+ format: input.format
14357
+ });
14358
+ }
14359
+ },
14360
+ {
14361
+ path: ["technical-aeo", "path"],
14362
+ usage: "canonry technical-aeo path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]",
14363
+ options: {
14364
+ "run-id": stringOption(),
14365
+ "from-node-key": stringOption(),
14366
+ "from-url": stringOption(),
14367
+ "to-node-key": stringOption(),
14368
+ "to-url": stringOption(),
14369
+ "max-depth": stringOption()
14370
+ },
14371
+ run: async (input) => {
14372
+ const usage = "canonry technical-aeo path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]";
14373
+ const project = requireProject(input, "technical-aeo.path", usage);
14374
+ const fromNodeKey = getString(input.values, "from-node-key");
14375
+ const fromUrl = getString(input.values, "from-url");
14376
+ const toNodeKey = getString(input.values, "to-node-key");
14377
+ const toUrl = getString(input.values, "to-url");
14378
+ if (!toNodeKey && !toUrl) {
14379
+ throw usageError(`Error: --to-node-key or --to-url is required
14380
+ Usage: ${usage}`, {
14381
+ message: "--to-node-key or --to-url is required",
14382
+ details: { command: "technical-aeo.path", usage }
14383
+ });
14384
+ }
14385
+ if (fromNodeKey && fromUrl || toNodeKey && toUrl) {
14386
+ throw usageError(`Error: node-key and URL selectors cannot be combined
14387
+ Usage: ${usage}`, {
14388
+ message: "node-key and URL selectors cannot be combined",
14389
+ details: { command: "technical-aeo.path", usage }
14390
+ });
14391
+ }
14392
+ await technicalAeoPath(project, {
14393
+ runId: getString(input.values, "run-id"),
14394
+ fromNodeKey,
14395
+ fromUrl,
14396
+ toNodeKey,
14397
+ toUrl,
14398
+ maxDepth: parseIntegerOption(input, "max-depth", { command: "technical-aeo.path", usage, message: "--max-depth must be an integer" }),
14399
+ format: input.format
14400
+ });
14401
+ }
14402
+ },
14403
+ {
14404
+ path: ["technical-aeo", "changes"],
14405
+ usage: "canonry technical-aeo changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
14406
+ options: {
14407
+ "from-run-id": stringOption(),
14408
+ "to-run-id": stringOption(),
14409
+ scope: stringOption(),
14410
+ change: stringOption(),
14411
+ cursor: stringOption(),
14412
+ limit: stringOption()
14413
+ },
14414
+ run: async (input) => {
14415
+ const usage = "canonry technical-aeo changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]";
14416
+ const project = requireProject(input, "technical-aeo.changes", usage);
14417
+ const fromRunId = getString(input.values, "from-run-id");
14418
+ const toRunId = getString(input.values, "to-run-id");
14419
+ const scope = getString(input.values, "scope");
14420
+ const change = getString(input.values, "change");
14421
+ if (scope !== void 0 && !isSiteHealthScope(scope)) {
14422
+ throw usageError(`Error: --scope must be all, pages, or links
14423
+ Usage: ${usage}`, {
14424
+ message: "--scope must be all, pages, or links",
14425
+ details: { command: "technical-aeo.changes", usage, option: "scope", value: scope }
14426
+ });
14427
+ }
14428
+ if (change !== void 0 && !isSiteHealthChange(change)) {
14429
+ throw usageError(`Error: --change must be all, added, removed, or changed
14430
+ Usage: ${usage}`, {
14431
+ message: "--change must be all, added, removed, or changed",
14432
+ details: { command: "technical-aeo.changes", usage, option: "change", value: change }
14433
+ });
14434
+ }
14435
+ await technicalAeoChanges(project, {
14436
+ fromRunId,
14437
+ toRunId,
14438
+ scope,
14439
+ change,
14440
+ cursor: getString(input.values, "cursor"),
14441
+ limit: parseIntegerOption(input, "limit", { command: "technical-aeo.changes", usage, message: "--limit must be an integer" }),
14442
+ format: input.format
14443
+ });
14444
+ }
14445
+ },
14135
14446
  {
14136
14447
  path: ["technical-aeo", "crawl-pages"],
14137
14448
  usage: "canonry technical-aeo crawl-pages <project> [--run-id <id>] [--inventory-eligible true|false] [--fetch-state <state>] [--indexability-state <state>] [--audit-state <state>] [--sort url|path|score-asc|score-desc] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
@@ -14169,6 +14480,37 @@ Usage: ${usage}`, {
14169
14480
  });
14170
14481
  }
14171
14482
  },
14483
+ {
14484
+ path: ["technical-aeo", "page-audit"],
14485
+ usage: "canonry technical-aeo page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]",
14486
+ options: { "run-id": stringOption(), "node-key": stringOption(), url: stringOption() },
14487
+ run: async (input) => {
14488
+ const usage = "canonry technical-aeo page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]";
14489
+ const project = requireProject(input, "technical-aeo.page-audit", usage);
14490
+ const nodeKey = getString(input.values, "node-key");
14491
+ const url = getString(input.values, "url");
14492
+ if (!nodeKey && !url) {
14493
+ throw usageError(`Error: --node-key or --url is required
14494
+ Usage: ${usage}`, {
14495
+ message: "--node-key or --url is required",
14496
+ details: { command: "technical-aeo.page-audit", usage }
14497
+ });
14498
+ }
14499
+ if (nodeKey && url) {
14500
+ throw usageError(`Error: --node-key and --url cannot be combined
14501
+ Usage: ${usage}`, {
14502
+ message: "--node-key and --url cannot be combined",
14503
+ details: { command: "technical-aeo.page-audit", usage }
14504
+ });
14505
+ }
14506
+ await technicalAeoPageAudit(project, {
14507
+ runId: getString(input.values, "run-id"),
14508
+ nodeKey,
14509
+ url,
14510
+ format: input.format
14511
+ });
14512
+ }
14513
+ },
14172
14514
  {
14173
14515
  path: ["technical-aeo", "structure"],
14174
14516
  usage: "canonry technical-aeo structure <project> [--run-id <id>] [--parent-path <path>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]",
@@ -14261,6 +14603,86 @@ Usage: ${usage}`, {
14261
14603
  }
14262
14604
  }
14263
14605
  ];
14606
+ function siteHealthAlias(sourcePath, path12, usage) {
14607
+ const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
14608
+ (candidate) => candidate.path.join(" ") === sourcePath.join(" ")
14609
+ );
14610
+ if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
14611
+ return {
14612
+ ...source,
14613
+ path: path12,
14614
+ usage,
14615
+ run: async (input) => {
14616
+ try {
14617
+ await source.run(input);
14618
+ } catch (error) {
14619
+ if (!(error instanceof CliError) || error.code !== "CLI_USAGE_ERROR") throw error;
14620
+ const firstLine = error.displayMessage?.split("\n", 1)[0] ?? `Error: ${error.message}`;
14621
+ throw usageError(`${firstLine}
14622
+ Usage: ${usage}`, {
14623
+ message: error.message,
14624
+ details: { ...error.details, command: path12.join("."), usage }
14625
+ });
14626
+ }
14627
+ }
14628
+ };
14629
+ }
14630
+ var SITE_HEALTH_COMPATIBILITY_COMMANDS = [
14631
+ siteHealthAlias(
14632
+ ["technical-aeo", "crawl"],
14633
+ ["site-health", "overview"],
14634
+ "canonry site-health overview <project> [--run-id <id>] [--format json]"
14635
+ ),
14636
+ siteHealthAlias(
14637
+ ["technical-aeo", "crawl-pages"],
14638
+ ["site-health", "pages"],
14639
+ "canonry site-health pages <project> [--run-id <id>] [--inventory-eligible true|false] [--fetch-state <state>] [--indexability-state <state>] [--audit-state <state>] [--sort url|path|score-asc|score-desc] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
14640
+ ),
14641
+ siteHealthAlias(
14642
+ ["technical-aeo", "page-audit"],
14643
+ ["site-health", "page-audit"],
14644
+ "canonry site-health page-audit <project> (--node-key <key>|--url <url>) [--run-id <id>] [--format json]"
14645
+ ),
14646
+ siteHealthAlias(
14647
+ ["technical-aeo", "structure"],
14648
+ ["site-health", "structure"],
14649
+ "canonry site-health structure <project> [--run-id <id>] [--parent-path <path>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
14650
+ ),
14651
+ siteHealthAlias(
14652
+ ["technical-aeo", "links"],
14653
+ ["site-health", "links"],
14654
+ "canonry site-health links <project> [--run-id <id>] [--source-url <url>] [--target-url <url>] [--followable|--nofollow] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
14655
+ ),
14656
+ siteHealthAlias(
14657
+ ["technical-aeo", "links", "neighbors"],
14658
+ ["site-health", "neighbors"],
14659
+ "canonry site-health neighbors <project> (--node-key <key>|--url <url>) [--run-id <id>] [--limit <n>] [--format json]"
14660
+ ),
14661
+ siteHealthAlias(
14662
+ ["technical-aeo", "dead-links"],
14663
+ ["site-health", "dead-links"],
14664
+ "canonry site-health dead-links <project> [--run-id <id>] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
14665
+ ),
14666
+ siteHealthAlias(
14667
+ ["technical-aeo", "subgraph"],
14668
+ ["site-health", "subgraph"],
14669
+ "canonry site-health subgraph <project> [--run-id <id>] [--node-key <key>|--url <url>] [--hops <n>] [--max-nodes <n>] [--max-edges <n>] [--format json]"
14670
+ ),
14671
+ siteHealthAlias(
14672
+ ["technical-aeo", "path"],
14673
+ ["site-health", "path"],
14674
+ "canonry site-health path <project> (--to-node-key <key>|--to-url <url>) [--run-id <id>] [--from-node-key <key>|--from-url <url>] [--max-depth <n>] [--format json]"
14675
+ ),
14676
+ siteHealthAlias(
14677
+ ["technical-aeo", "changes"],
14678
+ ["site-health", "changes"],
14679
+ "canonry site-health changes <project> [--from-run-id <id>] [--to-run-id <id>] [--scope all|pages|links] [--change all|added|removed|changed] [--cursor <cursor>] [--limit <n>] [--format json|jsonl]"
14680
+ )
14681
+ ];
14682
+ var TECHNICAL_AEO_CLI_COMMANDS = [
14683
+ ...TECHNICAL_AEO_CLI_COMMANDS_BASE,
14684
+ ...SITE_HEALTH_COMPATIBILITY_COMMANDS
14685
+ ];
14264
14686
 
14265
14687
  // src/commands/visibility-stats.ts
14266
14688
  async function showVisibilityStats(project, opts) {
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-YSESZNVT.js";
3
+ } from "./chunk-ZWESOFA5.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-OSHRDV36.js";
7
- import "./chunk-VOL2BSM5.js";
8
- import "./chunk-VIKPFI6F.js";
6
+ } from "./chunk-CPASKGUE.js";
7
+ import "./chunk-ABNN2ZVR.js";
8
+ import "./chunk-AMPT2EP7.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-VOL2BSM5.js";
4
- import "./chunk-VIKPFI6F.js";
3
+ } from "./chunk-ABNN2ZVR.js";
4
+ import "./chunk-AMPT2EP7.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-OSHRDV36.js";
6
+ } from "./chunk-CPASKGUE.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-VIKPFI6F.js";
9
+ } from "./chunk-AMPT2EP7.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -111,8 +111,8 @@ var CANONRY_MCP_TOOLKITS = [
111
111
  {
112
112
  name: "monitoring",
113
113
  title: "Measurement, runs, insights, health",
114
- description: "Inspect portfolio and Property measurement, question results, run history, intelligence insights, and health timelines.",
115
- whenToLoad: "Load when finding weak Properties, reviewing answers and competitors, checking data quality, comparing runs, or investigating regressions."
114
+ description: "Inspect portfolio and Property measurement, question results, run history, intelligence insights, health timelines, and bounded Site Health reads (overview, page audit evidence, focused subgraphs, paths, and scan changes).",
115
+ whenToLoad: "Load when finding weak Properties, reviewing answers and competitors, checking data quality, tying a Site Health page score to concrete findings, investigating site architecture, comparing scans, or diagnosing regressions."
116
116
  },
117
117
  {
118
118
  name: "setup",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.155.0",
3
+ "version": "4.156.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -53,6 +53,8 @@
53
53
  "cron-parser": "^5.5.0",
54
54
  "drizzle-orm": "^0.45.2",
55
55
  "fastify": "^5.8.5",
56
+ "graphology": "0.26.0",
57
+ "graphology-layout-forceatlas2": "0.10.1",
56
58
  "node-cron": "^4.2.1",
57
59
  "openai": "^6.0.0",
58
60
  "pdf-lib": "^1.17.1",
@@ -67,26 +69,26 @@
67
69
  "tsup": "^8.5.1",
68
70
  "tsx": "^4.19.0",
69
71
  "@ainyc/canonry-api-client": "0.0.0",
72
+ "@ainyc/canonry-config": "0.0.0",
70
73
  "@ainyc/canonry-api-routes": "0.0.0",
71
74
  "@ainyc/canonry-contracts": "0.0.0",
72
75
  "@ainyc/canonry-db": "0.0.0",
73
- "@ainyc/canonry-config": "0.0.0",
74
76
  "@ainyc/canonry-integration-bing": "0.0.0",
75
77
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
76
- "@ainyc/canonry-integration-google": "0.0.0",
77
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
78
78
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
79
- "@ainyc/canonry-integration-google-places": "0.0.0",
79
+ "@ainyc/canonry-integration-google": "0.0.0",
80
80
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
81
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
82
+ "@ainyc/canonry-integration-traffic": "0.0.0",
81
83
  "@ainyc/canonry-integration-wordpress": "0.0.0",
82
- "@ainyc/canonry-provider-cdp": "0.0.0",
83
84
  "@ainyc/canonry-intelligence": "0.0.0",
84
- "@ainyc/canonry-integration-traffic": "0.0.0",
85
85
  "@ainyc/canonry-provider-claude": "0.0.0",
86
+ "@ainyc/canonry-provider-cdp": "0.0.0",
86
87
  "@ainyc/canonry-provider-gemini": "0.0.0",
88
+ "@ainyc/canonry-provider-openai": "0.0.0",
87
89
  "@ainyc/canonry-provider-local": "0.0.0",
88
- "@ainyc/canonry-provider-perplexity": "0.0.0",
89
- "@ainyc/canonry-provider-openai": "0.0.0"
90
+ "@ainyc/canonry-integration-google-places": "0.0.0",
91
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
90
92
  },
91
93
  "scripts": {
92
94
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",