@canonry/canonry 4.123.0 → 4.124.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 (24) hide show
  1. package/assets/assets/{AuditHistoryPanel-BVQMDIhQ.js → AuditHistoryPanel-fPBADGsr.js} +1 -1
  2. package/assets/assets/{BacklinksPage-nrHT9VMG.js → BacklinksPage-DKer7s8M.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-_8qLsQzI.js → ChartPrimitives-0DFDFhCZ.js} +1 -1
  4. package/assets/assets/{HistoryPage-DqsKKdyy.js → HistoryPage-BTkTlXg0.js} +1 -1
  5. package/assets/assets/{ProjectPage-GyKoQUv_.js → ProjectPage-BzqDCkp2.js} +1 -1
  6. package/assets/assets/{RunRow-BRZZcSjQ.js → RunRow-DyIeH4_d.js} +1 -1
  7. package/assets/assets/{RunsPage-24aI8DKF.js → RunsPage-KVu_37ds.js} +1 -1
  8. package/assets/assets/{SettingsPage-BaNeTG1i.js → SettingsPage-CCLXSBpq.js} +1 -1
  9. package/assets/assets/{TrafficPage-ByvcLSjv.js → TrafficPage-BH7a0sS2.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-B-siTXVg.js → TrafficSourceDetailPage-kIlf0kCe.js} +1 -1
  11. package/assets/assets/{arrow-left-CBh1cZNH.js → arrow-left-jhivZs4g.js} +1 -1
  12. package/assets/assets/{extract-error-message-DJZCPudR.js → extract-error-message-XU_cbcLD.js} +1 -1
  13. package/assets/assets/{index-mzLganDP.js → index-CqFSTLK5.js} +7 -7
  14. package/assets/assets/{trash-2-Bh-Yvkgc.js → trash-2-CcmHLt5u.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-JYBNBK25.js → chunk-4D2XUMMV.js} +4 -4
  17. package/dist/{chunk-4AZ7VES6.js → chunk-BGLBWFMV.js} +120 -18
  18. package/dist/{chunk-MMFSI3OG.js → chunk-SLCGTOKT.js} +17 -1
  19. package/dist/{chunk-HJIIJI7K.js → chunk-WGGB7QBD.js} +1 -1
  20. package/dist/cli.js +11 -5
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-K35EN43I.js → intelligence-service-SACHNNCT.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +10 -10
@@ -1 +1 @@
1
- import{c}from"./index-mzLganDP.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
1
+ import{c}from"./index-CqFSTLK5.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-mzLganDP.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-CqFSTLK5.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-DSxTZhaH.js">
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-HJIIJI7K.js";
13
+ } from "./chunk-WGGB7QBD.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -115,7 +115,7 @@ import {
115
115
  siteAuditPages,
116
116
  siteAuditSnapshots,
117
117
  usageCounters
118
- } from "./chunk-4AZ7VES6.js";
118
+ } from "./chunk-BGLBWFMV.js";
119
119
  import {
120
120
  AGENT_MEMORY_VALUE_MAX_BYTES,
121
121
  AGENT_PROVIDER_IDS,
@@ -185,7 +185,7 @@ import {
185
185
  validationError,
186
186
  winnabilityClassLabel,
187
187
  withRetry
188
- } from "./chunk-MMFSI3OG.js";
188
+ } from "./chunk-SLCGTOKT.js";
189
189
 
190
190
  // src/telemetry.ts
191
191
  import crypto from "crypto";
@@ -6901,7 +6901,7 @@ function readStoredGroundingSources(rawResponse) {
6901
6901
  return result;
6902
6902
  }
