@canonry/canonry 7.77.1 → 7.78.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 (139) hide show
  1. package/assets/agent-workspace/skills/aero/references/seo-analysis.md +13 -2
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -2
  3. package/assets/assets/{AgentSetupPage-BZXWC3x6.js → AgentSetupPage-BGdvyQFL.js} +4 -4
  4. package/assets/assets/{AuditHistoryPanel-DcrUuwRu.js → AuditHistoryPanel-Bb5wq-Bk.js} +1 -1
  5. package/assets/assets/{BacklinksPage-BSpHJBiy.js → BacklinksPage-i1cfxTDj.js} +1 -1
  6. package/assets/assets/{HistoryPage-0a0CK9zF.js → HistoryPage-C2NRdNrn.js} +1 -1
  7. package/assets/assets/MeasurementPropertyPage-CwPuJMBy.js +4 -0
  8. package/assets/assets/ProjectPage-Bbnbg7c9.js +11 -0
  9. package/assets/assets/{RunsPage-D0vbz1Ho.js → RunsPage-CJHJ7kHk.js} +1 -1
  10. package/assets/assets/{SettingsPage-DjRm5cmn.js → SettingsPage-r8VrtFyJ.js} +1 -1
  11. package/assets/assets/SiteHealthSection-D1oSuo9O.js +2 -0
  12. package/assets/assets/ToneBadge-CUe-WrzN.js +60 -0
  13. package/assets/assets/{TrafficPage-Z8Z_z0k9.js → TrafficPage-DY2ykwH1.js} +1 -1
  14. package/assets/assets/TrafficSourceDetailPage-D3KHgRvF.js +1 -0
  15. package/assets/assets/{account-context-zaSxLBst.js → account-context-B50QqDEA.js} +1 -1
  16. package/assets/assets/{card-COqALGzB.js → card-BE2fqwVq.js} +1 -1
  17. package/assets/assets/{extract-error-message-DKve0SNr.js → extract-error-message-xuN_xeRJ.js} +1 -1
  18. package/assets/assets/{format-helpers-DKrHlaV7.js → format-helpers-CiC6gALY.js} +1 -1
  19. package/assets/assets/{index-CKZK95pI.css → index-CHHZDc6d.css} +1 -1
  20. package/assets/assets/{index-fzqtwr6m.js → index-Dfm8z5cG.js} +23 -23
  21. package/assets/assets/{query-client-COM9uzxL.js → query-client-C5GhDMY7.js} +1 -1
  22. package/assets/assets/react-query-CmHyfYFq.js +1 -0
  23. package/assets/assets/{react-sigma_core.esm.min-e91U3Myi.js → react-sigma_core.esm.min-Bjdtl0TQ.js} +1 -1
  24. package/assets/assets/{service-BgFTYqJA.js → service-B2i_ZzWP.js} +1 -1
  25. package/assets/index.html +8 -8
  26. package/dist/{ads-M6IAQDG7.js → ads-MIR5MCHL.js} +3 -3
  27. package/dist/{agent-X2T5X5K3.js → agent-IXRL5RBI.js} +7 -7
  28. package/dist/{agent-ask-55LQH4TI.js → agent-ask-K42H6H2T.js} +3 -3
  29. package/dist/{agent-conversations-O6WC2NPV.js → agent-conversations-2T3W4DHO.js} +3 -3
  30. package/dist/{agent-memory-6DWFMB4I.js → agent-memory-LJBNSTSP.js} +3 -3
  31. package/dist/{agent-providers-Q4YDVQX3.js → agent-providers-QR4LY3AP.js} +3 -3
  32. package/dist/{agent-transcript-SJKCP2AO.js → agent-transcript-JIHJWHV6.js} +3 -3
  33. package/dist/{analytics-4JOQMWH7.js → analytics-JRV7DBBU.js} +3 -3
  34. package/dist/{apply-EAKKKIZD.js → apply-ZFWDSAV4.js} +3 -3
  35. package/dist/{backfill-PASKIVGP.js → backfill-LU2AQIPY.js} +9 -9
  36. package/dist/{backlinks-TICPC323.js → backlinks-NAC2373F.js} +3 -3
  37. package/dist/{bing-MC7TSL4E.js → bing-L3LDBJOC.js} +3 -3
  38. package/dist/{bootstrap-X2BW64W7.js → bootstrap-I6DA4KWB.js} +10 -10
  39. package/dist/{cdp-TCJKITCR.js → cdp-4F74XLTH.js} +7 -7
  40. package/dist/{chunk-CCNPBUQ5.js → chunk-367K6XVX.js} +2 -2
  41. package/dist/{chunk-JPOIDGGF.js → chunk-3MGM3U6V.js} +1 -1
  42. package/dist/{chunk-2BDWXHYF.js → chunk-45RGH7XI.js} +3 -3
  43. package/dist/{chunk-RPJJOBKN.js → chunk-6ACWXQNZ.js} +3 -1
  44. package/dist/{chunk-B7JHQ3FW.js → chunk-6P6YBNKN.js} +7 -7
  45. package/dist/{chunk-YMOQSEO3.js → chunk-6PRFVLIK.js} +15 -4
  46. package/dist/{chunk-D5XPB7MM.js → chunk-6VBLPCCX.js} +1 -1
  47. package/dist/{chunk-5QR7D4RQ.js → chunk-7HB6QLX4.js} +1 -1
  48. package/dist/{chunk-7UVCZZQJ.js → chunk-AI44ZCMK.js} +1 -1
  49. package/dist/{chunk-X7UC755Q.js → chunk-C5WSQXP5.js} +26 -2
  50. package/dist/{chunk-CQMNWC5K.js → chunk-EFMZSYRD.js} +160 -26
  51. package/dist/{chunk-FXIZBJZ4.js → chunk-EHXNYXAS.js} +3 -3
  52. package/dist/{chunk-2WBT6TEJ.js → chunk-FCYGY4FT.js} +1 -1
  53. package/dist/{chunk-XYYWKCJQ.js → chunk-JERATA5R.js} +1 -1
  54. package/dist/{chunk-P3BWUKJL.js → chunk-JEUL5QZP.js} +4 -4
  55. package/dist/{chunk-PSNTEGNY.js → chunk-KZB6HLFT.js} +1 -1
  56. package/dist/{chunk-UGNNR7EO.js → chunk-L2KMR7CM.js} +2 -2
  57. package/dist/{chunk-H4ANN32B.js → chunk-L6F2HOQH.js} +8 -8
  58. package/dist/{chunk-GDKMVOZI.js → chunk-M3BUP6XD.js} +104 -13
  59. package/dist/{chunk-4QHTXT4A.js → chunk-MUYTDD35.js} +40 -4
  60. package/dist/{chunk-TESDUMVK.js → chunk-O4WBYEGV.js} +1 -1
  61. package/dist/{chunk-FCGYYZHL.js → chunk-PM2DB3HE.js} +164 -10
  62. package/dist/{chunk-TRW4XLQK.js → chunk-QXSOXDUR.js} +2 -2
  63. package/dist/{chunk-2SCPPWRW.js → chunk-RSVANBNW.js} +4 -4
  64. package/dist/{chunk-UB65XBH2.js → chunk-SAHH2FXO.js} +3 -3
  65. package/dist/{chunk-46KV7CZU.js → chunk-SOQVI7EZ.js} +2 -2
  66. package/dist/{chunk-Z2FY6AGZ.js → chunk-TZRYVW75.js} +2 -2
  67. package/dist/{chunk-C3UFOS7J.js → chunk-UH44UCRM.js} +2 -2
  68. package/dist/{chunk-TJYS2XCI.js → chunk-UQOC5ZQE.js} +4 -4
  69. package/dist/{chunk-2BRVGCVU.js → chunk-VLOCDWCS.js} +7 -1
  70. package/dist/{chunk-6EZQHF67.js → chunk-WCFANFR2.js} +371 -340
  71. package/dist/{chunk-ZXYCPRNY.js → chunk-WSWHLKKJ.js} +1 -1
  72. package/dist/{chunk-NDTU7FGL.js → chunk-WVCDUKJ2.js} +1 -1
  73. package/dist/{chunk-JZT3ZRD3.js → chunk-WXIL2EFM.js} +4 -4
  74. package/dist/{chunk-JSCO223C.js → chunk-ZHFE6J3A.js} +1 -1
  75. package/dist/{chunk-HD2VVIPD.js → chunk-ZUC7CJVD.js} +5 -5
  76. package/dist/{citations-IA3OCV53.js → citations-2XELMCCV.js} +3 -3
  77. package/dist/{cli-catalog-ZVFLJXID.js → cli-catalog-KC6JYHSW.js} +3 -3
  78. package/dist/{cli-commands-O7BYY3W5.js → cli-commands-WCOAB6XU.js} +7 -7
  79. package/dist/cli.js +13 -13
  80. package/dist/{competitor-4FVAZAQZ.js → competitor-IK4GYTZO.js} +3 -3
  81. package/dist/{content-OVBF6U7Z.js → content-SMQPJXPC.js} +3 -3
  82. package/dist/{dashboard-UGZSSIWK.js → dashboard-EE24ROVG.js} +6 -6
  83. package/dist/{demo-5CU3JDHC.js → demo-7XH2N65A.js} +1 -1
  84. package/dist/{demo-server-43QV3UKM.js → demo-server-CVB65UYW.js} +12 -12
  85. package/dist/{discover-R5BYAPOU.js → discover-3KHUCVE7.js} +3 -3
  86. package/dist/{discover-eval-3FA4BR2L.js → discover-eval-DOEP4ZQ7.js} +3 -3
  87. package/dist/{doctor-2Q5LVV2P.js → doctor-3T6RKSE7.js} +3 -3
  88. package/dist/{evidence-LMGRT5IO.js → evidence-4NFL5KM3.js} +3 -3
  89. package/dist/{export-cmd-LBLZ273Z.js → export-cmd-BQC6XLJ7.js} +3 -3
  90. package/dist/{ga-PBIWRU67.js → ga-HBRA4UUK.js} +3 -3
  91. package/dist/{gbp-NYG7SW6P.js → gbp-2WMBTDJV.js} +3 -3
  92. package/dist/{google-57XEYVOU.js → google-JFUE4BNJ.js} +4 -4
  93. package/dist/{google-marketing-XPE6CVQI.js → google-marketing-5ROIKBC7.js} +1 -1
  94. package/dist/{health-cmd-SJVYQZQL.js → health-cmd-7BMVCCG2.js} +3 -3
  95. package/dist/{history-5XYZNWGP.js → history-QA4ZXCVH.js} +3 -3
  96. package/dist/index.d.ts +216 -23
  97. package/dist/index.js +26 -26
  98. package/dist/{init-KXR6PJEQ.js → init-O4G2MDMC.js} +12 -12
  99. package/dist/{insights-L7OOH34I.js → insights-K4UX2PSI.js} +3 -3
  100. package/dist/intelligence-service-D7DS4UKT.js +16 -0
  101. package/dist/{journeys-VXBGDFBO.js → journeys-UWEWJXGT.js} +3 -3
  102. package/dist/{keys-YMB5HAAG.js → keys-CL5YYE7Q.js} +3 -3
  103. package/dist/{keyword-RZEMKH3K.js → keyword-IFCC5IZT.js} +3 -3
  104. package/dist/{logs-ONZWSYXS.js → logs-2A3HBUBK.js} +3 -3
  105. package/dist/{mcp-KK6EFHAZ.js → mcp-B6L7PKZ4.js} +4 -4
  106. package/dist/mcp.js +10 -10
  107. package/dist/{measurement-plan-LE7INWW6.js → measurement-plan-GYSO6CXX.js} +5 -5
  108. package/dist/{notify-T6T5LYRR.js → notify-XM23I2B3.js} +3 -3
  109. package/dist/{organic-evidence-P2GZ766W.js → organic-evidence-U7HZMXKP.js} +3 -3
  110. package/dist/{overview-P2AILIWJ.js → overview-TSAK5DQP.js} +4 -4
  111. package/dist/{project-7MHQSOJD.js → project-L7DLDEPI.js} +3 -3
  112. package/dist/{query-EUDMU6L7.js → query-FM5HDHO4.js} +3 -3
  113. package/dist/{research-4DASCVGY.js → research-7G5JU44N.js} +3 -3
  114. package/dist/{results-export-GKXHX55L.js → results-export-JJDGG6ZP.js} +3 -3
  115. package/dist/{run-6LJGMJ5S.js → run-ZVIQVG23.js} +4 -4
  116. package/dist/{schedule-D74P2BO5.js → schedule-TV7LWSYI.js} +3 -3
  117. package/dist/{search-W7AOYWRM.js → search-7KRZLZ6S.js} +3 -3
  118. package/dist/{serve-UNVCQSRC.js → serve-WSOG525W.js} +27 -27
  119. package/dist/{session-6NGNVCKP.js → session-OUGQSGVV.js} +14 -14
  120. package/dist/{settings-LGD64PFE.js → settings-AOXQJOHN.js} +3 -3
  121. package/dist/{skills-CYHWYN42.js → skills-DWXA55QA.js} +2 -2
  122. package/dist/{snapshot-PDYGSZZH.js → snapshot-B44CXKFU.js} +3 -3
  123. package/dist/{src-SXB3MOP3.js → src-IBGSUYEG.js} +2 -2
  124. package/dist/{status-UZ4SE7ZP.js → status-RNBSFZ6Q.js} +4 -4
  125. package/dist/{technical-aeo-BNH7NV44.js → technical-aeo-UAOKK5QH.js} +3 -3
  126. package/dist/{telemetry-UWDS7QNJ.js → telemetry-55PUOMA7.js} +4 -4
  127. package/dist/{tools-EDPBQCI2.js → tools-HRGP4FXN.js} +11 -11
  128. package/dist/{traffic-NI6ACQO4.js → traffic-M4OHG5JW.js} +5 -5
  129. package/dist/{users-XTZ65NLC.js → users-GU7MULRM.js} +3 -3
  130. package/dist/{visibility-stats-EPF6MHYQ.js → visibility-stats-4IDTDXDC.js} +3 -3
  131. package/dist/{wordpress-ZACJ3H5M.js → wordpress-CYXB4R5Y.js} +3 -3
  132. package/package.json +1 -1
  133. package/assets/assets/MeasurementPropertyPage-DbfTFN3F.js +0 -4
  134. package/assets/assets/ProjectPage-D0m-Xpp3.js +0 -9
  135. package/assets/assets/SiteHealthSection-BCdPVFEJ.js +0 -2
  136. package/assets/assets/ToneBadge-DSJSzqI6.js +0 -60
  137. package/assets/assets/TrafficSourceDetailPage-DJyDO527.js +0 -1
  138. package/assets/assets/react-query-BVRWpXWq.js +0 -1
  139. package/dist/intelligence-service-NXZYQJBH.js +0 -16
