@ainyc/canonry 4.35.0 → 4.36.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.
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-B3FBOECD.js";
8
+ } from "./chunk-O7S623DL.js";
9
9
  import {
10
10
  DEFAULT_RUN_HISTORY_LIMIT,
11
11
  IntelligenceService,
@@ -29,6 +29,7 @@ import {
29
29
  buildContentTargetRows,
30
30
  buildGapQueryScore,
31
31
  buildInventory,
32
+ buildMentionCoverage,
32
33
  buildMentionLandscape,
33
34
  buildMovementSummary,
34
35
  buildOverviewCompetitors,
@@ -73,7 +74,7 @@ import {
73
74
  schedules,
74
75
  trafficSources,
75
76
  usageCounters
76
- } from "./chunk-MLS5KJWK.js";
77
+ } from "./chunk-JQQXMCQ7.js";
77
78
  import {
78
79
  AGENT_MEMORY_VALUE_MAX_BYTES,
79
80
  AGENT_PROVIDER_IDS,
@@ -186,7 +187,7 @@ import {
186
187
  visibilityStateFromAnswerMentioned,
187
188
  windowCutoff,
188
189
  wordpressEnvSchema
189
- } from "./chunk-EM5GVF3C.js";
190
+ } from "./chunk-XJVYVURK.js";
190
191
 
191
192
  // src/telemetry.ts
192
193
  import crypto from "crypto";
@@ -475,8 +476,8 @@ function checkLatestVersionForServer(opts) {
475
476
  // src/server.ts
476
477
  import { createRequire as createRequire4 } from "module";
477
478
  import crypto34 from "crypto";
478
- import fs12 from "fs";
479
- import path14 from "path";
479
+ import fs13 from "fs";
480
+ import path15 from "path";
480
481
  import { fileURLToPath as fileURLToPath2 } from "url";
481
482
  import { eq as eq41 } from "drizzle-orm";
482
483
  import Fastify from "fastify";
@@ -548,7 +549,7 @@ async function authPlugin(app, opts = {}) {
548
549
 
549
550
  // ../api-routes/src/projects.ts
550
551
  import crypto4 from "crypto";
551
- import { eq as eq3 } from "drizzle-orm";
552
+ import { eq as eq3, sql as sql2 } from "drizzle-orm";
552
553
 
553
554
  // ../api-routes/src/helpers.ts
554
555
  import crypto3 from "crypto";
@@ -726,6 +727,30 @@ async function projectRoutes(app, opts) {
726
727
  const project = resolveProject(app.db, request.params.name);
727
728
  return reply.send(formatProject(project));
728
729
  });
730
+ app.get("/projects/:name/delete-preview", async (request, reply) => {
731
+ const project = resolveProject(app.db, request.params.name);
732
+ const pid = project.id;
733
+ const count = (n) => n ?? 0;
734
+ const queryCount = app.db.select({ n: sql2`count(*)` }).from(queries).where(eq3(queries.projectId, pid)).get();
735
+ const competitorCount = app.db.select({ n: sql2`count(*)` }).from(competitors).where(eq3(competitors.projectId, pid)).get();
736
+ const runCount = app.db.select({ n: sql2`count(*)` }).from(runs).where(eq3(runs.projectId, pid)).get();
737
+ const snapshotCount = app.db.select({ n: sql2`count(*)` }).from(querySnapshots).innerJoin(runs, eq3(querySnapshots.runId, runs.id)).where(eq3(runs.projectId, pid)).get();
738
+ const insightCount = app.db.select({ n: sql2`count(*)` }).from(insights).where(eq3(insights.projectId, pid)).get();
739
+ const auditLogCount = app.db.select({ n: sql2`count(*)` }).from(auditLog).where(eq3(auditLog.projectId, pid)).get();
740
+ return reply.send({
741
+ project: { id: project.id, name: project.name },
742
+ cascadeRows: {
743
+ queries: count(queryCount?.n),
744
+ competitors: count(competitorCount?.n),
745
+ runs: count(runCount?.n),
746
+ snapshots: count(snapshotCount?.n),
747
+ insights: count(insightCount?.n)
748
+ },
749
+ detachedRows: {
750
+ auditLog: count(auditLogCount?.n)
751
+ }
752
+ });
753
+ });
729
754
  app.delete("/projects/:name", async (request, reply) => {
730
755
  const project = resolveProject(app.db, request.params.name);
731
756
  writeAuditLog(app.db, {
@@ -1300,7 +1325,7 @@ function parseCompetitorBatch(value) {
1300
1325
 
1301
1326
  // ../api-routes/src/runs.ts
1302
1327
  import crypto8 from "crypto";
1303
- import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql2 } from "drizzle-orm";
1328
+ import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql3 } from "drizzle-orm";
1304
1329
 
1305
1330
  // ../api-routes/src/run-queue.ts
1306
1331
  import crypto7 from "crypto";
@@ -1472,7 +1497,7 @@ async function runRoutes(app, opts) {
1472
1497
  });
1473
1498
  app.get("/projects/:name/runs/latest", async (request, reply) => {
1474
1499
  const project = resolveProject(app.db, request.params.name);
1475
- const countRow = app.db.select({ count: sql2`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
1500
+ const countRow = app.db.select({ count: sql3`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
1476
1501
  const totalRuns = countRow?.count ?? 0;
1477
1502
  const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
1478
1503
  if (!latestRun) {
@@ -1800,7 +1825,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
1800
1825
  const body = JSON.stringify(payload);
1801
1826
  const isHttps = target.url.protocol === "https:";
1802
1827
  const port = target.url.port ? Number(target.url.port) : isHttps ? 443 : 80;
1803
- const path15 = `${target.url.pathname}${target.url.search}`;
1828
+ const path16 = `${target.url.pathname}${target.url.search}`;
1804
1829
  const headers = {
1805
1830
  "Content-Length": String(Buffer.byteLength(body)),
1806
1831
  "Content-Type": "application/json",
@@ -1816,7 +1841,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
1816
1841
  headers,
1817
1842
  hostname: target.address,
1818
1843
  method: "POST",
1819
- path: path15,
1844
+ path: path16,
1820
1845
  port,
1821
1846
  timeout: REQUEST_TIMEOUT_MS
1822
1847
  };
@@ -2984,7 +3009,7 @@ async function intelligenceRoutes(app) {
2984
3009
  }
2985
3010
 
2986
3011
  // ../api-routes/src/report.ts
2987
- import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray5, lt, lte, ne, or as or3, sql as sql3 } from "drizzle-orm";
3012
+ import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray5, lt, lte, ne, or as or3, sql as sql4 } from "drizzle-orm";
2988
3013
 
2989
3014
  // ../api-routes/src/report-renderer.ts
2990
3015
  var COLORS = {
@@ -3033,9 +3058,9 @@ function inferAdSource(params) {
3033
3058
  function formatLandingPageHtml(raw) {
3034
3059
  const value = raw ?? "";
3035
3060
  const queryIdx = value.indexOf("?");
3036
- const path15 = queryIdx === -1 ? value : value.slice(0, queryIdx);
3061
+ const path16 = queryIdx === -1 ? value : value.slice(0, queryIdx);
3037
3062
  const query = queryIdx === -1 ? "" : value.slice(queryIdx + 1);
3038
- const pathHtml = `<span class="page-path">${escapeHtml(path15 || "/")}</span>`;
3063
+ const pathHtml = `<span class="page-path">${escapeHtml(path16 || "/")}</span>`;
3039
3064
  if (!query) return pathHtml;
3040
3065
  let summary = "";
3041
3066
  try {
@@ -4389,7 +4414,7 @@ function renderLineChart(points, color, title, height = 200) {
4389
4414
  y: padY + usableH - p.y / max * usableH,
4390
4415
  raw: p
4391
4416
  }));
4392
- const path15 = xy.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
4417
+ const path16 = xy.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
4393
4418
  const dots = xy.map((p) => `<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="3" fill="${color}" />`).join("");
4394
4419
  const xLabels = xy.map((p, i) => {
4395
4420
  if (points.length > 8 && i % Math.ceil(points.length / 6) !== 0 && i !== points.length - 1) return "";
@@ -4401,7 +4426,7 @@ function renderLineChart(points, color, title, height = 200) {
4401
4426
  <line x1="${padX}" y1="${padY + usableH}" x2="${padX + usableW}" y2="${padY + usableH}" stroke="${COLORS.border}" stroke-width="1" />
4402
4427
  <text x="${padX - 6}" y="${(padY + 4).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">${formatNumber(max)}</text>
4403
4428
  <text x="${padX - 6}" y="${(padY + usableH).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">0</text>
4404
- <path d="${path15}" stroke="${color}" stroke-width="2" fill="none" />
4429
+ <path d="${path16}" stroke="${color}" stroke-width="2" fill="none" />
4405
4430
  ${dots}
4406
4431
  ${xLabels}
4407
4432
  </svg>
@@ -5375,9 +5400,9 @@ function buildGaTrafficByPage(db, projectId) {
5375
5400
  }).from(gaTrafficSnapshots).where(eq12(gaTrafficSnapshots.projectId, projectId)).all();
5376
5401
  const map = /* @__PURE__ */ new Map();
5377
5402
  for (const row of rows) {
5378
- const path15 = extractPath(row.landingPage);
5379
- if (!path15) continue;
5380
- map.set(path15, (map.get(path15) ?? 0) + (row.sessions ?? 0));
5403
+ const path16 = extractPath(row.landingPage);
5404
+ if (!path16) continue;
5405
+ map.set(path16, (map.get(path16) ?? 0) + (row.sessions ?? 0));
5381
5406
  }
5382
5407
  return map;
5383
5408
  }
@@ -5572,8 +5597,8 @@ function normalizeDomain(domain) {
5572
5597
  function extractPath(url) {
5573
5598
  if (!url) return "";
5574
5599
  const match = /^https?:\/\/[^/]+(.*)$/.exec(url.trim());
5575
- const path15 = match ? match[1] : url.trim();
5576
- const stripped = path15.replace(/\/+$/, "");
5600
+ const path16 = match ? match[1] : url.trim();
5601
+ const stripped = path16.replace(/\/+$/, "");
5577
5602
  return stripped || "/";
5578
5603
  }
5579
5604
 
@@ -5860,7 +5885,7 @@ function buildAiReferrals(db, projectId) {
5860
5885
  return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
5861
5886
  }
5862
5887
  function nonSubresourceReferralPathCondition() {
5863
- return sql3`
5888
+ return sql4`
5864
5889
  LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
5865
5890
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
5866
5891
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
@@ -5899,7 +5924,7 @@ function buildServerActivity(db, projectId) {
5899
5924
  const priorStart = new Date(priorStartMs).toISOString();
5900
5925
  const trendStart = new Date(trendStartMs).toISOString();
5901
5926
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5902
- db.select({ total: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5927
+ db.select({ total: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5903
5928
  and6(
5904
5929
  eq13(crawlerEventsHourly.projectId, projectId),
5905
5930
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
@@ -5909,7 +5934,7 @@ function buildServerActivity(db, projectId) {
5909
5934
  ).get()?.total ?? 0
5910
5935
  );
5911
5936
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5912
- db.select({ total: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5937
+ db.select({ total: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5913
5938
  and6(
5914
5939
  eq13(crawlerEventsHourly.projectId, projectId),
5915
5940
  ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
@@ -5919,7 +5944,7 @@ function buildServerActivity(db, projectId) {
5919
5944
  ).get()?.total ?? 0
5920
5945
  );
5921
5946
  const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5922
- db.select({ total: sql3`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
5947
+ db.select({ total: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
5923
5948
  and6(
5924
5949
  eq13(aiReferralEventsHourly.projectId, projectId),
5925
5950
  nonSubresourceReferralPathCondition(),
@@ -5937,7 +5962,7 @@ function buildServerActivity(db, projectId) {
5937
5962
  const crawlerByOperatorRows = db.select({
5938
5963
  operator: crawlerEventsHourly.operator,
5939
5964
  verificationStatus: crawlerEventsHourly.verificationStatus,
5940
- hits: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5965
+ hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5941
5966
  }).from(crawlerEventsHourly).where(
5942
5967
  and6(
5943
5968
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -5947,7 +5972,7 @@ function buildServerActivity(db, projectId) {
5947
5972
  ).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
5948
5973
  const crawlerByOperatorPriorRows = db.select({
5949
5974
  operator: crawlerEventsHourly.operator,
5950
- hits: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5975
+ hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5951
5976
  }).from(crawlerEventsHourly).where(
5952
5977
  and6(
5953
5978
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -5958,7 +5983,7 @@ function buildServerActivity(db, projectId) {
5958
5983
  ).groupBy(crawlerEventsHourly.operator).all();
5959
5984
  const referralByOperatorRows = db.select({
5960
5985
  operator: aiReferralEventsHourly.operator,
5961
- hits: sql3`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
5986
+ hits: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
5962
5987
  }).from(aiReferralEventsHourly).where(
5963
5988
  and6(
5964
5989
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -5998,8 +6023,8 @@ function buildServerActivity(db, projectId) {
5998
6023
  );
5999
6024
  const topPathsRows = db.select({
6000
6025
  path: crawlerEventsHourly.pathNormalized,
6001
- hits: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
6002
- operators: sql3`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
6026
+ hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
6027
+ operators: sql4`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
6003
6028
  }).from(crawlerEventsHourly).where(
6004
6029
  and6(
6005
6030
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -6007,7 +6032,7 @@ function buildServerActivity(db, projectId) {
6007
6032
  gte(crawlerEventsHourly.tsHour, headlineStart),
6008
6033
  lte(crawlerEventsHourly.tsHour, headlineEnd)
6009
6034
  )
6010
- ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql3`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6035
+ ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql4`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6011
6036
  const topCrawledPaths = topPathsRows.map((r) => ({
6012
6037
  path: r.path,
6013
6038
  verifiedHits: Number(r.hits),
@@ -6015,8 +6040,8 @@ function buildServerActivity(db, projectId) {
6015
6040
  }));
6016
6041
  const referralProductsRows = db.select({
6017
6042
  product: aiReferralEventsHourly.product,
6018
- arrivals: sql3`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6019
- landingPaths: sql3`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
6043
+ arrivals: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6044
+ landingPaths: sql4`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
6020
6045
  }).from(aiReferralEventsHourly).where(
6021
6046
  and6(
6022
6047
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6024,7 +6049,7 @@ function buildServerActivity(db, projectId) {
6024
6049
  gte(aiReferralEventsHourly.tsHour, headlineStart),
6025
6050
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6026
6051
  )
6027
- ).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql3`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
6052
+ ).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql4`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
6028
6053
  const referralProducts = referralProductsRows.map((r) => ({
6029
6054
  product: r.product,
6030
6055
  arrivals: Number(r.arrivals),
@@ -6032,8 +6057,8 @@ function buildServerActivity(db, projectId) {
6032
6057
  }));
6033
6058
  const topReferralRows = db.select({
6034
6059
  path: aiReferralEventsHourly.landingPathNormalized,
6035
- arrivals: sql3`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6036
- products: sql3`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
6060
+ arrivals: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6061
+ products: sql4`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
6037
6062
  }).from(aiReferralEventsHourly).where(
6038
6063
  and6(
6039
6064
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6041,15 +6066,15 @@ function buildServerActivity(db, projectId) {
6041
6066
  gte(aiReferralEventsHourly.tsHour, headlineStart),
6042
6067
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6043
6068
  )
6044
- ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql3`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6069
+ ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql4`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6045
6070
  const topReferralLandingPaths = topReferralRows.map((r) => ({
6046
6071
  path: r.path,
6047
6072
  arrivals: Number(r.arrivals),
6048
6073
  distinctProducts: Number(r.products)
6049
6074
  }));
6050
6075
  const crawlerTrendRows = db.select({
6051
- date: sql3`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
6052
- hits: sql3`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6076
+ date: sql4`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
6077
+ hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6053
6078
  }).from(crawlerEventsHourly).where(
6054
6079
  and6(
6055
6080
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -6057,10 +6082,10 @@ function buildServerActivity(db, projectId) {
6057
6082
  gte(crawlerEventsHourly.tsHour, trendStart),
6058
6083
  lte(crawlerEventsHourly.tsHour, headlineEnd)
6059
6084
  )
6060
- ).groupBy(sql3`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
6085
+ ).groupBy(sql4`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
6061
6086
  const referralTrendRows = db.select({
6062
- date: sql3`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
6063
- hits: sql3`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6087
+ date: sql4`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
6088
+ hits: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6064
6089
  }).from(aiReferralEventsHourly).where(
6065
6090
  and6(
6066
6091
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6068,7 +6093,7 @@ function buildServerActivity(db, projectId) {
6068
6093
  gte(aiReferralEventsHourly.tsHour, trendStart),
6069
6094
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6070
6095
  )
6071
- ).groupBy(sql3`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
6096
+ ).groupBy(sql4`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
6072
6097
  const dailyTrendMap = /* @__PURE__ */ new Map();
6073
6098
  for (const r of crawlerTrendRows) {
6074
6099
  const e = dailyTrendMap.get(r.date) ?? { verifiedCrawlerHits: 0, referralArrivals: 0 };
@@ -7128,7 +7153,7 @@ function normalizeDomain2(domain) {
7128
7153
  }
7129
7154
 
7130
7155
  // ../api-routes/src/composites.ts
7131
- import { eq as eq15, and as and7, desc as desc7, sql as sql4, like, or as or4, inArray as inArray7 } from "drizzle-orm";
7156
+ import { eq as eq15, and as and7, desc as desc7, sql as sql5, like, or as or4, inArray as inArray7 } from "drizzle-orm";
7132
7157
  var TOP_INSIGHT_LIMIT = 5;
7133
7158
  var SEARCH_HIT_HARD_LIMIT = 50;
7134
7159
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -7181,6 +7206,7 @@ async function compositeRoutes(app) {
7181
7206
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
7182
7207
  const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
7183
7208
  const scores = {
7209
+ mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
7184
7210
  visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
7185
7211
  gapQueries: buildGapQueryScore(latestSnapshots),
7186
7212
  indexCoverage: buildIndexCoverageScore(app, project.id),
@@ -7245,9 +7271,9 @@ async function compositeRoutes(app) {
7245
7271
  and7(
7246
7272
  eq15(queries.projectId, project.id),
7247
7273
  or4(
7248
- sql4`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
7249
- sql4`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
7250
- sql4`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
7274
+ sql5`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
7275
+ sql5`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
7276
+ sql5`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
7251
7277
  like(queries.query, pattern)
7252
7278
  )
7253
7279
  )
@@ -7258,8 +7284,8 @@ async function compositeRoutes(app) {
7258
7284
  or4(
7259
7285
  like(insights.title, pattern),
7260
7286
  like(insights.query, pattern),
7261
- sql4`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
7262
- sql4`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
7287
+ sql5`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
7288
+ sql5`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
7263
7289
  )
7264
7290
  )
7265
7291
  ).orderBy(desc7(insights.createdAt)).limit(limit + 1).all();
@@ -7331,6 +7357,7 @@ function loadSnapshotsByRunIds(app, runIds) {
7331
7357
  provider: querySnapshots.provider,
7332
7358
  model: querySnapshots.model,
7333
7359
  citationState: querySnapshots.citationState,
7360
+ answerMentioned: querySnapshots.answerMentioned,
7334
7361
  competitorOverlap: querySnapshots.competitorOverlap,
7335
7362
  citedDomains: querySnapshots.citedDomains
7336
7363
  }).from(querySnapshots).where(inArray7(querySnapshots.runId, [...runIds])).all());
@@ -7341,6 +7368,7 @@ function loadSnapshotsByRunIds(app, runIds) {
7341
7368
  provider: row.provider,
7342
7369
  model: row.model,
7343
7370
  citationState: row.citationState,
7371
+ answerMentioned: row.answerMentioned,
7344
7372
  competitorOverlap: parseJsonColumn(row.competitorOverlap, []),
7345
7373
  citedDomains: parseJsonColumn(row.citedDomains, [])
7346
7374
  });
@@ -7936,6 +7964,18 @@ var routeCatalog = [
7936
7964
  404: { description: "Project not found." }
7937
7965
  }
7938
7966
  },
7967
+ {
7968
+ method: "get",
7969
+ path: "/api/v1/projects/{name}/delete-preview",
7970
+ summary: "Preview the cascade impact of deleting a project",
7971
+ description: "Read-only impact summary backing `canonry project delete --dry-run`. Returns counts of rows that would cascade-delete (queries, competitors, runs, snapshots, insights) and rows that would be detached (audit_log \u2014 `project_id` set to NULL).",
7972
+ tags: ["projects"],
7973
+ parameters: [nameParameter],
7974
+ responses: {
7975
+ 200: { description: "Preview of cascade impact." },
7976
+ 404: { description: "Project not found." }
7977
+ }
7978
+ },
7939
7979
  {
7940
7980
  method: "post",
7941
7981
  path: "/api/v1/projects/{name}/locations",
@@ -10941,8 +10981,8 @@ async function openApiRoutes(app, opts = {}) {
10941
10981
  return reply.type("application/json").send(buildOpenApiDocument(opts));
10942
10982
  });
10943
10983
  }
10944
- function buildOperationId(method, path15) {
10945
- const parts = path15.split("/").filter(Boolean).map((part) => {
10984
+ function buildOperationId(method, path16) {
10985
+ const parts = path16.split("/").filter(Boolean).map((part) => {
10946
10986
  if (part.startsWith("{") && part.endsWith("}")) {
10947
10987
  return `by-${part.slice(1, -1)}`;
10948
10988
  }
@@ -11378,7 +11418,7 @@ function formatNotification(row) {
11378
11418
 
11379
11419
  // ../api-routes/src/google.ts
11380
11420
  import crypto14 from "crypto";
11381
- import { eq as eq18, and as and9, desc as desc8, sql as sql5 } from "drizzle-orm";
11421
+ import { eq as eq18, and as and9, desc as desc8, sql as sql6 } from "drizzle-orm";
11382
11422
 
11383
11423
  // ../integration-google/src/constants.ts
11384
11424
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -12604,11 +12644,11 @@ async function googleRoutes(app, opts) {
12604
12644
  const { startDate, endDate, query, page, limit, offset } = request.query;
12605
12645
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
12606
12646
  const conditions = [eq18(gscSearchData.projectId, project.id)];
12607
- if (startDate) conditions.push(sql5`${gscSearchData.date} >= ${startDate}`);
12608
- else if (cutoffDate) conditions.push(sql5`${gscSearchData.date} >= ${cutoffDate}`);
12609
- if (endDate) conditions.push(sql5`${gscSearchData.date} <= ${endDate}`);
12610
- if (query) conditions.push(sql5`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
12611
- if (page) conditions.push(sql5`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
12647
+ if (startDate) conditions.push(sql6`${gscSearchData.date} >= ${startDate}`);
12648
+ else if (cutoffDate) conditions.push(sql6`${gscSearchData.date} >= ${cutoffDate}`);
12649
+ if (endDate) conditions.push(sql6`${gscSearchData.date} <= ${endDate}`);
12650
+ if (query) conditions.push(sql6`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
12651
+ if (page) conditions.push(sql6`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
12612
12652
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
12613
12653
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
12614
12654
  const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
@@ -13853,7 +13893,7 @@ async function cdpRoutes(app, opts) {
13853
13893
 
13854
13894
  // ../api-routes/src/ga.ts
13855
13895
  import crypto16 from "crypto";
13856
- import { eq as eq21, desc as desc10, and as and12, sql as sql6 } from "drizzle-orm";
13896
+ import { eq as eq21, desc as desc10, and as and12, sql as sql7 } from "drizzle-orm";
13857
13897
  function gaLog(level, action, ctx) {
13858
13898
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
13859
13899
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -14150,8 +14190,8 @@ async function ga4Routes(app, opts) {
14150
14190
  tx.delete(gaTrafficSnapshots).where(
14151
14191
  and12(
14152
14192
  eq21(gaTrafficSnapshots.projectId, project.id),
14153
- sql6`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
14154
- sql6`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
14193
+ sql7`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
14194
+ sql7`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
14155
14195
  )
14156
14196
  ).run();
14157
14197
  for (const row of rows) {
@@ -14174,8 +14214,8 @@ async function ga4Routes(app, opts) {
14174
14214
  tx.delete(gaAiReferrals).where(
14175
14215
  and12(
14176
14216
  eq21(gaAiReferrals.projectId, project.id),
14177
- sql6`${gaAiReferrals.date} >= ${summary.periodStart}`,
14178
- sql6`${gaAiReferrals.date} <= ${summary.periodEnd}`
14217
+ sql7`${gaAiReferrals.date} >= ${summary.periodStart}`,
14218
+ sql7`${gaAiReferrals.date} <= ${summary.periodEnd}`
14179
14219
  )
14180
14220
  ).run();
14181
14221
  for (const row of aiReferrals) {
@@ -14200,8 +14240,8 @@ async function ga4Routes(app, opts) {
14200
14240
  tx.delete(gaSocialReferrals).where(
14201
14241
  and12(
14202
14242
  eq21(gaSocialReferrals.projectId, project.id),
14203
- sql6`${gaSocialReferrals.date} >= ${summary.periodStart}`,
14204
- sql6`${gaSocialReferrals.date} <= ${summary.periodEnd}`
14243
+ sql7`${gaSocialReferrals.date} >= ${summary.periodStart}`,
14244
+ sql7`${gaSocialReferrals.date} <= ${summary.periodEnd}`
14205
14245
  )
14206
14246
  ).run();
14207
14247
  for (const row of socialReferrals) {
@@ -14291,11 +14331,11 @@ async function ga4Routes(app, opts) {
14291
14331
  const cutoff = windowCutoff(window);
14292
14332
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
14293
14333
  const snapshotConditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
14294
- if (cutoffDate) snapshotConditions.push(sql6`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14334
+ if (cutoffDate) snapshotConditions.push(sql7`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14295
14335
  const aiConditions = [eq21(gaAiReferrals.projectId, project.id)];
14296
- if (cutoffDate) aiConditions.push(sql6`${gaAiReferrals.date} >= ${cutoffDate}`);
14336
+ if (cutoffDate) aiConditions.push(sql7`${gaAiReferrals.date} >= ${cutoffDate}`);
14297
14337
  const socialConditions = [eq21(gaSocialReferrals.projectId, project.id)];
14298
- if (cutoffDate) socialConditions.push(sql6`${gaSocialReferrals.date} >= ${cutoffDate}`);
14338
+ if (cutoffDate) socialConditions.push(sql7`${gaSocialReferrals.date} >= ${cutoffDate}`);
14299
14339
  const windowSummaryRow = cutoffDate ? app.db.select({
14300
14340
  totalSessions: gaTrafficWindowSummaries.totalSessions,
14301
14341
  totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
@@ -14308,9 +14348,9 @@ async function ga4Routes(app, opts) {
14308
14348
  )
14309
14349
  ).get() : null;
14310
14350
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
14311
- totalSessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
14312
- totalOrganicSessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
14313
- totalUsers: sql6`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
14351
+ totalSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
14352
+ totalOrganicSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
14353
+ totalUsers: sql7`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
14314
14354
  }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
14315
14355
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
14316
14356
  totalSessions: gaTrafficSummaries.totalSessions,
@@ -14318,38 +14358,38 @@ async function ga4Routes(app, opts) {
14318
14358
  totalUsers: gaTrafficSummaries.totalUsers
14319
14359
  }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
14320
14360
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
14321
- totalDirectSessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
14361
+ totalDirectSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
14322
14362
  }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
14323
14363
  const summaryMeta = app.db.select({
14324
14364
  periodStart: gaTrafficSummaries.periodStart,
14325
14365
  periodEnd: gaTrafficSummaries.periodEnd
14326
14366
  }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
14327
14367
  const rows = app.db.select({
14328
- landingPage: sql6`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14329
- sessions: sql6`SUM(${gaTrafficSnapshots.sessions})`,
14330
- organicSessions: sql6`SUM(${gaTrafficSnapshots.organicSessions})`,
14331
- directSessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
14332
- users: sql6`SUM(${gaTrafficSnapshots.users})`
14333
- }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql6`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql6`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14368
+ landingPage: sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14369
+ sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14370
+ organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14371
+ directSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
14372
+ users: sql7`SUM(${gaTrafficSnapshots.users})`
14373
+ }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql7`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14334
14374
  const aiReferralRows = app.db.select({
14335
14375
  source: gaAiReferrals.source,
14336
14376
  medium: gaAiReferrals.medium,
14337
14377
  sourceDimension: gaAiReferrals.sourceDimension,
14338
- sessions: sql6`SUM(${gaAiReferrals.sessions})`,
14339
- users: sql6`SUM(${gaAiReferrals.users})`
14378
+ sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14379
+ users: sql7`SUM(${gaAiReferrals.users})`
14340
14380
  }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
14341
14381
  const aiReferralLandingPageRows = app.db.select({
14342
14382
  source: gaAiReferrals.source,
14343
14383
  medium: gaAiReferrals.medium,
14344
14384
  sourceDimension: gaAiReferrals.sourceDimension,
14345
- landingPage: sql6`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14346
- sessions: sql6`SUM(${gaAiReferrals.sessions})`,
14347
- users: sql6`SUM(${gaAiReferrals.users})`
14385
+ landingPage: sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14386
+ sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14387
+ users: sql7`SUM(${gaAiReferrals.users})`
14348
14388
  }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
14349
14389
  gaAiReferrals.source,
14350
14390
  gaAiReferrals.medium,
14351
14391
  gaAiReferrals.sourceDimension,
14352
- sql6`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14392
+ sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14353
14393
  ).all();
14354
14394
  const aiReferrals = pickWinningDimension(
14355
14395
  aiReferralRows,
@@ -14360,10 +14400,10 @@ async function ga4Routes(app, opts) {
14360
14400
  (r) => `${r.source}\0${r.medium}\0${r.landingPage}`
14361
14401
  );
14362
14402
  const aiDeduped = app.db.select({
14363
- sessions: sql6`COALESCE(SUM(max_sessions), 0)`,
14364
- users: sql6`COALESCE(SUM(max_users), 0)`
14403
+ sessions: sql7`COALESCE(SUM(max_sessions), 0)`,
14404
+ users: sql7`COALESCE(SUM(max_users), 0)`
14365
14405
  }).from(
14366
- sql6`(
14406
+ sql7`(
14367
14407
  SELECT date, source, medium,
14368
14408
  MAX(dimension_sessions) AS max_sessions,
14369
14409
  MAX(dimension_users) AS max_users
@@ -14372,7 +14412,7 @@ async function ga4Routes(app, opts) {
14372
14412
  SUM(sessions) AS dimension_sessions,
14373
14413
  SUM(users) AS dimension_users
14374
14414
  FROM ga_ai_referrals
14375
- WHERE project_id = ${project.id}${cutoffDate ? sql6` AND date >= ${cutoffDate}` : sql6``}
14415
+ WHERE project_id = ${project.id}${cutoffDate ? sql7` AND date >= ${cutoffDate}` : sql7``}
14376
14416
  GROUP BY date, source, medium, source_dimension
14377
14417
  )
14378
14418
  GROUP BY date, source, medium
@@ -14380,8 +14420,8 @@ async function ga4Routes(app, opts) {
14380
14420
  ).get();
14381
14421
  const aiBySessionRows = app.db.select({
14382
14422
  channelGroup: gaAiReferrals.channelGroup,
14383
- sessions: sql6`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
14384
- users: sql6`COALESCE(SUM(${gaAiReferrals.users}), 0)`
14423
+ sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
14424
+ users: sql7`COALESCE(SUM(${gaAiReferrals.users}), 0)`
14385
14425
  }).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
14386
14426
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
14387
14427
  let aiBySessionUsers = 0;
@@ -14394,12 +14434,12 @@ async function ga4Routes(app, opts) {
14394
14434
  source: gaSocialReferrals.source,
14395
14435
  medium: gaSocialReferrals.medium,
14396
14436
  channelGroup: gaSocialReferrals.channelGroup,
14397
- sessions: sql6`SUM(${gaSocialReferrals.sessions})`,
14398
- users: sql6`SUM(${gaSocialReferrals.users})`
14399
- }).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql6`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14437
+ sessions: sql7`SUM(${gaSocialReferrals.sessions})`,
14438
+ users: sql7`SUM(${gaSocialReferrals.users})`
14439
+ }).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql7`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14400
14440
  const socialTotals = app.db.select({
14401
- sessions: sql6`SUM(${gaSocialReferrals.sessions})`,
14402
- users: sql6`SUM(${gaSocialReferrals.users})`
14441
+ sessions: sql7`SUM(${gaSocialReferrals.sessions})`,
14442
+ users: sql7`SUM(${gaSocialReferrals.users})`
14403
14443
  }).from(gaSocialReferrals).where(and12(...socialConditions)).get();
14404
14444
  const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
14405
14445
  const total = summaryRow?.totalSessions ?? 0;
@@ -14482,21 +14522,21 @@ async function ga4Routes(app, opts) {
14482
14522
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14483
14523
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14484
14524
  const conditions = [eq21(gaAiReferrals.projectId, project.id)];
14485
- if (cutoffDate) conditions.push(sql6`${gaAiReferrals.date} >= ${cutoffDate}`);
14525
+ if (cutoffDate) conditions.push(sql7`${gaAiReferrals.date} >= ${cutoffDate}`);
14486
14526
  const rows = app.db.select({
14487
14527
  date: gaAiReferrals.date,
14488
14528
  source: gaAiReferrals.source,
14489
14529
  medium: gaAiReferrals.medium,
14490
- landingPage: sql6`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14530
+ landingPage: sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14491
14531
  sourceDimension: gaAiReferrals.sourceDimension,
14492
- sessions: sql6`SUM(${gaAiReferrals.sessions})`,
14493
- users: sql6`SUM(${gaAiReferrals.users})`
14532
+ sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14533
+ users: sql7`SUM(${gaAiReferrals.users})`
14494
14534
  }).from(gaAiReferrals).where(and12(...conditions)).groupBy(
14495
14535
  gaAiReferrals.date,
14496
14536
  gaAiReferrals.source,
14497
14537
  gaAiReferrals.medium,
14498
14538
  gaAiReferrals.sourceDimension,
14499
- sql6`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14539
+ sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14500
14540
  ).orderBy(gaAiReferrals.date).all();
14501
14541
  return rows;
14502
14542
  });
@@ -14505,7 +14545,7 @@ async function ga4Routes(app, opts) {
14505
14545
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14506
14546
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14507
14547
  const conditions = [eq21(gaSocialReferrals.projectId, project.id)];
14508
- if (cutoffDate) conditions.push(sql6`${gaSocialReferrals.date} >= ${cutoffDate}`);
14548
+ if (cutoffDate) conditions.push(sql7`${gaSocialReferrals.date} >= ${cutoffDate}`);
14509
14549
  const rows = app.db.select({
14510
14550
  date: gaSocialReferrals.date,
14511
14551
  source: gaSocialReferrals.source,
@@ -14526,10 +14566,10 @@ async function ga4Routes(app, opts) {
14526
14566
  d.setDate(d.getDate() - n);
14527
14567
  return fmt(d);
14528
14568
  };
14529
- const sumSocial = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
14569
+ const sumSocial = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
14530
14570
  eq21(gaSocialReferrals.projectId, project.id),
14531
- sql6`${gaSocialReferrals.date} >= ${from}`,
14532
- sql6`${gaSocialReferrals.date} < ${to}`
14571
+ sql7`${gaSocialReferrals.date} >= ${from}`,
14572
+ sql7`${gaSocialReferrals.date} < ${to}`
14533
14573
  )).get();
14534
14574
  const current7d = sumSocial(daysAgo2(7), fmt(today));
14535
14575
  const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
@@ -14538,19 +14578,19 @@ async function ga4Routes(app, opts) {
14538
14578
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
14539
14579
  const sourceCurrent = app.db.select({
14540
14580
  source: gaSocialReferrals.source,
14541
- sessions: sql6`SUM(${gaSocialReferrals.sessions})`
14581
+ sessions: sql7`SUM(${gaSocialReferrals.sessions})`
14542
14582
  }).from(gaSocialReferrals).where(and12(
14543
14583
  eq21(gaSocialReferrals.projectId, project.id),
14544
- sql6`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
14545
- sql6`${gaSocialReferrals.date} < ${fmt(today)}`
14584
+ sql7`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
14585
+ sql7`${gaSocialReferrals.date} < ${fmt(today)}`
14546
14586
  )).groupBy(gaSocialReferrals.source).all();
14547
14587
  const sourcePrev = app.db.select({
14548
14588
  source: gaSocialReferrals.source,
14549
- sessions: sql6`SUM(${gaSocialReferrals.sessions})`
14589
+ sessions: sql7`SUM(${gaSocialReferrals.sessions})`
14550
14590
  }).from(gaSocialReferrals).where(and12(
14551
14591
  eq21(gaSocialReferrals.projectId, project.id),
14552
- sql6`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
14553
- sql6`${gaSocialReferrals.date} < ${daysAgo2(7)}`
14592
+ sql7`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
14593
+ sql7`${gaSocialReferrals.date} < ${daysAgo2(7)}`
14554
14594
  )).groupBy(gaSocialReferrals.source).all();
14555
14595
  const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
14556
14596
  let biggestMover = null;
@@ -14589,16 +14629,16 @@ async function ga4Routes(app, opts) {
14589
14629
  return fmt(d);
14590
14630
  };
14591
14631
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
14592
- const sumTotal = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql6`${gaTrafficSnapshots.date} >= ${from}`, sql6`${gaTrafficSnapshots.date} < ${to}`)).get();
14593
- const sumOrganic = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql6`${gaTrafficSnapshots.date} >= ${from}`, sql6`${gaTrafficSnapshots.date} < ${to}`)).get();
14594
- const sumDirect = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql6`${gaTrafficSnapshots.date} >= ${from}`, sql6`${gaTrafficSnapshots.date} < ${to}`)).get();
14595
- const sumAi = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14632
+ const sumTotal = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14633
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14634
+ const sumDirect = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14635
+ const sumAi = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14596
14636
  eq21(gaAiReferrals.projectId, project.id),
14597
- sql6`${gaAiReferrals.date} >= ${from}`,
14598
- sql6`${gaAiReferrals.date} < ${to}`,
14637
+ sql7`${gaAiReferrals.date} >= ${from}`,
14638
+ sql7`${gaAiReferrals.date} < ${to}`,
14599
14639
  eq21(gaAiReferrals.sourceDimension, "session")
14600
14640
  )).get();
14601
- const sumSocial = (from, to) => app.db.select({ sessions: sql6`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql6`${gaSocialReferrals.date} >= ${from}`, sql6`${gaSocialReferrals.date} < ${to}`)).get();
14641
+ const sumSocial = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${from}`, sql7`${gaSocialReferrals.date} < ${to}`)).get();
14602
14642
  const todayStr = fmt(today);
14603
14643
  const buildTrend = (sum) => {
14604
14644
  const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
@@ -14607,16 +14647,16 @@ async function ga4Routes(app, opts) {
14607
14647
  const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
14608
14648
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
14609
14649
  };
14610
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql6`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14650
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14611
14651
  eq21(gaAiReferrals.projectId, project.id),
14612
- sql6`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
14613
- sql6`${gaAiReferrals.date} < ${todayStr}`,
14652
+ sql7`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
14653
+ sql7`${gaAiReferrals.date} < ${todayStr}`,
14614
14654
  eq21(gaAiReferrals.sourceDimension, "session")
14615
14655
  )).groupBy(gaAiReferrals.source).all();
14616
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql6`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14656
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14617
14657
  eq21(gaAiReferrals.projectId, project.id),
14618
- sql6`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
14619
- sql6`${gaAiReferrals.date} < ${daysAgo2(7)}`,
14658
+ sql7`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
14659
+ sql7`${gaAiReferrals.date} < ${daysAgo2(7)}`,
14620
14660
  eq21(gaAiReferrals.sourceDimension, "session")
14621
14661
  )).groupBy(gaAiReferrals.source).all();
14622
14662
  const findBiggestMover = (current, prev) => {
@@ -14633,8 +14673,8 @@ async function ga4Routes(app, opts) {
14633
14673
  }
14634
14674
  return mover;
14635
14675
  };
14636
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql6`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql6`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql6`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
14637
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql6`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql6`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql6`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
14676
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql7`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql7`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
14677
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql7`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql7`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
14638
14678
  return {
14639
14679
  total: buildTrend(sumTotal),
14640
14680
  organic: buildTrend(sumOrganic),
@@ -14650,12 +14690,12 @@ async function ga4Routes(app, opts) {
14650
14690
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14651
14691
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14652
14692
  const conditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
14653
- if (cutoffDate) conditions.push(sql6`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14693
+ if (cutoffDate) conditions.push(sql7`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14654
14694
  const rows = app.db.select({
14655
14695
  date: gaTrafficSnapshots.date,
14656
- sessions: sql6`SUM(${gaTrafficSnapshots.sessions})`,
14657
- organicSessions: sql6`SUM(${gaTrafficSnapshots.organicSessions})`,
14658
- users: sql6`SUM(${gaTrafficSnapshots.users})`
14696
+ sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14697
+ organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14698
+ users: sql7`SUM(${gaTrafficSnapshots.users})`
14659
14699
  }).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
14660
14700
  return rows.map((r) => ({
14661
14701
  date: r.date,
@@ -14668,11 +14708,11 @@ async function ga4Routes(app, opts) {
14668
14708
  const project = resolveProject(app.db, request.params.name);
14669
14709
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14670
14710
  const trafficPages = app.db.select({
14671
- landingPage: sql6`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14672
- sessions: sql6`SUM(${gaTrafficSnapshots.sessions})`,
14673
- organicSessions: sql6`SUM(${gaTrafficSnapshots.organicSessions})`,
14674
- users: sql6`SUM(${gaTrafficSnapshots.users})`
14675
- }).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql6`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql6`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
14711
+ landingPage: sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14712
+ sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14713
+ organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14714
+ users: sql7`SUM(${gaTrafficSnapshots.users})`
14715
+ }).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql7`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
14676
14716
  return {
14677
14717
  pages: trafficPages.map((r) => ({
14678
14718
  landingPage: r.landingPage,
@@ -14888,10 +14928,10 @@ function buildAuthErrorMessage(res, responseText) {
14888
14928
  }
14889
14929
  return "WordPress credentials are invalid or lack permission for this action";
14890
14930
  }
14891
- async function fetchJson(connection, siteUrl, path15, init) {
14931
+ async function fetchJson(connection, siteUrl, path16, init) {
14892
14932
  if (siteUrl.startsWith("http:")) {
14893
14933
  }
14894
- const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path15}`, {
14934
+ const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path16}`, {
14895
14935
  ...init,
14896
14936
  headers: {
14897
14937
  "Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
@@ -16309,7 +16349,7 @@ async function wordpressRoutes(app, opts) {
16309
16349
 
16310
16350
  // ../api-routes/src/backlinks.ts
16311
16351
  import crypto18 from "crypto";
16312
- import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql7 } from "drizzle-orm";
16352
+ import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql8 } from "drizzle-orm";
16313
16353
 
16314
16354
  // ../integration-commoncrawl/src/constants.ts
16315
16355
  import os3 from "os";
@@ -16593,7 +16633,7 @@ async function queryBacklinks(opts) {
16593
16633
  const reversed = opts.targets.map(reverseDomain);
16594
16634
  const targetList = reversed.map(quote).join(", ");
16595
16635
  const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
16596
- const sql14 = `
16636
+ const sql15 = `
16597
16637
  WITH vertices AS (
16598
16638
  SELECT * FROM read_csv(
16599
16639
  ${quote(opts.vertexPath)},
@@ -16629,7 +16669,7 @@ async function queryBacklinks(opts) {
16629
16669
  const conn = await instance.connect();
16630
16670
  let rows;
16631
16671
  try {
16632
- const reader = await conn.runAndReadAll(sql14);
16672
+ const reader = await conn.runAndReadAll(sql15);
16633
16673
  rows = reader.getRowObjects();
16634
16674
  } finally {
16635
16675
  conn.disconnectSync?.();
@@ -16805,12 +16845,12 @@ function computeFilteredSummary(db, base) {
16805
16845
  );
16806
16846
  const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
16807
16847
  const unfilteredAgg = db.select({
16808
- count: sql7`count(*)`,
16809
- total: sql7`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16848
+ count: sql8`count(*)`,
16849
+ total: sql8`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16810
16850
  }).from(backlinkDomains).where(baseDomainCondition).get();
16811
16851
  const filteredAgg = db.select({
16812
- count: sql7`count(*)`,
16813
- total: sql7`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16852
+ count: sql8`count(*)`,
16853
+ total: sql8`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16814
16854
  }).from(backlinkDomains).where(filteredCondition).get();
16815
16855
  const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc11(backlinkDomains.numHosts)).limit(10).all();
16816
16856
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
@@ -16984,7 +17024,7 @@ async function backlinksRoutes(app, opts) {
16984
17024
  eq22(backlinkDomains.release, targetRelease)
16985
17025
  );
16986
17026
  const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
16987
- const totalRow = app.db.select({ count: sql7`count(*)` }).from(backlinkDomains).where(domainCondition).get();
17027
+ const totalRow = app.db.select({ count: sql8`count(*)` }).from(backlinkDomains).where(domainCondition).get();
16988
17028
  const rows = app.db.select({
16989
17029
  linkingDomain: backlinkDomains.linkingDomain,
16990
17030
  numHosts: backlinkDomains.numHosts
@@ -17019,7 +17059,7 @@ async function backlinksRoutes(app, opts) {
17019
17059
 
17020
17060
  // ../api-routes/src/traffic.ts
17021
17061
  import crypto20 from "crypto";
17022
- import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql8 } from "drizzle-orm";
17062
+ import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql9 } from "drizzle-orm";
17023
17063
 
17024
17064
  // ../integration-cloud-run/src/auth.ts
17025
17065
  import crypto19 from "crypto";
@@ -17518,8 +17558,8 @@ var ASSET_PATH_PREFIXES = [
17518
17558
  "/img/",
17519
17559
  "/static/"
17520
17560
  ];
17521
- function normalizeTrafficPathPattern(path15) {
17522
- const cleanPath = path15.trim() || "/";
17561
+ function normalizeTrafficPathPattern(path16) {
17562
+ const cleanPath = path16.trim() || "/";
17523
17563
  const pathOnly = cleanPath.split("?")[0] || "/";
17524
17564
  const segments = pathOnly.split("/").map((segment) => {
17525
17565
  if (!segment) return segment;
@@ -17568,8 +17608,8 @@ function resolveAiReferralLandingPath(event, evidenceType) {
17568
17608
  }
17569
17609
  return normalizeTrafficPathPattern(event.path);
17570
17610
  }
17571
- function isLikelySubresourcePath(path15) {
17572
- const cleanPath = path15.split("?")[0] || "/";
17611
+ function isLikelySubresourcePath(path16) {
17612
+ const cleanPath = path16.split("?")[0] || "/";
17573
17613
  return ASSET_PATH_PREFIXES.some((prefix) => cleanPath.startsWith(prefix)) || ASSET_EXTENSION_PATTERN.test(cleanPath);
17574
17614
  }
17575
17615
  function actorKey(event) {
@@ -17755,11 +17795,11 @@ function buildEventId2(event) {
17755
17795
  function normalizeWordpressTrafficEvent(event) {
17756
17796
  if (!event.observed_at) return null;
17757
17797
  if (typeof event.id !== "number" || !Number.isFinite(event.id)) return null;
17758
- const path15 = event.path?.trim();
17759
- if (!path15) return null;
17798
+ const path16 = event.path?.trim();
17799
+ if (!path16) return null;
17760
17800
  const queryString = trimOrNull(event.query_string);
17761
17801
  const host = trimOrNull(event.host);
17762
- const requestUrl = host ? `https://${host}${path15}${queryString ? `?${queryString}` : ""}` : `${path15}${queryString ? `?${queryString}` : ""}`;
17802
+ const requestUrl = host ? `https://${host}${path16}${queryString ? `?${queryString}` : ""}` : `${path16}${queryString ? `?${queryString}` : ""}`;
17763
17803
  return {
17764
17804
  sourceType: TrafficSourceTypes.wordpress,
17765
17805
  evidenceKind: TrafficEvidenceKinds["raw-request"],
@@ -17769,7 +17809,7 @@ function normalizeWordpressTrafficEvent(event) {
17769
17809
  method: trimOrNull(event.method),
17770
17810
  requestUrl,
17771
17811
  host,
17772
- path: path15,
17812
+ path: path16,
17773
17813
  queryString,
17774
17814
  status: typeof event.status === "number" && Number.isFinite(event.status) ? event.status : null,
17775
17815
  userAgent: trimOrNull(event.user_agent),
@@ -17933,15 +17973,15 @@ function stringLabels(input) {
17933
17973
  );
17934
17974
  }
17935
17975
  function normalizeVercelLogRow(row) {
17936
- const path15 = row.requestPath;
17937
- if (!path15) return null;
17976
+ const path16 = row.requestPath;
17977
+ if (!path16) return null;
17938
17978
  const observedAt = row.timestamp;
17939
17979
  if (!observedAt) return null;
17940
17980
  const requestId = row.requestId;
17941
17981
  if (!requestId) return null;
17942
17982
  const host = emptyToNull(row.domain);
17943
17983
  const queryString = serializeSearchParams(row.requestSearchParams);
17944
- const requestUrl = host ? `https://${host}${path15}${queryString ? `?${queryString}` : ""}` : null;
17984
+ const requestUrl = host ? `https://${host}${path16}${queryString ? `?${queryString}` : ""}` : null;
17945
17985
  return {
17946
17986
  sourceType: TrafficSourceTypes.vercel,
17947
17987
  evidenceKind: TrafficEvidenceKinds["raw-request"],
@@ -17951,7 +17991,7 @@ function normalizeVercelLogRow(row) {
17951
17991
  method: row.requestMethod ?? null,
17952
17992
  requestUrl,
17953
17993
  host,
17954
- path: path15,
17994
+ path: path16,
17955
17995
  queryString,
17956
17996
  status: resolveStatus(row),
17957
17997
  userAgent: emptyToNull(row.clientUserAgent),
@@ -18757,7 +18797,7 @@ async function trafficRoutes(app, opts) {
18757
18797
  crawlerEventsHourly.status
18758
18798
  ],
18759
18799
  set: {
18760
- hits: sql8`${crawlerEventsHourly.hits} + ${bucket.hits}`,
18800
+ hits: sql9`${crawlerEventsHourly.hits} + ${bucket.hits}`,
18761
18801
  sampledUserAgent: bucket.sampledUserAgent,
18762
18802
  updatedAt: finishedAt
18763
18803
  }
@@ -18792,7 +18832,7 @@ async function trafficRoutes(app, opts) {
18792
18832
  aiReferralEventsHourly.status
18793
18833
  ],
18794
18834
  set: {
18795
- sessionsOrHits: sql8`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
18835
+ sessionsOrHits: sql9`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
18796
18836
  updatedAt: finishedAt
18797
18837
  }
18798
18838
  }).run();
@@ -19044,19 +19084,19 @@ async function trafficRoutes(app, opts) {
19044
19084
  return response;
19045
19085
  });
19046
19086
  function buildSourceDetail(projectId, row, since) {
19047
- const crawlerTotals = app.db.select({ total: sql8`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19087
+ const crawlerTotals = app.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19048
19088
  and15(
19049
19089
  eq23(crawlerEventsHourly.sourceId, row.id),
19050
19090
  gte2(crawlerEventsHourly.tsHour, since)
19051
19091
  )
19052
19092
  ).get();
19053
- const aiTotals = app.db.select({ total: sql8`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19093
+ const aiTotals = app.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19054
19094
  and15(
19055
19095
  eq23(aiReferralEventsHourly.sourceId, row.id),
19056
19096
  gte2(aiReferralEventsHourly.tsHour, since)
19057
19097
  )
19058
19098
  ).get();
19059
- const sampleTotals = app.db.select({ total: sql8`COUNT(*)` }).from(rawEventSamples).where(
19099
+ const sampleTotals = app.db.select({ total: sql9`COUNT(*)` }).from(rawEventSamples).where(
19060
19100
  and15(
19061
19101
  eq23(rawEventSamples.sourceId, row.id),
19062
19102
  gte2(rawEventSamples.ts, since)
@@ -19158,7 +19198,7 @@ async function trafficRoutes(app, opts) {
19158
19198
  ];
19159
19199
  if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
19160
19200
  const crawlerWhere = and15(...crawlerFilters);
19161
- const total = app.db.select({ total: sql8`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
19201
+ const total = app.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
19162
19202
  crawlerTotal = Number(total?.total ?? 0);
19163
19203
  const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
19164
19204
  for (const r of rows) {
@@ -19183,7 +19223,7 @@ async function trafficRoutes(app, opts) {
19183
19223
  ];
19184
19224
  if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
19185
19225
  const aiWhere = and15(...aiFilters);
19186
- const total = app.db.select({ total: sql8`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
19226
+ const total = app.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
19187
19227
  aiReferralTotal = Number(total?.total ?? 0);
19188
19228
  const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
19189
19229
  for (const r of rows) {
@@ -19216,6 +19256,75 @@ async function trafficRoutes(app, opts) {
19216
19256
  });
19217
19257
  }
19218
19258
 
19259
+ // ../api-routes/src/doctor/checks/agent.ts
19260
+ import fs6 from "fs";
19261
+ import path7 from "path";
19262
+ var REQUIRED_SKILLS = ["canonry", "aero"];
19263
+ var skillsInstalledCheck = {
19264
+ id: "agent.skills.installed",
19265
+ category: CheckCategories.agent,
19266
+ scope: CheckScopes.global,
19267
+ title: "Agent skills installed (~/.claude/skills/)",
19268
+ run: () => {
19269
+ const home = process.env.HOME;
19270
+ if (!home) {
19271
+ return {
19272
+ status: CheckStatuses.skipped,
19273
+ code: "agent.skills.no-home",
19274
+ summary: "Cannot determine $HOME \u2014 skip skills filesystem check.",
19275
+ remediation: null
19276
+ };
19277
+ }
19278
+ const skillsBase = path7.join(home, ".claude", "skills");
19279
+ const installed = [];
19280
+ const missing = [];
19281
+ for (const name of REQUIRED_SKILLS) {
19282
+ const dir = path7.join(skillsBase, name);
19283
+ if (isInstalled(dir)) installed.push(name);
19284
+ else missing.push(name);
19285
+ }
19286
+ const details = {
19287
+ checkedPath: skillsBase,
19288
+ installed,
19289
+ missing
19290
+ };
19291
+ if (missing.length === 0) {
19292
+ return {
19293
+ status: CheckStatuses.ok,
19294
+ code: "agent.skills.installed",
19295
+ summary: `Both canonry and aero skills are installed in ${skillsBase}.`,
19296
+ remediation: null,
19297
+ details
19298
+ };
19299
+ }
19300
+ if (installed.length === 0) {
19301
+ return {
19302
+ status: CheckStatuses.warn,
19303
+ code: "agent.skills.not-installed",
19304
+ summary: "Agent skills are not installed for Claude Code on this machine. Claude sessions on this host will not auto-load canonry/aero reference docs.",
19305
+ remediation: "Run `canonry skills install --dir ~` (or `canonry skills install --user`) to install both skills to ~/.claude/skills/ and ~/.codex/skills/.",
19306
+ details
19307
+ };
19308
+ }
19309
+ return {
19310
+ status: CheckStatuses.warn,
19311
+ code: "agent.skills.partial",
19312
+ summary: `Only ${installed.length} of ${REQUIRED_SKILLS.length} agent skills are installed (${installed.join(", ")}); ${missing.join(", ")} missing.`,
19313
+ remediation: `Run \`canonry skills install ${missing.join(" ")} --dir ~\` to fill the gap.`,
19314
+ details
19315
+ };
19316
+ }
19317
+ };
19318
+ function isInstalled(dir) {
19319
+ try {
19320
+ if (!fs6.existsSync(dir)) return false;
19321
+ return fs6.existsSync(path7.join(dir, "SKILL.md"));
19322
+ } catch {
19323
+ return false;
19324
+ }
19325
+ }
19326
+ var AGENT_CHECKS = [skillsInstalledCheck];
19327
+
19219
19328
  // ../api-routes/src/doctor/checks/bing-auth.ts
19220
19329
  var BING_AUTH_CHECKS = [
19221
19330
  {
@@ -19837,7 +19946,7 @@ var providersConfiguredCheck = {
19837
19946
  var PROVIDERS_CHECKS = [providersConfiguredCheck];
19838
19947
 
19839
19948
  // ../api-routes/src/doctor/checks/traffic-source.ts
19840
- import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql9 } from "drizzle-orm";
19949
+ import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql10 } from "drizzle-orm";
19841
19950
  var RECENT_DATA_WARN_DAYS = 7;
19842
19951
  var RECENT_DATA_FAIL_DAYS = 30;
19843
19952
  function skippedNoProject2() {
@@ -19931,7 +20040,7 @@ var recentDataCheck = {
19931
20040
  const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
19932
20041
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
19933
20042
  const recentCrawlers = Number(
19934
- ctx.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20043
+ ctx.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19935
20044
  and16(
19936
20045
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
19937
20046
  gte3(crawlerEventsHourly.tsHour, warnCutoff)
@@ -19939,7 +20048,7 @@ var recentDataCheck = {
19939
20048
  ).get()?.total ?? 0
19940
20049
  );
19941
20050
  const recentReferrals = Number(
19942
- ctx.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20051
+ ctx.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19943
20052
  and16(
19944
20053
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
19945
20054
  gte3(aiReferralEventsHourly.tsHour, warnCutoff)
@@ -19955,7 +20064,7 @@ var recentDataCheck = {
19955
20064
  };
19956
20065
  }
19957
20066
  const olderCrawlers = Number(
19958
- ctx.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20067
+ ctx.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19959
20068
  and16(
19960
20069
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
19961
20070
  gte3(crawlerEventsHourly.tsHour, failCutoff)
@@ -19963,7 +20072,7 @@ var recentDataCheck = {
19963
20072
  ).get()?.total ?? 0
19964
20073
  );
19965
20074
  const olderReferrals = Number(
19966
- ctx.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20075
+ ctx.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19967
20076
  and16(
19968
20077
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
19969
20078
  gte3(aiReferralEventsHourly.tsHour, failCutoff)
@@ -20145,7 +20254,8 @@ var ALL_CHECKS = [
20145
20254
  ...BING_AUTH_CHECKS,
20146
20255
  ...GA_AUTH_CHECKS,
20147
20256
  ...PROVIDERS_CHECKS,
20148
- ...TRAFFIC_SOURCE_CHECKS
20257
+ ...TRAFFIC_SOURCE_CHECKS,
20258
+ ...AGENT_CHECKS
20149
20259
  ];
20150
20260
  var CHECK_BY_ID = Object.fromEntries(
20151
20261
  ALL_CHECKS.map((check) => [check.id, check])
@@ -22450,7 +22560,7 @@ var localAdapter = {
22450
22560
  };
22451
22561
 
22452
22562
  // ../provider-cdp/src/adapter.ts
22453
- import path8 from "path";
22563
+ import path9 from "path";
22454
22564
  import os4 from "os";
22455
22565
 
22456
22566
  // ../provider-cdp/src/connection.ts
@@ -22815,12 +22925,12 @@ function sleep2(ms) {
22815
22925
  }
22816
22926
 
22817
22927
  // ../provider-cdp/src/screenshot.ts
22818
- import fs6 from "fs";
22819
- import path7 from "path";
22928
+ import fs7 from "fs";
22929
+ import path8 from "path";
22820
22930
  async function captureElementScreenshot(client, selector, outputPath) {
22821
- const dir = path7.dirname(outputPath);
22822
- if (!fs6.existsSync(dir)) {
22823
- fs6.mkdirSync(dir, { recursive: true });
22931
+ const dir = path8.dirname(outputPath);
22932
+ if (!fs7.existsSync(dir)) {
22933
+ fs7.mkdirSync(dir, { recursive: true });
22824
22934
  }
22825
22935
  let clip;
22826
22936
  try {
@@ -22854,7 +22964,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
22854
22964
  }
22855
22965
  const { data } = await client.Page.captureScreenshot(screenshotParams);
22856
22966
  const buffer = Buffer.from(data, "base64");
22857
- fs6.writeFileSync(outputPath, buffer);
22967
+ fs7.writeFileSync(outputPath, buffer);
22858
22968
  return outputPath;
22859
22969
  }
22860
22970
 
@@ -22915,7 +23025,7 @@ function getConnection(config) {
22915
23025
  return conn;
22916
23026
  }
22917
23027
  function getScreenshotDir2() {
22918
- return path8.join(os4.homedir(), ".canonry", "screenshots");
23028
+ return path9.join(os4.homedir(), ".canonry", "screenshots");
22919
23029
  }
22920
23030
  var cdpChatgptAdapter = {
22921
23031
  name: "cdp:chatgpt",
@@ -22979,7 +23089,7 @@ var cdpChatgptAdapter = {
22979
23089
  const answerText = await target.extractAnswer(client);
22980
23090
  const groundingSources = await target.extractCitations(client);
22981
23091
  const screenshotId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
22982
- const screenshotPath = path8.join(getScreenshotDir2(), `${screenshotId}.png`);
23092
+ const screenshotPath = path9.join(getScreenshotDir2(), `${screenshotId}.png`);
22983
23093
  let capturedScreenshotPath;
22984
23094
  try {
22985
23095
  capturedScreenshotPath = await captureElementScreenshot(
@@ -23612,10 +23722,10 @@ function removeWordpressConnection(config, projectName) {
23612
23722
 
23613
23723
  // src/job-runner.ts
23614
23724
  import crypto24 from "crypto";
23615
- import fs7 from "fs";
23616
- import path9 from "path";
23725
+ import fs8 from "fs";
23726
+ import path10 from "path";
23617
23727
  import os5 from "os";
23618
- import { and as and18, eq as eq27, inArray as inArray9, sql as sql10 } from "drizzle-orm";
23728
+ import { and as and18, eq as eq27, inArray as inArray9, sql as sql11 } from "drizzle-orm";
23619
23729
 
23620
23730
  // src/run-telemetry.ts
23621
23731
  import crypto23 from "crypto";
@@ -24106,12 +24216,12 @@ var JobRunner = class {
24106
24216
  allBrandNames
24107
24217
  );
24108
24218
  let screenshotRelPath = null;
24109
- if (raw.screenshotPath && fs7.existsSync(raw.screenshotPath)) {
24219
+ if (raw.screenshotPath && fs8.existsSync(raw.screenshotPath)) {
24110
24220
  const snapshotId = crypto24.randomUUID();
24111
- const screenshotDir = path9.join(os5.homedir(), ".canonry", "screenshots", runId);
24112
- if (!fs7.existsSync(screenshotDir)) fs7.mkdirSync(screenshotDir, { recursive: true });
24113
- const destPath = path9.join(screenshotDir, `${snapshotId}.png`);
24114
- fs7.renameSync(raw.screenshotPath, destPath);
24221
+ const screenshotDir = path10.join(os5.homedir(), ".canonry", "screenshots", runId);
24222
+ if (!fs8.existsSync(screenshotDir)) fs8.mkdirSync(screenshotDir, { recursive: true });
24223
+ const destPath = path10.join(screenshotDir, `${snapshotId}.png`);
24224
+ fs8.renameSync(raw.screenshotPath, destPath);
24115
24225
  screenshotRelPath = `${runId}/${snapshotId}.png`;
24116
24226
  this.db.insert(querySnapshots).values({
24117
24227
  id: snapshotId,
@@ -24296,7 +24406,7 @@ var JobRunner = class {
24296
24406
  updatedAt: now
24297
24407
  }).onConflictDoUpdate({
24298
24408
  target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
24299
- set: { count: sql10`${usageCounters.count} + ${count}`, updatedAt: now }
24409
+ set: { count: sql11`${usageCounters.count} + ${count}`, updatedAt: now }
24300
24410
  }).run();
24301
24411
  }
24302
24412
  flushProviderUsage(projectId, providerDispatchCounts) {
@@ -24366,7 +24476,7 @@ function buildPhases(input) {
24366
24476
 
24367
24477
  // src/gsc-sync.ts
24368
24478
  import crypto25 from "crypto";
24369
- import { eq as eq28, and as and19, sql as sql11 } from "drizzle-orm";
24479
+ import { eq as eq28, and as and19, sql as sql12 } from "drizzle-orm";
24370
24480
  var log2 = createLogger("GscSync");
24371
24481
  function formatDate3(d) {
24372
24482
  return d.toISOString().split("T")[0];
@@ -24420,8 +24530,8 @@ async function executeGscSync(db, runId, projectId, opts) {
24420
24530
  db.delete(gscSearchData).where(
24421
24531
  and19(
24422
24532
  eq28(gscSearchData.projectId, projectId),
24423
- sql11`${gscSearchData.date} >= ${startDate}`,
24424
- sql11`${gscSearchData.date} <= ${endDate}`
24533
+ sql12`${gscSearchData.date} >= ${startDate}`,
24534
+ sql12`${gscSearchData.date} <= ${endDate}`
24425
24535
  )
24426
24536
  ).run();
24427
24537
  const batchSize = 500;
@@ -24956,8 +25066,8 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
24956
25066
 
24957
25067
  // src/commoncrawl-sync.ts
24958
25068
  import crypto28 from "crypto";
24959
- import path10 from "path";
24960
- import { and as and21, eq as eq31, sql as sql12 } from "drizzle-orm";
25069
+ import path11 from "path";
25070
+ import { and as and21, eq as eq31, sql as sql13 } from "drizzle-orm";
24961
25071
  var log6 = createLogger("CommonCrawlSync");
24962
25072
  var INSERT_CHUNK_SIZE = 1e4;
24963
25073
  function defaultDeps() {
@@ -24985,9 +25095,9 @@ async function executeReleaseSync(db, syncId, opts) {
24985
25095
  error: null
24986
25096
  }).where(eq31(ccReleaseSyncs.id, syncId)).run();
24987
25097
  const paths = ccReleasePaths(release);
24988
- const releaseCacheDir = path10.join(deps.cacheDir, release);
24989
- const vertexPath = path10.join(releaseCacheDir, paths.vertexFilename);
24990
- const edgesPath = path10.join(releaseCacheDir, paths.edgesFilename);
25098
+ const releaseCacheDir = path11.join(deps.cacheDir, release);
25099
+ const vertexPath = path11.join(releaseCacheDir, paths.vertexFilename);
25100
+ const edgesPath = path11.join(releaseCacheDir, paths.edgesFilename);
24991
25101
  const [vertex, edges] = await Promise.all([
24992
25102
  deps.downloadFile({ url: paths.vertexUrl, destPath: vertexPath }),
24993
25103
  deps.downloadFile({ url: paths.edgesUrl, destPath: edgesPath })
@@ -25147,7 +25257,7 @@ function computeSummary(rows) {
25147
25257
 
25148
25258
  // src/backlink-extract.ts
25149
25259
  import crypto29 from "crypto";
25150
- import fs8 from "fs";
25260
+ import fs9 from "fs";
25151
25261
  import { and as and22, desc as desc15, eq as eq32 } from "drizzle-orm";
25152
25262
  var log7 = createLogger("BacklinkExtract");
25153
25263
  function defaultDeps2() {
@@ -25176,7 +25286,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
25176
25286
  if (!sync.vertexPath || !sync.edgesPath) {
25177
25287
  throw new Error(`Release ${sync.release} is missing cached file paths`);
25178
25288
  }
25179
- if (!fs8.existsSync(sync.vertexPath) || !fs8.existsSync(sync.edgesPath)) {
25289
+ if (!fs9.existsSync(sync.vertexPath) || !fs9.existsSync(sync.edgesPath)) {
25180
25290
  throw new Error(
25181
25291
  `Cache for release ${sync.release} is missing from disk (expected at ${sync.vertexPath}). The sync record exists in the database, but the ~16 GB dump was deleted or never present on this machine. Re-sync this release from the Backlinks admin page to restore the cache.`
25182
25292
  );
@@ -25991,19 +26101,24 @@ function readStoredGroundingSources(rawResponse) {
25991
26101
  return result;
25992
26102
  }
25993
26103
  async function backfillInsightsCommand(project, opts) {
25994
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-WAJOEOJV.js");
26104
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-7AWRUNI2.js");
25995
26105
  const config = loadConfig();
25996
26106
  const db = createClient(config.database);
25997
26107
  migrate(db);
25998
26108
  const service = new IntelligenceService2(db);
25999
26109
  const isJson = opts?.format === "json";
26110
+ const isDryRun = opts?.dryRun === true;
26000
26111
  if (!isJson) {
26001
- process.stderr.write(`Backfilling insights for "${project}"...
26112
+ const scope = opts?.since ? ` (since ${opts.since})` : "";
26113
+ const mode = isDryRun ? " [DRY RUN \u2014 no writes]" : "";
26114
+ process.stderr.write(`Backfilling insights for "${project}"${scope}${mode}...
26002
26115
  `);
26003
26116
  }
26004
26117
  const result = service.backfill(project, {
26005
26118
  fromRunId: opts?.fromRun,
26006
- toRunId: opts?.toRun
26119
+ toRunId: opts?.toRun,
26120
+ since: opts?.since,
26121
+ dryRun: isDryRun
26007
26122
  }, (info) => {
26008
26123
  if (!isJson) {
26009
26124
  process.stderr.write(` [${info.index}/${info.total}] ${info.runId} \u2014 ${info.insights} insights
@@ -26016,15 +26131,23 @@ async function backfillInsightsCommand(project, opts) {
26016
26131
  skipped: result.skipped,
26017
26132
  totalInsights: result.totalInsights
26018
26133
  };
26134
+ if (result.dryRun) {
26135
+ output.dryRun = true;
26136
+ output.delta = result.delta;
26137
+ }
26019
26138
  if (isJson) {
26020
26139
  console.log(JSON.stringify(output, null, 2));
26021
26140
  return;
26022
26141
  }
26023
26142
  console.log(`
26024
- Backfill complete.`);
26143
+ Backfill ${isDryRun ? "preview" : "complete"}.`);
26025
26144
  console.log(` Processed: ${result.processed}`);
26026
26145
  console.log(` Skipped: ${result.skipped}`);
26027
26146
  console.log(` Insights: ${result.totalInsights}`);
26147
+ if (result.delta) {
26148
+ console.log(` Delta: -${result.delta.wouldDelete} existing +${result.delta.wouldCreate} new (net ${result.delta.netChange >= 0 ? "+" : ""}${result.delta.netChange})`);
26149
+ console.log(` No DB writes performed. Re-run without --dry-run to apply.`);
26150
+ }
26028
26151
  }
26029
26152
  function reparseProviderSnapshot(provider, rawResponse) {
26030
26153
  const envelope = parseJsonColumn(rawResponse, {});
@@ -26621,8 +26744,8 @@ import crypto33 from "crypto";
26621
26744
  import { eq as eq39 } from "drizzle-orm";
26622
26745
 
26623
26746
  // src/agent/session.ts
26624
- import fs11 from "fs";
26625
- import path13 from "path";
26747
+ import fs12 from "fs";
26748
+ import path14 from "path";
26626
26749
  import { Agent } from "@mariozechner/pi-agent-core";
26627
26750
  import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
26628
26751
 
@@ -26723,26 +26846,26 @@ function buildAgentProvidersResponse(config) {
26723
26846
  }
26724
26847
 
26725
26848
  // src/agent/skill-paths.ts
26726
- import fs9 from "fs";
26727
- import path11 from "path";
26849
+ import fs10 from "fs";
26850
+ import path12 from "path";
26728
26851
  import { fileURLToPath } from "url";
26729
26852
  function resolveAeroSkillDir(pkgDir) {
26730
- const here = pkgDir ?? path11.dirname(fileURLToPath(import.meta.url));
26853
+ const here = pkgDir ?? path12.dirname(fileURLToPath(import.meta.url));
26731
26854
  const candidates = [
26732
- path11.join(here, "../assets/agent-workspace/skills/aero"),
26733
- path11.join(here, "../../assets/agent-workspace/skills/aero"),
26734
- path11.join(here, "../../../../skills/aero")
26855
+ path12.join(here, "../assets/agent-workspace/skills/aero"),
26856
+ path12.join(here, "../../assets/agent-workspace/skills/aero"),
26857
+ path12.join(here, "../../../../skills/aero")
26735
26858
  ];
26736
26859
  for (const candidate of candidates) {
26737
- if (fs9.existsSync(path11.join(candidate, "SKILL.md"))) return candidate;
26860
+ if (fs10.existsSync(path12.join(candidate, "SKILL.md"))) return candidate;
26738
26861
  }
26739
26862
  throw new Error(`Aero skill not found. Searched:
26740
26863
  ${candidates.join("\n ")}`);
26741
26864
  }
26742
26865
 
26743
26866
  // src/agent/skill-tools.ts
26744
- import fs10 from "fs";
26745
- import path12 from "path";
26867
+ import fs11 from "fs";
26868
+ import path13 from "path";
26746
26869
  import { Type } from "@sinclair/typebox";
26747
26870
  var MAX_DOC_CHARS = 2e4;
26748
26871
  function textResult(details) {
@@ -26763,13 +26886,13 @@ function parseDescription(body) {
26763
26886
  return "(no description)";
26764
26887
  }
26765
26888
  function scanSkillDocs(skillDir) {
26766
- const refsDir = path12.join(skillDir ?? resolveAeroSkillDir(), "references");
26767
- if (!fs10.existsSync(refsDir)) return [];
26889
+ const refsDir = path13.join(skillDir ?? resolveAeroSkillDir(), "references");
26890
+ if (!fs11.existsSync(refsDir)) return [];
26768
26891
  const entries = [];
26769
- for (const file of fs10.readdirSync(refsDir)) {
26892
+ for (const file of fs11.readdirSync(refsDir)) {
26770
26893
  if (!file.endsWith(".md")) continue;
26771
- const filePath = path12.join(refsDir, file);
26772
- const body = fs10.readFileSync(filePath, "utf-8");
26894
+ const filePath = path13.join(refsDir, file);
26895
+ const body = fs11.readFileSync(filePath, "utf-8");
26773
26896
  entries.push({
26774
26897
  slug: file.replace(/\.md$/, ""),
26775
26898
  description: parseDescription(body),
@@ -26812,8 +26935,8 @@ function buildReadSkillDocTool() {
26812
26935
  availableSlugs: docs.map((d) => d.slug)
26813
26936
  });
26814
26937
  }
26815
- const filePath = path12.join(skillDir, "references", `${match.slug}.md`);
26816
- const content = fs10.readFileSync(filePath, "utf-8");
26938
+ const filePath = path13.join(skillDir, "references", `${match.slug}.md`);
26939
+ const content = fs11.readFileSync(filePath, "utf-8");
26817
26940
  if (content.length > MAX_DOC_CHARS) {
26818
26941
  return textResult({
26819
26942
  slug: match.slug,
@@ -26907,10 +27030,10 @@ function ensureBuiltinsRegistered() {
26907
27030
  }
26908
27031
  function loadAeroSystemPrompt(pkgDir) {
26909
27032
  const skillDir = resolveAeroSkillDir(pkgDir);
26910
- const skillBody = fs11.readFileSync(path13.join(skillDir, "SKILL.md"), "utf-8");
26911
- const soulPath = path13.join(skillDir, "soul.md");
26912
- if (!fs11.existsSync(soulPath)) return skillBody;
26913
- const soulBody = fs11.readFileSync(soulPath, "utf-8");
27033
+ const skillBody = fs12.readFileSync(path14.join(skillDir, "SKILL.md"), "utf-8");
27034
+ const soulPath = path14.join(skillDir, "soul.md");
27035
+ if (!fs12.existsSync(soulPath)) return skillBody;
27036
+ const soulBody = fs12.readFileSync(soulPath, "utf-8");
26914
27037
  return `${soulBody.trimEnd()}
26915
27038
 
26916
27039
  ---
@@ -26968,7 +27091,7 @@ function resolveSessionProviderAndModel(config, opts) {
26968
27091
 
26969
27092
  // src/agent/memory-store.ts
26970
27093
  import crypto32 from "crypto";
26971
- import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql13 } from "drizzle-orm";
27094
+ import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql14 } from "drizzle-orm";
26972
27095
  var COMPACTION_KEY_PREFIX = "compaction:";
26973
27096
  var COMPACTION_NOTES_PER_SESSION = 3;
26974
27097
  function rowToDto2(row) {
@@ -27054,7 +27177,7 @@ function writeCompactionNote(db, args) {
27054
27177
  ).orderBy(desc17(agentMemory.updatedAt)).all();
27055
27178
  const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
27056
27179
  if (stale.length > 0) {
27057
- tx.delete(agentMemory).where(sql13`${agentMemory.id} IN (${sql13.join(stale.map((s) => sql13`${s}`), sql13`, `)})`).run();
27180
+ tx.delete(agentMemory).where(sql14`${agentMemory.id} IN (${sql14.join(stale.map((s) => sql14`${s}`), sql14`, `)})`).run();
27058
27181
  }
27059
27182
  const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
27060
27183
  if (row) inserted = rowToDto2(row);
@@ -27812,13 +27935,13 @@ function extractHostname(domain) {
27812
27935
  function fetchWithPinnedAddress(target) {
27813
27936
  return new Promise((resolve) => {
27814
27937
  const port = target.url.port ? Number(target.url.port) : 443;
27815
- const path15 = target.url.pathname + target.url.search;
27938
+ const path16 = target.url.pathname + target.url.search;
27816
27939
  const req = https2.request(
27817
27940
  {
27818
27941
  hostname: target.address,
27819
27942
  family: target.family,
27820
27943
  port,
27821
- path: path15,
27944
+ path: path16,
27822
27945
  method: "GET",
27823
27946
  timeout: FETCH_TIMEOUT_MS2,
27824
27947
  servername: target.url.hostname,
@@ -28723,8 +28846,8 @@ async function createServer(opts) {
28723
28846
  );
28724
28847
  jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
28725
28848
  const snapshotService = new SnapshotService(registry);
28726
- const orphanedOpenClawDir = path14.join(os6.homedir(), ".openclaw-aero");
28727
- if (fs12.existsSync(orphanedOpenClawDir)) {
28849
+ const orphanedOpenClawDir = path15.join(os6.homedir(), ".openclaw-aero");
28850
+ if (fs13.existsSync(orphanedOpenClawDir)) {
28728
28851
  app.log.warn(
28729
28852
  { path: orphanedOpenClawDir },
28730
28853
  "OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
@@ -29439,10 +29562,10 @@ async function createServer(opts) {
29439
29562
  return snapshotService.createReport(input);
29440
29563
  }
29441
29564
  });
29442
- const dirname = path14.dirname(fileURLToPath2(import.meta.url));
29443
- const assetsDir = path14.join(dirname, "..", "assets");
29444
- if (fs12.existsSync(assetsDir)) {
29445
- const indexPath = path14.join(assetsDir, "index.html");
29565
+ const dirname = path15.dirname(fileURLToPath2(import.meta.url));
29566
+ const assetsDir = path15.join(dirname, "..", "assets");
29567
+ if (fs13.existsSync(assetsDir)) {
29568
+ const indexPath = path15.join(assetsDir, "index.html");
29446
29569
  const injectConfig = (html) => {
29447
29570
  const clientConfig = {};
29448
29571
  if (basePath) clientConfig.basePath = basePath;
@@ -29460,8 +29583,8 @@ async function createServer(opts) {
29460
29583
  index: false
29461
29584
  });
29462
29585
  const serveIndex = (_request, reply) => {
29463
- if (fs12.existsSync(indexPath)) {
29464
- const html = fs12.readFileSync(indexPath, "utf-8");
29586
+ if (fs13.existsSync(indexPath)) {
29587
+ const html = fs13.readFileSync(indexPath, "utf-8");
29465
29588
  return reply.type("text/html").send(injectConfig(html));
29466
29589
  }
29467
29590
  return reply.status(404).send({ error: "Dashboard not built" });
@@ -29481,8 +29604,8 @@ async function createServer(opts) {
29481
29604
  if (basePath && !url.startsWith(basePath)) {
29482
29605
  return reply.status(404).send({ error: "Not found", path: request.url });
29483
29606
  }
29484
- if (fs12.existsSync(indexPath)) {
29485
- const html = fs12.readFileSync(indexPath, "utf-8");
29607
+ if (fs13.existsSync(indexPath)) {
29608
+ const html = fs13.readFileSync(indexPath, "utf-8");
29486
29609
  return reply.type("text/html").send(injectConfig(html));
29487
29610
  }
29488
29611
  return reply.status(404).send({ error: "Not found" });