6903
6903
  async function backfillInsightsCommand(project, opts) {
6904
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-K35EN43I.js");
6904
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-SACHNNCT.js");
6905
6905
  const config = loadConfig();
6906
6906
  const db = createClient(config.database);
6907
6907
  migrate(db);
@@ -141,6 +141,7 @@ import {
141
141
  classifyTrafficPath,
142
142
  clusterByCosine,
143
143
  coerceSkillManifest,
144
+ compactDateToIso,
144
145
  competitorBatchRequestSchema,
145
146
  competitorDtoSchema,
146
147
  computeDedupSimilarityStats,
@@ -334,7 +335,7 @@ import {
334
335
  wordpressSchemaDeployResultDtoSchema,
335
336
  wordpressSchemaStatusResultDtoSchema,
336
337
  wordpressStatusDtoSchema
337
- } from "./chunk-MMFSI3OG.js";
338
+ } from "./chunk-SLCGTOKT.js";
338
339
 
339
340
  // src/intelligence-service.ts
340
341
  import { eq as eq41, desc as desc18, asc as asc9, and as and32, ne as ne6, or as or8, inArray as inArray15, gte as gte10, lte as lte6 } from "drizzle-orm";
@@ -378,6 +379,7 @@ __export(schema_exports, {
378
379
  domainClassifications: () => domainClassifications,
379
380
  gaAiReferrals: () => gaAiReferrals,
380
381
  gaConnections: () => gaConnections,
382
+ gaDailyTotals: () => gaDailyTotals,
381
383
  gaSocialReferrals: () => gaSocialReferrals,
382
384
  gaTrafficSnapshots: () => gaTrafficSnapshots,
383
385
  gaTrafficSummaries: () => gaTrafficSummaries,
@@ -823,6 +825,20 @@ var gaTrafficSnapshots = sqliteTable("ga_traffic_snapshots", {
823
825
  index("idx_ga_traffic_page_normalized").on(table.projectId, table.date, table.landingPageNormalized),
824
826
  index("idx_ga_traffic_run").on(table.syncRunId)
825
827
  ]);
828
+ var gaDailyTotals = sqliteTable("ga_daily_totals", {
829
+ id: text("id").primaryKey(),
830
+ projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
831
+ date: text("date").notNull(),
832
+ sessions: integer("sessions").notNull().default(0),
833
+ users: integer("users").notNull().default(0),
834
+ syncedAt: text("synced_at").notNull(),
835
+ syncRunId: text("sync_run_id").references(() => runs.id, { onDelete: "cascade" }),
836
+ createdAt: text("created_at").notNull()
837
+ }, (table) => [
838
+ uniqueIndex("idx_ga_daily_totals_project_date").on(table.projectId, table.date),
839
+ index("idx_ga_daily_totals_project").on(table.projectId),
840
+ index("idx_ga_daily_totals_run").on(table.syncRunId)
841
+ ]);
826
842
  var gaAiReferrals = sqliteTable("ga_ai_referrals", {
827
843
  id: text("id").primaryKey(),
828
844
  projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
@@ -3929,6 +3945,31 @@ var MIGRATION_VERSIONS = [
3929
3945
  run: (tx) => {
3930
3946
  backfillQuerySnapshotServedModel(tx);
3931
3947
  }
3948
+ },
3949
+ {
3950
+ // `users` is not additive across landing pages — one visitor who lands on
3951
+ // three pages is one user but contributes to three `ga_traffic_snapshots`
3952
+ // rows. Summing that dimensioned table overcounts the day. This table
3953
+ // stores the same day fetched with NO landing-page dimension, so GA does
3954
+ // the dedup and the stored number matches the GA UI. Same shape and
3955
+ // rationale as `gsc_daily_totals`.
3956
+ version: 106,
3957
+ name: "ga-daily-totals",
3958
+ statements: [
3959
+ `CREATE TABLE IF NOT EXISTS ga_daily_totals (
3960
+ id TEXT PRIMARY KEY,
3961
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
3962
+ date TEXT NOT NULL,
3963
+ sessions INTEGER NOT NULL DEFAULT 0,
3964
+ users INTEGER NOT NULL DEFAULT 0,
3965
+ synced_at TEXT NOT NULL,
3966
+ sync_run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,
3967
+ created_at TEXT NOT NULL
3968
+ )`,
3969
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_ga_daily_totals_project_date ON ga_daily_totals(project_id, date)`,
3970
+ `CREATE INDEX IF NOT EXISTS idx_ga_daily_totals_project ON ga_daily_totals(project_id)`,
3971
+ `CREATE INDEX IF NOT EXISTS idx_ga_daily_totals_run ON ga_daily_totals(sync_run_id)`
3972
+ ]
3932
3973
  }
3933
3974
  ];
3934
3975
  function rebuildBacklinkTableWithSource(tx, table) {
@@ -22475,9 +22516,7 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
22475
22516
  row.directSessions = directMap.get(key) ?? 0;
22476
22517
  }
22477
22518
  for (const row of rows) {
22478
- if (row.date.length === 8 && !row.date.includes("-")) {
22479
- row.date = `${row.date.slice(0, 4)}-${row.date.slice(4, 6)}-${row.date.slice(6, 8)}`;
22480
- }
22519
+ row.date = compactDateToIso(row.date);
22481
22520
  }
22482
22521
  ga4Log("info", "fetch-traffic.done", { propertyId, rowCount: rows.length });
22483
22522
  return rows;
@@ -22544,6 +22583,28 @@ async function fetchAggregateSummary(accessToken, propertyId, days) {
22544
22583
  ga4Log("info", "fetch-aggregate.done", { propertyId, ...summary });
22545
22584
  return summary;
22546
22585
  }
22586
+ async function fetchDailyTotals(accessToken, propertyId, days) {
22587
+ validateAccessToken2(accessToken);
22588
+ validatePropertyId(propertyId);
22589
+ const syncDays = Math.min(Math.max(1, days ?? GA4_DEFAULT_SYNC_DAYS), GA4_MAX_SYNC_DAYS);
22590
+ const endDate = /* @__PURE__ */ new Date();
22591
+ const startDate = /* @__PURE__ */ new Date();
22592
+ startDate.setDate(startDate.getDate() - syncDays);
22593
+ ga4Log("info", "fetch-daily-totals.start", { propertyId, days: syncDays });
22594
+ const res = await runReport(accessToken, propertyId, {
22595
+ dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
22596
+ dimensions: [{ name: GA4_DIMENSIONS.date }],
22597
+ metrics: [{ name: GA4_METRICS.sessions }, { name: GA4_METRICS.totalUsers }],
22598
+ limit: syncDays + 1
22599
+ });
22600
+ const rows = (res.rows ?? []).map((row) => ({
22601
+ date: compactDateToIso(row.dimensionValues[0]?.value ?? ""),
22602
+ sessions: parseInt(row.metricValues[0]?.value ?? "0", 10) || 0,
22603
+ users: parseInt(row.metricValues[1]?.value ?? "0", 10) || 0
22604
+ })).filter((row) => row.date.length > 0);
22605
+ ga4Log("info", "fetch-daily-totals.done", { propertyId, days: syncDays, rows: rows.length });
22606
+ return rows;
22607
+ }
22547
22608
  var WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90 };
22548
22609
  async function fetchWindowSummary(accessToken, propertyId, windowKey) {
22549
22610
  validateAccessToken2(accessToken);
@@ -22691,9 +22752,7 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
22691
22752
  }
22692
22753
  const dedupedRows = [...deduped.values()];
22693
22754
  for (const row of dedupedRows) {
22694
- if (row.date.length === 8 && !row.date.includes("-")) {
22695
- row.date = `${row.date.slice(0, 4)}-${row.date.slice(4, 6)}-${row.date.slice(6, 8)}`;
22696
- }
22755
+ row.date = compactDateToIso(row.date);
22697
22756
  }
22698
22757
  ga4Log("info", "fetch-ai-referrals.done", { propertyId, rowCount: dedupedRows.length });
22699
22758
  return dedupedRows;
@@ -22754,9 +22813,7 @@ async function fetchSocialReferrals(accessToken, propertyId, days) {
22754
22813
  if (pageRows.length < PAGE_SIZE || offset >= totalRows) break;
22755
22814
  }
22756
22815
  for (const row of rows) {
22757
- if (row.date.length === 8 && !row.date.includes("-")) {
22758
- row.date = `${row.date.slice(0, 4)}-${row.date.slice(4, 6)}-${row.date.slice(6, 8)}`;
22759
- }
22816
+ row.date = compactDateToIso(row.date);
22760
22817
  }
22761
22818
  ga4Log("info", "fetch-social-referrals.done", { propertyId, rowCount: rows.length });
22762
22819
  return rows;
@@ -29234,6 +29291,23 @@ async function cdpRoutes(app, opts) {
29234
29291
  // ../api-routes/src/ga.ts
29235
29292
  import crypto24 from "crypto";
29236
29293
  import { eq as eq29, desc as desc13, and as and23, sql as sql12 } from "drizzle-orm";
29294
+
29295
+ // ../api-routes/src/ga-session-history.ts
29296
+ function buildSessionHistory(landingPageDays, dailyTotals) {
29297
+ const totalsByDate = new Map(dailyTotals.map((row) => [row.date, row.users]));
29298
+ return landingPageDays.map((day) => {
29299
+ const deduplicated = totalsByDate.get(day.date);
29300
+ return {
29301
+ date: day.date,
29302
+ sessions: day.sessions,
29303
+ organicSessions: day.organicSessions,
29304
+ users: deduplicated ?? day.users,
29305
+ usersSource: deduplicated === void 0 ? "landing-page-sum" : "deduplicated"
29306
+ };
29307
+ });
29308
+ }
29309
+
29310
+ // ../api-routes/src/ga.ts
29237
29311
  function gaLog(level, action, ctx) {
29238
29312
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
29239
29313
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -29512,6 +29586,7 @@ async function ga4Routes(app, opts) {
29512
29586
  }
29513
29587
  app.db.delete(gaTrafficSnapshots).where(eq29(gaTrafficSnapshots.projectId, project.id)).run();
29514
29588
  app.db.delete(gaTrafficSummaries).where(eq29(gaTrafficSummaries.projectId, project.id)).run();
29589
+ app.db.delete(gaDailyTotals).where(eq29(gaDailyTotals.projectId, project.id)).run();
29515
29590
  app.db.delete(gaAiReferrals).where(eq29(gaAiReferrals.projectId, project.id)).run();
29516
29591
  app.db.delete(gaSocialReferrals).where(eq29(gaSocialReferrals.projectId, project.id)).run();
29517
29592
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
@@ -29576,7 +29651,8 @@ async function ga4Routes(app, opts) {
29576
29651
  const WINDOW_KEYS = ["7d", "30d", "90d"];
29577
29652
  const fetches = [
29578
29653
  fetchAggregateSummary(accessToken, propertyId, days),
29579
- ...WINDOW_KEYS.map((w) => fetchWindowSummary(accessToken, propertyId, w))
29654
+ ...WINDOW_KEYS.map((w) => fetchWindowSummary(accessToken, propertyId, w)),
29655
+ fetchDailyTotals(accessToken, propertyId, days)
29580
29656
  ];
29581
29657
  if (syncTraffic) fetches.push(fetchTrafficByLandingPage(accessToken, propertyId, days));
29582
29658
  if (syncAi) fetches.push(fetchAiReferrals(accessToken, propertyId, days));
@@ -29584,7 +29660,8 @@ async function ga4Routes(app, opts) {
29584
29660
  const results = await Promise.all(fetches);
29585
29661
  const summary = results[0];
29586
29662
  const windowSummaries = results.slice(1, 1 + WINDOW_KEYS.length);
29587
- let idx = 1 + WINDOW_KEYS.length;
29663
+ const dailyTotals = results[1 + WINDOW_KEYS.length];
29664
+ let idx = 2 + WINDOW_KEYS.length;
29588
29665
  if (syncTraffic) {
29589
29666
  rows = results[idx++];
29590
29667
  }
@@ -29620,6 +29697,25 @@ async function ga4Routes(app, opts) {
29620
29697
  }).run();
29621
29698
  }
29622
29699
  }
29700
+ tx.delete(gaDailyTotals).where(
29701
+ and23(
29702
+ eq29(gaDailyTotals.projectId, project.id),
29703
+ sql12`${gaDailyTotals.date} >= ${summary.periodStart}`,
29704
+ sql12`${gaDailyTotals.date} <= ${summary.periodEnd}`
29705
+ )
29706
+ ).run();
29707
+ for (const row of dailyTotals) {
29708
+ tx.insert(gaDailyTotals).values({
29709
+ id: crypto24.randomUUID(),
29710
+ projectId: project.id,
29711
+ date: row.date,
29712
+ sessions: row.sessions,
29713
+ users: row.users,
29714
+ syncedAt: now,
29715
+ syncRunId: runId,
29716
+ createdAt: now
29717
+ }).run();
29718
+ }
29623
29719
  if (syncAi) {
29624
29720
  tx.delete(gaAiReferrals).where(
29625
29721
  and23(
@@ -30116,12 +30212,18 @@ async function ga4Routes(app, opts) {
30116
30212
  organicSessions: sql12`SUM(${gaTrafficSnapshots.organicSessions})`,
30117
30213
  users: sql12`SUM(${gaTrafficSnapshots.users})`
30118
30214
  }).from(gaTrafficSnapshots).where(and23(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
30119
- return rows.map((r) => ({
30120
- date: r.date,
30121
- sessions: r.sessions ?? 0,
30122
- organicSessions: r.organicSessions ?? 0,
30123
- users: r.users ?? 0
30124
- }));
30215
+ const totalConditions = [eq29(gaDailyTotals.projectId, project.id)];
30216
+ if (cutoffDate) totalConditions.push(sql12`${gaDailyTotals.date} >= ${cutoffDate}`);
30217
+ const totals = app.db.select({ date: gaDailyTotals.date, users: gaDailyTotals.users }).from(gaDailyTotals).where(and23(...totalConditions)).all();
30218
+ return buildSessionHistory(
30219
+ rows.map((r) => ({
30220
+ date: r.date,
30221
+ sessions: r.sessions ?? 0,
30222
+ organicSessions: r.organicSessions ?? 0,
30223
+ users: r.users ?? 0
30224
+ })),
30225
+ totals
30226
+ );
30125
30227
  });
30126
30228
  app.get("/projects/:name/ga/coverage", async (request, _reply) => {
30127
30229
  const project = resolveProject(app.db, request.params.name);
@@ -867,6 +867,10 @@ function formatWindowCountDelta(d, countLabel, windowLabel) {
867
867
  const sign = d.deltaAbs > 0 ? "+" : "";
868
868
  return `${sign}${formatNumber(Math.round(d.deltaAbs))} ${countLabel} ${windowLabel}`;
869
869
  }
870
+ function compactDateToIso(value) {
871
+ if (value.length !== 8 || !/^\d{8}$/.test(value)) return value;
872
+ return `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`;
873
+ }
870
874
 
871
875
  // ../contracts/src/model-pointers.ts
872
876
  var modelPointerEventKindSchema = z7.enum(["introduced", "repointed", "retired"]);
@@ -3908,7 +3912,18 @@ var ga4SessionHistoryEntrySchema = z24.object({
3908
3912
  date: z24.string(),
3909
3913
  sessions: z24.number(),
3910
3914
  organicSessions: z24.number(),
3911
- users: z24.number()
3915
+ /**
3916
+ * Unique visitors for the day. Deduplicated by GA when `usersSource` is
3917
+ * `deduplicated`; a landing-page sum (which overcounts multi-page visitors)
3918
+ * for days synced before per-day totals were captured.
3919
+ */
3920
+ users: z24.number(),
3921
+ /**
3922
+ * How `users` was derived. `deduplicated` matches the GA UI's active users;
3923
+ * `landing-page-sum` is the legacy overcount kept so historical days still
3924
+ * render. Never compare the two across a series without saying which is which.
3925
+ */
3926
+ usersSource: z24.enum(["deduplicated", "landing-page-sum"])
3912
3927
  });
3913
3928
 
3914
3929
  // ../contracts/src/answer-visibility.ts
@@ -7140,6 +7155,7 @@ export {
7140
7155
  formatDeltaCopy,
7141
7156
  formatAverageDelta,
7142
7157
  formatWindowCountDelta,
7158
+ compactDateToIso,
7143
7159
  evaluateModelPointerExposure,
7144
7160
  buildModelChangeNotice,
7145
7161
  googleConnectionDtoSchema,
@@ -37,7 +37,7 @@ import {
37
37
  trafficConnectVercelRequestSchema,
38
38
  trafficConnectWordpressRequestSchema,
39
39
  trafficEventKindSchema
40
- } from "./chunk-MMFSI3OG.js";
40
+ } from "./chunk-SLCGTOKT.js";
41
41
 
42
42
  // src/config.ts
43
43
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  setTelemetrySource,
30
30
  showFirstRunNotice,
31
31
  trackEvent
32
- } from "./chunk-JYBNBK25.js";
32
+ } from "./chunk-4D2XUMMV.js";
33
33
  import {
34
34
  CliError,
35
35
  EXIT_SYSTEM_ERROR,
@@ -46,7 +46,7 @@ import {
46
46
  saveConfig,
47
47
  saveConfigPatch,
48
48
  usageError
49
- } from "./chunk-HJIIJI7K.js";
49
+ } from "./chunk-WGGB7QBD.js";
50
50
  import {
51
51
  apiKeys,
52
52
  createClient,
@@ -54,7 +54,7 @@ import {
54
54
  projects,
55
55
  queries,
56
56
  renderReportHtml
57
- } from "./chunk-4AZ7VES6.js";
57
+ } from "./chunk-BGLBWFMV.js";
58
58
  import {
59
59
  AdsOperationKinds,
60
60
  AdsOperationStates,
@@ -95,7 +95,7 @@ import {
95
95
  providerQuotaPolicySchema,
96
96
  resolveProviderInput,
97
97
  winnabilityClassSchema
98
- } from "./chunk-MMFSI3OG.js";
98
+ } from "./chunk-SLCGTOKT.js";
99
99
 
100
100
  // src/cli.ts
101
101
  import { pathToFileURL } from "url";
@@ -3763,10 +3763,16 @@ async function gaSessionHistory(project, opts) {
3763
3763
  console.log(` ${"DATE".padEnd(dateWidth)} ${"SESSIONS".padEnd(10)}${"ORGANIC".padEnd(10)}${"USERS".padEnd(8)}`);
3764
3764
  console.log(` ${"\u2500".repeat(dateWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
3765
3765
  for (const row of result) {
3766
+ const users = row.usersSource === "deduplicated" ? String(row.users) : `${row.users}*`;
3766
3767
  console.log(
3767
- ` ${row.date.padEnd(dateWidth)} ${String(row.sessions).padEnd(10)}${String(row.organicSessions).padEnd(10)}${String(row.users).padEnd(8)}`
3768
+ ` ${row.date.padEnd(dateWidth)} ${String(row.sessions).padEnd(10)}${String(row.organicSessions).padEnd(10)}${users.padEnd(8)}`
3768
3769
  );
3769
3770
  }
3771
+ if (result.some((row) => row.usersSource !== "deduplicated")) {
3772
+ console.log("\n * Visitor count is approximate \u2014 it adds up each page a visitor landed on,");
3773
+ console.log(" so someone who read several pages is counted more than once. Run");
3774
+ console.log(` "canonry ga sync ${project}" to replace these with exact counts.`);
3775
+ }
3770
3776
  }
3771
3777
  async function gaCoverage(project, format) {
3772
3778
  const client = getClient6();
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-JYBNBK25.js";
3
+ } from "./chunk-4D2XUMMV.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-HJIIJI7K.js";
7
- import "./chunk-4AZ7VES6.js";
8
- import "./chunk-MMFSI3OG.js";
6
+ } from "./chunk-WGGB7QBD.js";
7
+ import "./chunk-BGLBWFMV.js";
8
+ import "./chunk-SLCGTOKT.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-4AZ7VES6.js";
4
- import "./chunk-MMFSI3OG.js";
3
+ } from "./chunk-BGLBWFMV.js";
4
+ import "./chunk-SLCGTOKT.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-HJIIJI7K.js";
6
+ } from "./chunk-WGGB7QBD.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-MMFSI3OG.js";
9
+ } from "./chunk-SLCGTOKT.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.123.0",
3
+ "version": "4.124.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",
@@ -66,26 +66,26 @@
66
66
  "tsup": "^8.5.1",
67
67
  "tsx": "^4.19.0",
68
68
  "@ainyc/canonry-api-client": "0.0.0",
69
- "@ainyc/canonry-config": "0.0.0",
70
69
  "@ainyc/canonry-contracts": "0.0.0",
71
- "@ainyc/canonry-db": "0.0.0",
70
+ "@ainyc/canonry-config": "0.0.0",
72
71
  "@ainyc/canonry-api-routes": "0.0.0",
72
+ "@ainyc/canonry-db": "0.0.0",
73
73
  "@ainyc/canonry-integration-bing": "0.0.0",
74
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
75
74
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
76
75
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
77
- "@ainyc/canonry-integration-google": "0.0.0",
78
76
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
77
+ "@ainyc/canonry-integration-google-places": "0.0.0",
79
78
  "@ainyc/canonry-integration-traffic": "0.0.0",
80
79
  "@ainyc/canonry-integration-wordpress": "0.0.0",
81
- "@ainyc/canonry-integration-google-places": "0.0.0",
82
80
  "@ainyc/canonry-intelligence": "0.0.0",
83
- "@ainyc/canonry-provider-claude": "0.0.0",
84
81
  "@ainyc/canonry-provider-cdp": "0.0.0",
85
- "@ainyc/canonry-provider-perplexity": "0.0.0",
86
- "@ainyc/canonry-provider-openai": "0.0.0",
82
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
83
+ "@ainyc/canonry-provider-gemini": "0.0.0",
84
+ "@ainyc/canonry-provider-claude": "0.0.0",
87
85
  "@ainyc/canonry-provider-local": "0.0.0",
88
- "@ainyc/canonry-provider-gemini": "0.0.0"
86
+ "@ainyc/canonry-integration-google": "0.0.0",
87
+ "@ainyc/canonry-provider-openai": "0.0.0",
88
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
89
89
  },
90
90
  "scripts": {
91
91
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",