@canonry/canonry 4.125.0 → 4.126.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.
@@ -388,6 +388,11 @@ cnry google discover-sitemaps <project> --wait # auto-discover and inspe
388
388
 
389
389
  cnry google sync <project> # sync GSC data
390
390
  cnry google sync <project> --days 30 --full --wait # full sync with wait
391
+ # `--full` re-fetches 480 days (GSC's 16-month retention ceiling) and is also the
392
+ # BACKFILL path: it repopulates the accurate per-query totals (`dimensions:
393
+ # ['date','query']`, no `page` fan-out) and the property daily totals for the
394
+ # whole retained window, not just recent days. Run it once per project after
395
+ # upgrading to pick up accurate history; a normal sync only covers its own window.
391
396
 
392
397
  cnry google coverage <project> # index coverage summary
393
398
  cnry google refresh <project> # force-fetch fresh GSC coverage data
@@ -396,6 +396,7 @@ __export(schema_exports, {
396
396
  googleConnections: () => googleConnections,
397
397
  gscCoverageSnapshots: () => gscCoverageSnapshots,
398
398
  gscDailyTotals: () => gscDailyTotals,
399
+ gscQueryDailyTotals: () => gscQueryDailyTotals,
399
400
  gscSearchData: () => gscSearchData,
400
401
  gscUrlInspections: () => gscUrlInspections,
401
402
  healthSnapshots: () => healthSnapshots,
@@ -656,6 +657,27 @@ var gscDailyTotals = sqliteTable("gsc_daily_totals", {
656
657
  uniqueIndex("idx_gsc_daily_totals_project_date").on(table.projectId, table.date),
657
658
  index("idx_gsc_daily_totals_project").on(table.projectId)
658
659
  ]);
660
+ var gscQueryDailyTotals = sqliteTable("gsc_query_daily_totals", {
661
+ id: text("id").primaryKey(),
662
+ projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
663
+ date: text("date").notNull(),
664
+ query: text("query").notNull(),
665
+ clicks: integer("clicks").notNull().default(0),
666
+ impressions: integer("impressions").notNull().default(0),
667
+ /**
668
+ * Stored as a string like `gscSearchData.position` (parsed on read). CTR is
669
+ * derived (clicks / impressions) and intentionally not stored.
670
+ */
671
+ position: text("position").notNull().default("0"),
672
+ syncedAt: text("synced_at").notNull(),
673
+ syncRunId: text("sync_run_id").references(() => runs.id, { onDelete: "cascade" }),
674
+ createdAt: text("created_at").notNull()
675
+ }, (table) => [
676
+ uniqueIndex("idx_gsc_query_daily_totals_project_date_query").on(table.projectId, table.date, table.query),
677
+ index("idx_gsc_query_daily_totals_project_date").on(table.projectId, table.date),
678
+ index("idx_gsc_query_daily_totals_query").on(table.query),
679
+ index("idx_gsc_query_daily_totals_run").on(table.syncRunId)
680
+ ]);
659
681
  var gscUrlInspections = sqliteTable("gsc_url_inspections", {
660
682
  id: text("id").primaryKey(),
661
683
  projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
@@ -3971,6 +3993,34 @@ var MIGRATION_VERSIONS = [
3971
3993
  `CREATE INDEX IF NOT EXISTS idx_ga_daily_totals_project ON ga_daily_totals(project_id)`,
3972
3994
  `CREATE INDEX IF NOT EXISTS idx_ga_daily_totals_run ON ga_daily_totals(sync_run_id)`
3973
3995
  ]
3996
+ },
3997
+ {
3998
+ // Per-query daily totals fetched WITHOUT the `page` dimension. Summing
3999
+ // `gsc_search_data` by query multiplies impressions by how many of the
4000
+ // site's pages ranked on the same SERP — ~0% for single-page queries but
4001
+ // ~500% for brand+category terms, which reorders a top-queries table.
4002
+ // Dropping `page` makes Google do the dedup. Per-query counterpart to
4003
+ // `gsc_daily_totals`.
4004
+ version: 107,
4005
+ name: "gsc-query-daily-totals",
4006
+ statements: [
4007
+ `CREATE TABLE IF NOT EXISTS gsc_query_daily_totals (
4008
+ id TEXT PRIMARY KEY,
4009
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
4010
+ date TEXT NOT NULL,
4011
+ query TEXT NOT NULL,
4012
+ clicks INTEGER NOT NULL DEFAULT 0,
4013
+ impressions INTEGER NOT NULL DEFAULT 0,
4014
+ position TEXT NOT NULL DEFAULT '0',
4015
+ synced_at TEXT NOT NULL,
4016
+ sync_run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,
4017
+ created_at TEXT NOT NULL
4018
+ )`,
4019
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_gsc_query_daily_totals_project_date_query ON gsc_query_daily_totals(project_id, date, query)`,
4020
+ `CREATE INDEX IF NOT EXISTS idx_gsc_query_daily_totals_project_date ON gsc_query_daily_totals(project_id, date)`,
4021
+ `CREATE INDEX IF NOT EXISTS idx_gsc_query_daily_totals_query ON gsc_query_daily_totals(query)`,
4022
+ `CREATE INDEX IF NOT EXISTS idx_gsc_query_daily_totals_run ON gsc_query_daily_totals(sync_run_id)`
4023
+ ]
3974
4024
  }
3975
4025
  ];
3976
4026
  function rebuildBacklinkTableWithSource(tx, table) {
@@ -9584,14 +9634,24 @@ function computeQueryChanges(projectQueries, cutoff) {
9584
9634
  label: `+${count2} kp`
9585
9635
  }));
9586
9636
  }
9637
+ function pooledRate(buckets, rateKey) {
9638
+ const countKey = rateKey === "citationRate" ? "cited" : "mentionedCount";
9639
+ let numerator = 0;
9640
+ let denominator = 0;
9641
+ for (const bucket of buckets) {
9642
+ numerator += bucket[countKey];
9643
+ denominator += bucket.total;
9644
+ }
9645
+ return denominator > 0 ? numerator / denominator : 0;
9646
+ }
9587
9647
  function computeTrend(buckets, rateKey) {
9588
9648
  const nonEmpty = buckets.filter((b) => b.total > 0);
9589
9649
  if (nonEmpty.length < 2) return "stable";
9590
9650
  const mid = Math.floor(nonEmpty.length / 2);
9591
9651
  const firstHalf = nonEmpty.slice(0, mid);
9592
9652
  const secondHalf = nonEmpty.slice(mid);
9593
- const avgFirst = firstHalf.reduce((s, b) => s + b[rateKey], 0) / firstHalf.length;
9594
- const avgSecond = secondHalf.reduce((s, b) => s + b[rateKey], 0) / secondHalf.length;
9653
+ const avgFirst = pooledRate(firstHalf, rateKey);
9654
+ const avgSecond = pooledRate(secondHalf, rateKey);
9595
9655
  const diff = avgSecond - avgFirst;
9596
9656
  if (diff > 0.05) return "improving";
9597
9657
  if (diff < -0.05) return "declining";
@@ -10048,7 +10108,12 @@ function buildGaTrafficByPage(db, projectId) {
10048
10108
  return map;
10049
10109
  }
10050
10110
  function sumAiReferralSessions(db, projectId) {
10051
- const rows = db.select({ sessions: gaAiReferrals.sessions }).from(gaAiReferrals).where(eq13(gaAiReferrals.projectId, projectId)).all();
10111
+ const rows = db.select({ sessions: gaAiReferrals.sessions }).from(gaAiReferrals).where(
10112
+ and9(
10113
+ eq13(gaAiReferrals.projectId, projectId),
10114
+ eq13(gaAiReferrals.sourceDimension, "session")
10115
+ )
10116
+ ).all();
10052
10117
  return rows.reduce((acc, r) => acc + (r.sessions ?? 0), 0);
10053
10118
  }
10054
10119
  function buildCandidateQueries(opts) {
@@ -10636,6 +10701,64 @@ function mergeGscDailyTotalsWithFallback(propertyTotals, dimensionedFallback) {
10636
10701
  for (const row of propertyTotals) byDate.set(row.date, row);
10637
10702
  return [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
10638
10703
  }
10704
+ function readGscQueryDailyRows(db, projectId, startDate, endDate) {
10705
+ const rows = db.select({
10706
+ date: gscQueryDailyTotals.date,
10707
+ query: gscQueryDailyTotals.query,
10708
+ clicks: gscQueryDailyTotals.clicks,
10709
+ impressions: gscQueryDailyTotals.impressions,
10710
+ position: gscQueryDailyTotals.position
10711
+ }).from(gscQueryDailyTotals).where(
10712
+ and11(
10713
+ eq15(gscQueryDailyTotals.projectId, projectId),
10714
+ sql6`${gscQueryDailyTotals.date} >= ${startDate}`,
10715
+ sql6`${gscQueryDailyTotals.date} <= ${endDate}`
10716
+ )
10717
+ ).all();
10718
+ return rows.map((r) => {
10719
+ const position = Number(r.position);
10720
+ return {
10721
+ date: r.date,
10722
+ query: r.query,
10723
+ clicks: r.clicks,
10724
+ impressions: r.impressions,
10725
+ position: Number.isFinite(position) ? position : 0
10726
+ };
10727
+ });
10728
+ }
10729
+ function mergeGscQueryTotalsWithFallback(accurateDays, fallbackDays) {
10730
+ const dayKey = (r) => `${r.date}\0${r.query}`;
10731
+ const byDay = /* @__PURE__ */ new Map();
10732
+ for (const row of fallbackDays) byDay.set(dayKey(row), { row, accurate: false });
10733
+ for (const row of accurateDays) byDay.set(dayKey(row), { row, accurate: true });
10734
+ const byQuery = /* @__PURE__ */ new Map();
10735
+ for (const { row, accurate } of byDay.values()) {
10736
+ const acc = byQuery.get(row.query) ?? {
10737
+ clicks: 0,
10738
+ impressions: 0,
10739
+ weightedPositionSum: 0,
10740
+ positionSum: 0,
10741
+ positionDays: 0,
10742
+ sawAccurate: false,
10743
+ sawLegacy: false
10744
+ };
10745
+ acc.clicks += row.clicks;
10746
+ acc.impressions += row.impressions;
10747
+ acc.weightedPositionSum += row.position * row.impressions;
10748
+ acc.positionSum += row.position;
10749
+ acc.positionDays += 1;
10750
+ if (accurate) acc.sawAccurate = true;
10751
+ else acc.sawLegacy = true;
10752
+ byQuery.set(row.query, acc);
10753
+ }
10754
+ return [...byQuery.entries()].map(([query, acc]) => ({
10755
+ query,
10756
+ clicks: acc.clicks,
10757
+ impressions: acc.impressions,
10758
+ position: acc.impressions > 0 ? acc.weightedPositionSum / acc.impressions : acc.positionDays > 0 ? acc.positionSum / acc.positionDays : 0,
10759
+ source: acc.sawAccurate && acc.sawLegacy ? "mixed" : acc.sawAccurate ? "google" : "page-summed"
10760
+ }));
10761
+ }
10639
10762
 
10640
10763
  // ../api-routes/src/report-renderer.ts
10641
10764
  var COLORS = {
@@ -12969,15 +13092,16 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
12969
13092
  const dailyTotals = allDailyTotals.filter((r) => r.date >= startDate && r.date <= maxDate);
12970
13093
  if (rows.length === 0 && dailyTotals.length === 0) return null;
12971
13094
  let dimensionedClicks = 0;
12972
- const queryAgg = /* @__PURE__ */ new Map();
13095
+ const queryDayAgg = /* @__PURE__ */ new Map();
12973
13096
  const dimensionedTrendAgg = /* @__PURE__ */ new Map();
12974
13097
  for (const r of rows) {
12975
13098
  dimensionedClicks += r.clicks;
12976
- const q = queryAgg.get(r.query) ?? { clicks: 0, impressions: 0, weightedPositionSum: 0 };
13099
+ const dayKey = `${r.date} ${r.query}`;
13100
+ const q = queryDayAgg.get(dayKey) ?? { date: r.date, query: r.query, clicks: 0, impressions: 0, weightedPositionSum: 0 };
12977
13101
  q.clicks += r.clicks;
12978
13102
  q.impressions += r.impressions;
12979
13103
  q.weightedPositionSum += safeNum(r.position) * r.impressions;
12980
- queryAgg.set(r.query, q);
13104
+ queryDayAgg.set(dayKey, q);
12981
13105
  const t = dimensionedTrendAgg.get(r.date) ?? { clicks: 0, impressions: 0, weightedPositionSum: 0 };
12982
13106
  t.clicks += r.clicks;
12983
13107
  t.impressions += r.impressions;
@@ -13004,17 +13128,27 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
13004
13128
  const trend = dailySeries.map((d) => ({ date: d.date, clicks: d.clicks, impressions: d.impressions }));
13005
13129
  const ctr = totalImpressions > 0 ? totalClicks / totalImpressions : 0;
13006
13130
  const avgPosition = totalImpressions > 0 ? weightedPositionSum / totalImpressions : 0;
13007
- const topQueries = [...queryAgg.entries()].map(([query, agg]) => ({
13008
- query,
13131
+ const queryTotals = mergeGscQueryTotalsWithFallback(
13132
+ readGscQueryDailyRows(db, projectId, startDate, maxDate),
13133
+ [...queryDayAgg.values()].map((agg) => ({
13134
+ date: agg.date,
13135
+ query: agg.query,
13136
+ clicks: agg.clicks,
13137
+ impressions: agg.impressions,
13138
+ position: agg.impressions > 0 ? agg.weightedPositionSum / agg.impressions : 0
13139
+ }))
13140
+ );
13141
+ const topQueries = queryTotals.map((agg) => ({
13142
+ query: agg.query,
13009
13143
  clicks: agg.clicks,
13010
13144
  impressions: agg.impressions,
13011
13145
  ctr: agg.impressions > 0 ? agg.clicks / agg.impressions : 0,
13012
- avgPosition: agg.impressions > 0 ? agg.weightedPositionSum / agg.impressions : 0,
13013
- category: categorizeQuery(query, projectBrandNames, canonicalDomain)
13146
+ avgPosition: agg.position,
13147
+ category: categorizeQuery(agg.query, projectBrandNames, canonicalDomain)
13014
13148
  })).sort((a, b) => b.clicks - a.clicks).slice(0, TOP_QUERIES_LIMIT);
13015
13149
  const categoryAgg = /* @__PURE__ */ new Map();
13016
- for (const [query, agg] of queryAgg) {
13017
- const cat = categorizeQuery(query, projectBrandNames, canonicalDomain);
13150
+ for (const agg of queryTotals) {
13151
+ const cat = categorizeQuery(agg.query, projectBrandNames, canonicalDomain);
13018
13152
  const bucket = categoryAgg.get(cat) ?? { clicks: 0, impressions: 0 };
13019
13153
  bucket.clicks += agg.clicks;
13020
13154
  bucket.impressions += agg.impressions;
@@ -13029,9 +13163,9 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
13029
13163
  const periodStart = trend[0]?.date ?? "";
13030
13164
  const periodEnd = trend.at(-1)?.date ?? "";
13031
13165
  const trackedSet = new Set(trackedQueries.map((q) => q.toLowerCase()));
13032
- const gscQuerySet = new Set([...queryAgg.keys()].map((q) => q.toLowerCase()));
13166
+ const gscQuerySet = new Set(queryTotals.map((q) => q.query.toLowerCase()));
13033
13167
  const trackedButNoGsc = trackedQueries.filter((q) => !gscQuerySet.has(q.toLowerCase())).sort();
13034
- const gscButNotTracked = [...queryAgg.entries()].filter(([q]) => !trackedSet.has(q.toLowerCase())).filter(([q]) => categorizeQuery(q, projectBrandNames, canonicalDomain) !== "brand").sort((a, b) => b[1].impressions - a[1].impressions).map(([q]) => q).slice(0, TOP_QUERIES_LIMIT);
13168
+ const gscButNotTracked = [...queryTotals].filter((agg) => !trackedSet.has(agg.query.toLowerCase())).filter((agg) => categorizeQuery(agg.query, projectBrandNames, canonicalDomain) !== "brand").sort((a, b) => b.impressions - a.impressions).map((agg) => agg.query).slice(0, TOP_QUERIES_LIMIT);
13035
13169
  return {
13036
13170
  periodStart,
13037
13171
  periodEnd,
@@ -15754,7 +15888,8 @@ function summarizeTransitionsFromSnapshots(latest, previous, since) {
15754
15888
  }
15755
15889
  function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
15756
15890
  const cutoff = new Date(Date.now() - 28 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
15757
- const rows = app.db.select({
15891
+ const dimensionedRows = app.db.select({
15892
+ date: gscSearchData.date,
15758
15893
  query: gscSearchData.query,
15759
15894
  impressions: sql8`COALESCE(SUM(${gscSearchData.impressions}), 0)`,
15760
15895
  clicks: sql8`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
@@ -15766,12 +15901,23 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
15766
15901
  eq20(gscSearchData.projectId, projectId),
15767
15902
  sql8`${gscSearchData.date} >= ${cutoff}`,
15768
15903
  sql8`${gscSearchData.impressions} > 0`
15769
- )).groupBy(gscSearchData.query).orderBy(sql8`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
15770
- const gscRows = rows.map((r) => ({
15904
+ )).groupBy(gscSearchData.date, gscSearchData.query).all();
15905
+ const todayIso = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15906
+ const merged = mergeGscQueryTotalsWithFallback(
15907
+ readGscQueryDailyRows(app.db, projectId, cutoff, todayIso),
15908
+ dimensionedRows.map((r) => ({
15909
+ date: r.date,
15910
+ query: r.query,
15911
+ impressions: Number(r.impressions),
15912
+ clicks: Number(r.clicks),
15913
+ position: Number(r.avgPosition)
15914
+ }))
15915
+ );
15916
+ const gscRows = merged.filter((r) => r.impressions > 0).sort((a, b) => b.impressions - a.impressions).slice(0, 100).map((r) => ({
15771
15917
  query: r.query,
15772
- impressions: Number(r.impressions),
15773
- clicks: Number(r.clicks),
15774
- avgPosition: Number(r.avgPosition)
15918
+ impressions: r.impressions,
15919
+ clicks: r.clicks,
15920
+ avgPosition: r.position
15775
15921
  }));
15776
15922
  return buildSuggestedQueries(gscRows, { trackedQueries });
15777
15923
  }
@@ -42265,6 +42411,7 @@ export {
42265
42411
  notifications,
42266
42412
  gscSearchData,
42267
42413
  gscDailyTotals,
42414
+ gscQueryDailyTotals,
42268
42415
  gscUrlInspections,
42269
42416
  gscCoverageSnapshots,
42270
42417
  siteAuditSnapshots,
@@ -77,6 +77,7 @@ import {
77
77
  groupRunsByCreatedAt,
78
78
  gscCoverageSnapshots,
79
79
  gscDailyTotals,
80
+ gscQueryDailyTotals,
80
81
  gscSearchData,
81
82
  gscUrlInspections,
82
83
  hashAttributes,
@@ -115,7 +116,7 @@ import {
115
116
  siteAuditPages,
116
117
  siteAuditSnapshots,
117
118
  usageCounters
118
- } from "./chunk-YPRUBNUK.js";
119
+ } from "./chunk-FCGRCCX6.js";
119
120
  import {
120
121
  AGENT_MEMORY_VALUE_MAX_BYTES,
121
122
  AGENT_PROVIDER_IDS,
@@ -3869,6 +3870,36 @@ async function executeGscSync(db, runId, projectId, opts) {
3869
3870
  }).run();
3870
3871
  }
3871
3872
  log2.info("daily-totals.complete", { runId, projectId, rowCount: totalRows.length });
3873
+ const queryTotalRows = await fetchSearchAnalytics(accessToken, propertyId, {
3874
+ startDate,
3875
+ endDate,
3876
+ dimensions: ["date", "query"]
3877
+ });
3878
+ db.delete(gscQueryDailyTotals).where(
3879
+ and2(
3880
+ eq2(gscQueryDailyTotals.projectId, projectId),
3881
+ sql2`${gscQueryDailyTotals.date} >= ${startDate}`,
3882
+ sql2`${gscQueryDailyTotals.date} <= ${endDate}`
3883
+ )
3884
+ ).run();
3885
+ const queryTotalsNow = (/* @__PURE__ */ new Date()).toISOString();
3886
+ for (const row of queryTotalRows) {
3887
+ const [date, query] = row.keys;
3888
+ if (!date || !query) continue;
3889
+ db.insert(gscQueryDailyTotals).values({
3890
+ id: crypto4.randomUUID(),
3891
+ projectId,
3892
+ date,
3893
+ query,
3894
+ clicks: row.clicks,
3895
+ impressions: row.impressions,
3896
+ position: String(row.position),
3897
+ syncedAt: queryTotalsNow,
3898
+ syncRunId: runId,
3899
+ createdAt: queryTotalsNow
3900
+ }).run();
3901
+ }
3902
+ log2.info("query-totals.complete", { runId, projectId, rowCount: queryTotalRows.length });
3872
3903
  const pageClicks = /* @__PURE__ */ new Map();
3873
3904
  for (const row of rows) {
3874
3905
  const page = row.keys[1];
@@ -6936,7 +6967,7 @@ function readStoredGroundingSources(rawResponse) {
6936
6967
  return result;
6937
6968
  }
6938
6969
  async function backfillInsightsCommand(project, opts) {
6939
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-WJ2ZR7GF.js");
6970
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-K6B3MTDS.js");
6940
6971
  const config = loadConfig();
6941
6972
  const db = createClient(config.database);
6942
6973
  migrate(db);
package/dist/cli.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  setTelemetrySource,
30
30
  showFirstRunNotice,
31
31
  trackEvent
32
- } from "./chunk-DN5H2ZN7.js";
32
+ } from "./chunk-M6GKSLOJ.js";
33
33
  import {
34
34
  CliError,
35
35
  EXIT_SYSTEM_ERROR,
@@ -54,7 +54,7 @@ import {
54
54
  projects,
55
55
  queries,
56
56
  renderReportHtml
57
- } from "./chunk-YPRUBNUK.js";
57
+ } from "./chunk-FCGRCCX6.js";
58
58
  import {
59
59
  AdsOperationKinds,
60
60
  AdsOperationStates,
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-DN5H2ZN7.js";
3
+ } from "./chunk-M6GKSLOJ.js";
4
4
  import {
5
5
  loadConfig
6
6
  } from "./chunk-WA57WG6W.js";
7
- import "./chunk-YPRUBNUK.js";
7
+ import "./chunk-FCGRCCX6.js";
8
8
  import "./chunk-RFLNTI7Y.js";
9
9
  export {
10
10
  createServer,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-YPRUBNUK.js";
3
+ } from "./chunk-FCGRCCX6.js";
4
4
  import "./chunk-RFLNTI7Y.js";
5
5
  export {
6
6
  IntelligenceService
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.125.0",
3
+ "version": "4.126.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",
@@ -67,25 +67,25 @@
67
67
  "tsx": "^4.19.0",
68
68
  "@ainyc/canonry-api-client": "0.0.0",
69
69
  "@ainyc/canonry-api-routes": "0.0.0",
70
- "@ainyc/canonry-config": "0.0.0",
71
70
  "@ainyc/canonry-contracts": "0.0.0",
72
71
  "@ainyc/canonry-db": "0.0.0",
72
+ "@ainyc/canonry-config": "0.0.0",
73
73
  "@ainyc/canonry-integration-bing": "0.0.0",
74
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
75
- "@ainyc/canonry-integration-google": "0.0.0",
76
74
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
75
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
77
76
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
77
+ "@ainyc/canonry-integration-google": "0.0.0",
78
78
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
79
79
  "@ainyc/canonry-integration-google-places": "0.0.0",
80
+ "@ainyc/canonry-integration-traffic": "0.0.0",
81
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
80
82
  "@ainyc/canonry-intelligence": "0.0.0",
81
83
  "@ainyc/canonry-provider-cdp": "0.0.0",
82
- "@ainyc/canonry-provider-claude": "0.0.0",
83
- "@ainyc/canonry-provider-gemini": "0.0.0",
84
- "@ainyc/canonry-integration-wordpress": "0.0.0",
85
- "@ainyc/canonry-integration-traffic": "0.0.0",
86
84
  "@ainyc/canonry-provider-local": "0.0.0",
87
85
  "@ainyc/canonry-provider-openai": "0.0.0",
88
- "@ainyc/canonry-provider-perplexity": "0.0.0"
86
+ "@ainyc/canonry-provider-gemini": "0.0.0",
87
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
88
+ "@ainyc/canonry-provider-claude": "0.0.0"
89
89
  },
90
90
  "scripts": {
91
91
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",