@@ -13524,6 +13524,8 @@ var STORED_READ_OPERATIONS = /* @__PURE__ */ new Set([
13524
13524
  "GET /api/v1/projects/{name}/seo/keyword-metrics",
13525
13525
  "GET /api/v1/projects/{name}/seo/domain-intelligence",
13526
13526
  "GET /api/v1/projects/{name}/seo/ranked-keywords",
13527
+ "GET /api/v1/projects/{name}/seo/competitors",
13528
+ "GET /api/v1/projects/{name}/seo/keyword-gaps",
13527
13529
  "GET /api/v1/projects/{name}/seo/market-rankings",
13528
13530
  "GET /api/v1/projects/{name}/seo/market-rankings/summary",
13529
13531
  "GET /api/v1/projects/{name}/gbp/local-post-attempt",
@@ -21270,7 +21272,7 @@ function uncheckedSourcesLabel(value) {
21270
21272
 
21271
21273
  // ../contracts/src/seo.ts
21272
21274
  import { z as z66 } from "zod";
21273
- var seoCapabilitySchema = z66.enum(["keyword-metrics", "domain-intelligence", "ranked-keywords", "market-rankings"]);
21275
+ var seoCapabilitySchema = z66.enum(["keyword-metrics", "domain-intelligence", "ranked-keywords", "market-rankings", "competitor-discovery", "keyword-gaps"]);
21274
21276
  var SeoCapabilities = seoCapabilitySchema.enum;
21275
21277
  var seoProviderSchema = z66.enum(["dataforseo"]);
21276
21278
  var SeoProviders = seoProviderSchema.enum;
@@ -21318,8 +21320,10 @@ var seoRankingSettingsSchema = z66.object({
21318
21320
  var seoProfileInputSchema = z66.object({
21319
21321
  name: z66.string().trim().min(1).max(120),
21320
21322
  connectionId: z66.string().min(1).max(100),
21321
- capabilities: z66.array(seoCapabilitySchema).min(1).max(4),
21323
+ capabilities: z66.array(seoCapabilitySchema).min(1).max(6),
21322
21324
  targets: z66.array(seoTargetSchema).max(20),
21325
+ /** Explicit comparison domains; saving them never starts provider work. */
21326
+ competitors: z66.array(seoTargetSchema).max(5).optional(),
21323
21327
  keywords: z66.array(z66.string().trim().min(1).max(80).regex(/^\S+(?:\s+\S+){0,9}$/, "Use at most 10 words per keyword.")).max(700),
21324
21328
  locationCode: z66.number().int().positive(),
21325
21329
  languageCode: z66.string().min(2).max(10),
@@ -21431,6 +21435,41 @@ var seoRankedKeywordSchema = z66.object({
21431
21435
  serpFeatures: z66.array(z66.string()),
21432
21436
  provenance: seoProvenanceSchema
21433
21437
  }).strict();
21438
+ var seoCompetitorSchema = z66.object({
21439
+ target: seoTargetSchema,
21440
+ domain: z66.string(),
21441
+ sharedKeywords: z66.number().int().nonnegative().nullable(),
21442
+ /** The competitor's average organic position across shared searches. */
21443
+ averagePosition: z66.number().nonnegative().nullable(),
21444
+ organicKeywords: z66.number().int().nonnegative().nullable(),
21445
+ estimatedOrganicTraffic: z66.number().nonnegative().nullable(),
21446
+ provenance: seoProvenanceSchema
21447
+ }).strict();
21448
+ var seoKeywordGapModeSchema = z66.enum(["shared", "missing"]);
21449
+ var SeoKeywordGapModes = seoKeywordGapModeSchema.enum;
21450
+ var seoKeywordGapKindSchema = z66.enum(["shared", "missing", "outranked"]);
21451
+ var SeoKeywordGapKinds = seoKeywordGapKindSchema.enum;
21452
+ var seoKeywordGapSchema = z66.object({
21453
+ target: seoTargetSchema,
21454
+ competitor: seoTargetSchema,
21455
+ keyword: z66.string(),
21456
+ targetRankOrganic: z66.number().int().positive().nullable(),
21457
+ competitorRankOrganic: z66.number().int().positive(),
21458
+ targetUrl: z66.string().nullable(),
21459
+ competitorUrl: z66.string().nullable(),
21460
+ searchVolume: z66.number().int().nonnegative().nullable(),
21461
+ keywordDifficulty: z66.number().min(0).max(100).nullable().describe("Provider difficulty score on a 0\u2013100 scale; not a percentage."),
21462
+ cpcUsd: z66.number().nonnegative().nullable(),
21463
+ kind: seoKeywordGapKindSchema,
21464
+ /** Target organic position minus competitor position; positive means the competitor is ahead. Null when the target is absent. */
21465
+ rankGap: z66.number().int().nullable(),
21466
+ provenance: seoProvenanceSchema
21467
+ }).strict();
21468
+ function seoKeywordGapComparison(targetRankOrganic, competitorRankOrganic) {
21469
+ if (targetRankOrganic === null) return { kind: SeoKeywordGapKinds.missing, rankGap: null };
21470
+ const rankGap = targetRankOrganic - competitorRankOrganic;
21471
+ return { kind: rankGap > 0 ? SeoKeywordGapKinds.outranked : SeoKeywordGapKinds.shared, rankGap };
21472
+ }
21434
21473
  var seoMarketRankingContextSchema = z66.object({
21435
21474
  marketKey: z66.string(),
21436
21475
  marketLabel: z66.string(),
@@ -21465,16 +21504,21 @@ var SeoMarketRankingOutcomes = seoMarketRankingOutcomeSchema.enum;
21465
21504
  var seoMarketRankingRowSchema = seoMarketRankingSchema.extend({
21466
21505
  outcome: seoMarketRankingOutcomeSchema,
21467
21506
  /** A tracked competitor ranks above the target in these results, or ranks while the target is not in them. */
21468
- rivalAhead: z66.boolean()
21507
+ rivalAhead: z66.boolean(),
21508
+ topCompetitor: seoMarketRankingCompetitorSchema.nullable(),
21509
+ /** Target position minus the best tracked competitor's position; null if either position is unavailable. */
21510
+ rankGap: z66.number().int().nullable()
21469
21511
  }).strict();
21470
21512
  var seoResultsSchema = z66.object({
21471
21513
  keywordMetrics: z66.array(seoKeywordMetricsSchema),
21472
21514
  domainIntelligence: z66.array(seoDomainIntelligenceSchema),
21473
21515
  rankedKeywords: z66.array(seoRankedKeywordSchema),
21474
- marketRankings: z66.array(seoMarketRankingSchema)
21516
+ marketRankings: z66.array(seoMarketRankingSchema),
21517
+ competitors: z66.array(seoCompetitorSchema),
21518
+ keywordGaps: z66.array(seoKeywordGapSchema)
21475
21519
  }).strict();
21476
21520
  function emptySeoResults() {
21477
- return { keywordMetrics: [], domainIntelligence: [], rankedKeywords: [], marketRankings: [] };
21521
+ return { keywordMetrics: [], domainIntelligence: [], rankedKeywords: [], marketRankings: [], competitors: [], keywordGaps: [] };
21478
21522
  }
21479
21523
  function marketRegionName(marketLabel) {
21480
21524
  const comma = marketLabel.lastIndexOf(",");
@@ -21514,6 +21558,8 @@ var seoTaskSchema = z66.object({
21514
21558
  id: z66.string(),
21515
21559
  capability: seoCapabilitySchema,
21516
21560
  target: seoTargetSchema.nullable(),
21561
+ /** Keyword gaps: the comparison domain and shared/missing population, including failed tasks. */
21562
+ comparison: z66.object({ competitor: seoTargetSchema, mode: seoKeywordGapModeSchema }).strict().nullable(),
21517
21563
  status: seoRunStatusSchema,
21518
21564
  error: z66.string().nullable(),
21519
21565
  remoteTaskId: z66.string().nullable(),
@@ -21527,7 +21573,9 @@ var seoResultCountsSchema = z66.object({
21527
21573
  keywordMetrics: z66.number().int().nonnegative(),
21528
21574
  domainIntelligence: z66.number().int().nonnegative(),
21529
21575
  rankedKeywords: z66.number().int().nonnegative(),
21530
- marketRankings: z66.number().int().nonnegative()
21576
+ marketRankings: z66.number().int().nonnegative(),
21577
+ competitors: z66.number().int().nonnegative(),
21578
+ keywordGaps: z66.number().int().nonnegative()
21531
21579
  }).strict();
21532
21580
  var seoRunDetailSchema = seoRunSchema.extend({
21533
21581
  tasks: z66.array(seoTaskSchema),
@@ -21551,6 +21599,18 @@ var seoPageShape = {
21551
21599
  var seoKeywordMetricsListSchema = z66.object({ ...seoPageShape, items: z66.array(seoKeywordMetricsSchema) }).strict();
21552
21600
  var seoDomainIntelligenceListSchema = z66.object({ ...seoPageShape, items: z66.array(seoDomainIntelligenceSchema) }).strict();
21553
21601
  var seoRankedKeywordListSchema = z66.object({ ...seoPageShape, items: z66.array(seoRankedKeywordSchema) }).strict();
21602
+ var seoCompetitorListSchema = z66.object({ ...seoPageShape, items: z66.array(seoCompetitorSchema) }).strict();
21603
+ var seoKeywordGapListSchema = z66.object({ ...seoPageShape, items: z66.array(seoKeywordGapSchema) }).strict();
21604
+ var seoCompetitorSortSchema = z66.enum(["domain", "-domain", "shared", "-shared", "position", "-position", "traffic", "-traffic"]);
21605
+ var SeoCompetitorSorts = seoCompetitorSortSchema.enum;
21606
+ var seoKeywordGapSortSchema = z66.enum(["keyword", "-keyword", "kind", "-kind", "target-rank", "-target-rank", "competitor-rank", "-competitor-rank", "rank-gap", "-rank-gap", "volume", "-volume"]);
21607
+ var SeoKeywordGapSorts = seoKeywordGapSortSchema.enum;
21608
+ var seoCompetitorQuerySchema = seoEvidenceQuerySchema.extend({ sort: seoCompetitorSortSchema.optional() }).strict();
21609
+ var seoKeywordGapQuerySchema = seoEvidenceQuerySchema.extend({
21610
+ /** Shared includes the outranked subset; missing and outranked select their exact category. */
21611
+ kind: seoKeywordGapKindSchema.optional(),
21612
+ sort: seoKeywordGapSortSchema.optional()
21613
+ }).strict();
21554
21614
  var seoMarketRankingFilterSchema = z66.enum(["needs-work", "page-1", "ranks-11-20", "ranks-21-plus", "not-ranked", "partly-checked", "rival-ahead", "failed", "cancelled"]);
21555
21615
  var SeoMarketRankingFilters = seoMarketRankingFilterSchema.enum;
21556
21616
  var seoMarketRankingSortSchema = z66.enum(["market", "rank", "-rank", "query", "-query"]);
@@ -21619,7 +21679,9 @@ var seoOverviewSchema = z66.object({
21619
21679
  domainIntelligenceCount: z66.number().int().nonnegative(),
21620
21680
  rankedKeywordCount: z66.number().int().nonnegative(),
21621
21681
  /** Rows the market-rankings read returns for the evidence refresh, searches that failed or never ran included. */
21622
- marketRankingCount: z66.number().int().nonnegative()
21682
+ marketRankingCount: z66.number().int().nonnegative(),
21683
+ competitorCount: z66.number().int().nonnegative(),
21684
+ keywordGapCount: z66.number().int().nonnegative()
21623
21685
  }).strict();
21624
21686
  var seoMicrosSchema = z66.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
21625
21687
  var seoOperatorTaskSchema = z66.object({
@@ -26325,8 +26387,16 @@ function outcomeOf(row) {
26325
26387
  }
26326
26388
  }
26327
26389
  function marketRankingRow(row) {
26328
- const best = bestCompetitorRank(row.competitors);
26329
- return { ...row, outcome: outcomeOf(row), rivalAhead: best !== null && (row.rankOrganic === null || best < row.rankOrganic) };
26390
+ const hasResult = row.searchStatus === SeoMarketRankingSearchStatuses.completed || row.searchStatus === SeoMarketRankingSearchStatuses.partial;
26391
+ const topCompetitor = hasResult ? [...row.competitors].sort((a, b) => a.rankOrganic - b.rankOrganic || compareText(a.domain, b.domain))[0] ?? null : null;
26392
+ const best = topCompetitor?.rankOrganic ?? null;
26393
+ return {
26394
+ ...row,
26395
+ outcome: outcomeOf(row),
26396
+ rivalAhead: best !== null && (row.rankOrganic === null || best < row.rankOrganic),
26397
+ topCompetitor,
26398
+ rankGap: row.rankOrganic === null || best === null ? null : row.rankOrganic - best
26399
+ };
26330
26400
  }
26331
26401
  function matchesMarketRankingFilter(row, filter) {
26332
26402
  switch (filter) {
@@ -26455,6 +26525,77 @@ function groupMarketRankings(rows, scope) {
26455
26525
  return [...groups.values()].map((group) => ({ key: group.key, label: group.label, kind: group.kind, counts: countMarketRankings(group.rows) })).sort((a, b) => b.counts.ranks11To20 - a.counts.ranks11To20 || b.counts.ranks21Plus - a.counts.ranks21Plus || compareText(a.label, b.label) || compareText(a.key, b.key));
26456
26526
  }
26457
26527
 
26528
+ // ../contracts/src/seo-research-sort.ts
26529
+ function numberOrder(value, direction, identity) {
26530
+ return (a, b) => {
26531
+ const left = value(a);
26532
+ const right = value(b);
26533
+ if (left === null || right === null) return left === right ? identity(a, b) : left === null ? 1 : -1;
26534
+ return direction * (left - right) || identity(a, b);
26535
+ };
26536
+ }
26537
+ function textOrder(value, direction, identity) {
26538
+ return (a, b) => direction * compareText(value(a).toLowerCase(), value(b).toLowerCase()) || identity(a, b);
26539
+ }
26540
+ var competitorIdentity = (a, b) => compareText(a.target.value, b.target.value) || compareText(a.domain, b.domain);
26541
+ var gapIdentity = (a, b) => compareText(a.target.value, b.target.value) || compareText(a.competitor.value, b.competitor.value) || compareText(a.keyword, b.keyword);
26542
+ function compareSeoCompetitorsBy(sort) {
26543
+ switch (sort) {
26544
+ case SeoCompetitorSorts.domain:
26545
+ return textOrder((row) => row.domain, 1, competitorIdentity);
26546
+ case SeoCompetitorSorts["-domain"]:
26547
+ return textOrder((row) => row.domain, -1, competitorIdentity);
26548
+ case SeoCompetitorSorts.shared:
26549
+ return numberOrder((row) => row.sharedKeywords, 1, competitorIdentity);
26550
+ case SeoCompetitorSorts["-shared"]:
26551
+ return numberOrder((row) => row.sharedKeywords, -1, competitorIdentity);
26552
+ case SeoCompetitorSorts.position:
26553
+ return numberOrder((row) => row.averagePosition, 1, competitorIdentity);
26554
+ case SeoCompetitorSorts["-position"]:
26555
+ return numberOrder((row) => row.averagePosition, -1, competitorIdentity);
26556
+ case SeoCompetitorSorts.traffic:
26557
+ return numberOrder((row) => row.estimatedOrganicTraffic, 1, competitorIdentity);
26558
+ case SeoCompetitorSorts["-traffic"]:
26559
+ return numberOrder((row) => row.estimatedOrganicTraffic, -1, competitorIdentity);
26560
+ default: {
26561
+ const unknown = sort;
26562
+ return unknown;
26563
+ }
26564
+ }
26565
+ }
26566
+ function compareSeoKeywordGapsBy(sort) {
26567
+ switch (sort) {
26568
+ case SeoKeywordGapSorts.keyword:
26569
+ return textOrder((row) => row.keyword, 1, gapIdentity);
26570
+ case SeoKeywordGapSorts["-keyword"]:
26571
+ return textOrder((row) => row.keyword, -1, gapIdentity);
26572
+ case SeoKeywordGapSorts.kind:
26573
+ return textOrder((row) => row.kind, 1, gapIdentity);
26574
+ case SeoKeywordGapSorts["-kind"]:
26575
+ return textOrder((row) => row.kind, -1, gapIdentity);
26576
+ case SeoKeywordGapSorts["target-rank"]:
26577
+ return numberOrder((row) => row.targetRankOrganic, 1, gapIdentity);
26578
+ case SeoKeywordGapSorts["-target-rank"]:
26579
+ return numberOrder((row) => row.targetRankOrganic, -1, gapIdentity);
26580
+ case SeoKeywordGapSorts["competitor-rank"]:
26581
+ return numberOrder((row) => row.competitorRankOrganic, 1, gapIdentity);
26582
+ case SeoKeywordGapSorts["-competitor-rank"]:
26583
+ return numberOrder((row) => row.competitorRankOrganic, -1, gapIdentity);
26584
+ case SeoKeywordGapSorts["rank-gap"]:
26585
+ return numberOrder((row) => row.rankGap, 1, gapIdentity);
26586
+ case SeoKeywordGapSorts["-rank-gap"]:
26587
+ return numberOrder((row) => row.rankGap, -1, gapIdentity);
26588
+ case SeoKeywordGapSorts.volume:
26589
+ return numberOrder((row) => row.searchVolume, 1, gapIdentity);
26590
+ case SeoKeywordGapSorts["-volume"]:
26591
+ return numberOrder((row) => row.searchVolume, -1, gapIdentity);
26592
+ default: {
26593
+ const unknown = sort;
26594
+ return unknown;
26595
+ }
26596
+ }
26597
+ }
26598
+
26458
26599
  export {
26459
26600
  GBP_POSTS_WRITE_SCOPE,
26460
26601
  GBP_LIVE_READ_SCOPE,
@@ -27699,6 +27840,11 @@ export {
27699
27840
  seoRefreshIntentInputSchema,
27700
27841
  seoQuoteSchema,
27701
27842
  seoSyncInputSchema,
27843
+ seoKeywordGapModeSchema,
27844
+ SeoKeywordGapModes,
27845
+ seoKeywordGapKindSchema,
27846
+ SeoKeywordGapKinds,
27847
+ seoKeywordGapComparison,
27702
27848
  SeoMarketRankingSearchStatuses,
27703
27849
  SeoMarketRankingOutcomes,
27704
27850
  emptySeoResults,
@@ -27710,6 +27856,12 @@ export {
27710
27856
  seoKeywordMetricsListSchema,
27711
27857
  seoDomainIntelligenceListSchema,
27712
27858
  seoRankedKeywordListSchema,
27859
+ seoCompetitorListSchema,
27860
+ seoKeywordGapListSchema,
27861
+ seoCompetitorSortSchema,
27862
+ seoKeywordGapSortSchema,
27863
+ seoCompetitorQuerySchema,
27864
+ seoKeywordGapQuerySchema,
27713
27865
  seoMarketRankingFilterSchema,
27714
27866
  seoMarketRankingSortSchema,
27715
27867
  SeoMarketRankingSorts,
@@ -27728,5 +27880,7 @@ export {
27728
27880
  compareMarketRankingsBy,
27729
27881
  marketRankingBands,
27730
27882
  countMarketRankings,
27731
- groupMarketRankings
27883
+ groupMarketRankings,
27884
+ compareSeoCompetitorsBy,
27885
+ compareSeoKeywordGapsBy
27732
27886
  };
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-VJWDBFSL.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-X7UC755Q.js";
7
+ } from "./chunk-C5WSQXP5.js";
8
8
  import {
9
9
  CliError,
10
10
  isMachineFormat
11
11
  } from "./chunk-HGMYEDAQ.js";
12
12
  import {
13
13
  describeError
14
- } from "./chunk-FCGYYZHL.js";
14
+ } from "./chunk-PM2DB3HE.js";
15
15
 
16
16
  // src/commands/mcp.ts
17
17
  import fs from "fs";
@@ -2,16 +2,16 @@ import {
2
2
  CANONRY_MCP_TOOLKITS,
3
3
  CANONRY_MCP_TOOLKIT_NAMES,
4
4
  isCanonryMcpToolkitName
5
- } from "./chunk-NDTU7FGL.js";
5
+ } from "./chunk-WVCDUKJ2.js";
6
6
  import {
7
7
  createApiClient,
8
8
  runWithUsageTags
9
- } from "./chunk-X7UC755Q.js";
9
+ } from "./chunk-C5WSQXP5.js";
10
10
  import {
11
11
  canonryMcpTools,
12
12
  mcpOutputSchema,
13
13
  toolOperationAccess
14
- } from "./chunk-4QHTXT4A.js";
14
+ } from "./chunk-MUYTDD35.js";
15
15
  import {
16
16
  PACKAGE_VERSION
17
17
  } from "./chunk-AOJU5LKX.js";
@@ -24,7 +24,7 @@ import {
24
24
  isReadOnlyKey,
25
25
  restrictedWriteScopes,
26
26
  upgradeCaveatFor
27
- } from "./chunk-FCGYYZHL.js";
27
+ } from "./chunk-PM2DB3HE.js";
28
28
 
29
29
  // src/mcp/server.ts
30
30
  import { randomUUID } from "crypto";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-X7UC755Q.js";
3
+ } from "./chunk-C5WSQXP5.js";
4
4
  import {
5
5
  emitJsonl
6
6
  } from "./chunk-Z7IUAJOF.js";
7
7
  import {
8
8
  measurementDraftOperationSchema,
9
9
  runMeasurementDraftAction
10
- } from "./chunk-5QR7D4RQ.js";
10
+ } from "./chunk-7HB6QLX4.js";
11
11
  import {
12
12
  isMachineFormat,
13
13
  systemError
@@ -37,7 +37,7 @@ import {
37
37
  unattributedAnswersLabel,
38
38
  uncheckedSourcesLabel,
39
39
  visibilityReportRequestSchema
40
- } from "./chunk-FCGYYZHL.js";
40
+ } from "./chunk-PM2DB3HE.js";
41
41
 
42
42
  // src/commands/measurement-plan.ts
43
43
  import crypto from "crypto";
@@ -5,7 +5,7 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-D5XPB7MM.js";
8
+ } from "./chunk-6VBLPCCX.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
@@ -15,7 +15,7 @@ import {
15
15
  factorStatusFromDistribution,
16
16
  factorStatusFromScore,
17
17
  roundPreservingTotal
18
- } from "./chunk-FCGYYZHL.js";
18
+ } from "./chunk-PM2DB3HE.js";
19
19
 
20
20
  // src/site-audit-factors.ts
21
21
  function recordedSharePct(factor) {
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getRequestContext
3
- } from "./chunk-JPOIDGGF.js";
3
+ } from "./chunk-3MGM3U6V.js";
4
4
  import {
5
5
  diagnosticIdentity,
6
6
  redactLogString,
7
7
  redactLogValue
8
- } from "./chunk-FCGYYZHL.js";
8
+ } from "./chunk-PM2DB3HE.js";
9
9
 
10
10
  // ../api-routes/src/runtime-logger.ts
11
11
  import { format } from "util";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseJsonColumn
3
- } from "./chunk-D5XPB7MM.js";
3
+ } from "./chunk-6VBLPCCX.js";
4
4
  import {
5
5
  AI_ENGINE_DOMAINS,
6
6
  AI_ENGINE_SELF_DOMAINS,
@@ -26,7 +26,7 @@ import {
26
26
  retryAfterDelayMs,
27
27
  usageCount,
28
28
  withRetry
29
- } from "./chunk-FCGYYZHL.js";
29
+ } from "./chunk-PM2DB3HE.js";
30
30
 
31
31
  // ../provider-openai/src/normalize.ts
32
32
  import OpenAI from "openai";
@@ -2,13 +2,13 @@ import {
2
2
  bucketCliCommandDuration,
3
3
  isTelemetryEnabled,
4
4
  trackEvent
5
- } from "./chunk-YMOQSEO3.js";
5
+ } from "./chunk-6PRFVLIK.js";
6
6
  import {
7
7
  getConfigDir
8
- } from "./chunk-2BRVGCVU.js";
8
+ } from "./chunk-VLOCDWCS.js";
9
9
  import {
10
10
  currentOutcomeAttribution
11
- } from "./chunk-JPOIDGGF.js";
11
+ } from "./chunk-3MGM3U6V.js";
12
12
  import {
13
13
  AGENT_NONE,
14
14
  OutcomeSurfaces,
@@ -19,7 +19,7 @@ import {
19
19
  integrationConnectionPropertiesSchema,
20
20
  normalizeAgentSlug,
21
21
  usageSurfaceSchema
22
- } from "./chunk-FCGYYZHL.js";
22
+ } from "./chunk-PM2DB3HE.js";
23
23
 
24
24
  // src/outcome-telemetry.ts
25
25
  import fs from "fs";
@@ -20,7 +20,7 @@ import {
20
20
  resolveProviderModel,
21
21
  schedulableRunKindSchema,
22
22
  sentimentEvaluatorProviderSchema
23
- } from "./chunk-FCGYYZHL.js";
23
+ } from "./chunk-PM2DB3HE.js";
24
24
 
25
25
  // ../config/src/index.ts
26
26
  import { z as z5 } from "zod";
@@ -1011,6 +1011,10 @@ function getConfigDir() {
1011
1011
  function getConfigPath() {
1012
1012
  return path.join(getConfigDir(), "config.yaml");
1013
1013
  }
1014
+ var loadedConfigPaths = /* @__PURE__ */ new WeakMap();
1015
+ function loadedConfigPath(config) {
1016
+ return loadedConfigPaths.get(config);
1017
+ }
1014
1018
  function loadConfig(configPath = getConfigPath()) {
1015
1019
  if (!fs.existsSync(configPath)) {
1016
1020
  throw new Error(
@@ -1159,6 +1163,7 @@ Do not use "canonry init --force" for recovery. It replaces credentials without
1159
1163
  if (parsedExternalMcp) {
1160
1164
  parsed.externalMcpServers = parsedExternalMcp;
1161
1165
  }
1166
+ loadedConfigPaths.set(parsed, path.resolve(configPath));
1162
1167
  return parsed;
1163
1168
  }
1164
1169
  function parseExternalMcpEnv(raw) {
@@ -1354,6 +1359,7 @@ export {
1354
1359
  providersWithUnsupportedBatch,
1355
1360
  getConfigDir,
1356
1361
  getConfigPath,
1362
+ loadedConfigPath,
1357
1363
  loadConfig,
1358
1364
  loadConfigRaw,
1359
1365
  saveConfig,