@ainyc/canonry 4.99.0 → 4.100.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.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +13 -1
- package/assets/assets/{BacklinksPage-CuWs7I1D.js → BacklinksPage-DWZdWEm3.js} +1 -1
- package/assets/assets/{ChartPrimitives-bm37tprp.js → ChartPrimitives-C-ky5206.js} +1 -1
- package/assets/assets/ProjectPage-CpbFpi6J.js +6 -0
- package/assets/assets/{RunRow-DzhQl87g.js → RunRow-Bj-D4kZe.js} +1 -1
- package/assets/assets/{RunsPage-wOaewhbF.js → RunsPage-GktitsAV.js} +1 -1
- package/assets/assets/{SettingsPage-CJdhhkkE.js → SettingsPage-AsFLFDS3.js} +1 -1
- package/assets/assets/{TrafficPage-BJBeN5Ph.js → TrafficPage-Cf3uCkxs.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-D3PmH0B1.js → TrafficSourceDetailPage-B5t7nf9C.js} +1 -1
- package/assets/assets/{arrow-left-BmITMI6O.js → arrow-left-CZFQuQO_.js} +1 -1
- package/assets/assets/{extract-error-message-BGwnyAfP.js → extract-error-message--bieZ2IX.js} +1 -1
- package/assets/assets/index-9hI0ODQU.css +1 -0
- package/assets/assets/index-Cb2h7KkD.js +210 -0
- package/assets/assets/{trash-2-Bd2mkP_w.js → trash-2-sKO0k75f.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-CEHD33NQ.js → chunk-74SOIAIY.js} +262 -119
- package/dist/{chunk-CWFTMFEU.js → chunk-ADLS64PG.js} +117 -3
- package/dist/{chunk-DJVUNURL.js → chunk-SNHYRSUF.js} +401 -40
- package/dist/{chunk-VQN74WWA.js → chunk-UVIHWF45.js} +27 -1
- package/dist/cli.js +110 -4
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-QHBXW4KI.js → intelligence-service-3CRNJLUZ.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +10 -10
- package/assets/assets/ProjectPage-BksYe8FP.js +0 -6
- package/assets/assets/index-BSX6r6ju.js +0 -210
- package/assets/assets/index-BsZNCwg8.css +0 -1
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
DiscoveryBuckets,
|
|
19
19
|
DiscoveryCompetitorTypes,
|
|
20
20
|
DiscoverySessionStatuses,
|
|
21
|
+
PortfolioChangeTypes,
|
|
21
22
|
RunKinds,
|
|
22
23
|
RunStatuses,
|
|
23
24
|
RunTriggers,
|
|
@@ -131,6 +132,7 @@ import {
|
|
|
131
132
|
formatIsoDate,
|
|
132
133
|
formatNumber,
|
|
133
134
|
formatRatio,
|
|
135
|
+
formatRunErrorOneLine,
|
|
134
136
|
formatWindowCountDelta,
|
|
135
137
|
ga4AiReferralHistoryEntrySchema,
|
|
136
138
|
ga4SessionHistoryEntrySchema,
|
|
@@ -184,6 +186,7 @@ import {
|
|
|
184
186
|
parseRunError,
|
|
185
187
|
parseWindow,
|
|
186
188
|
pickClusterRepresentative,
|
|
189
|
+
portfolioDtoSchema,
|
|
187
190
|
projectConfigSchema,
|
|
188
191
|
projectDtoSchema,
|
|
189
192
|
projectOverviewDtoSchema,
|
|
@@ -263,10 +266,10 @@ import {
|
|
|
263
266
|
wordpressSchemaDeployResultDtoSchema,
|
|
264
267
|
wordpressSchemaStatusResultDtoSchema,
|
|
265
268
|
wordpressStatusDtoSchema
|
|
266
|
-
} from "./chunk-
|
|
269
|
+
} from "./chunk-ADLS64PG.js";
|
|
267
270
|
|
|
268
271
|
// src/intelligence-service.ts
|
|
269
|
-
import { eq as eq37, desc as desc18, asc as
|
|
272
|
+
import { eq as eq37, desc as desc18, asc as asc6, and as and27, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
270
273
|
|
|
271
274
|
// ../db/src/client.ts
|
|
272
275
|
import { mkdirSync } from "fs";
|
|
@@ -4428,19 +4431,23 @@ function buildCitationScorecard(snapshots, queryLookup) {
|
|
|
4428
4431
|
answerMentioned: snap.answerMentioned ?? null,
|
|
4429
4432
|
model: snap.model
|
|
4430
4433
|
};
|
|
4431
|
-
const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
|
|
4434
|
+
const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, mentioned: 0, total: 0 };
|
|
4432
4435
|
counts2.total++;
|
|
4433
4436
|
if (snap.citationState === CitationStates.cited) counts2.cited++;
|
|
4437
|
+
if (snap.answerMentioned === true) counts2.mentioned++;
|
|
4434
4438
|
providerCounts.set(snap.provider, counts2);
|
|
4435
4439
|
}
|
|
4436
4440
|
const providerRates = providerList.map((provider) => {
|
|
4437
|
-
const counts2 = providerCounts.get(provider) ?? { cited: 0, total: 0 };
|
|
4441
|
+
const counts2 = providerCounts.get(provider) ?? { cited: 0, mentioned: 0, total: 0 };
|
|
4438
4442
|
const citationRate = counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0;
|
|
4443
|
+
const mentionRate = counts2.total > 0 ? Math.round(counts2.mentioned / counts2.total * 100) : 0;
|
|
4439
4444
|
return {
|
|
4440
4445
|
provider,
|
|
4441
4446
|
citedCount: counts2.cited,
|
|
4447
|
+
mentionedCount: counts2.mentioned,
|
|
4442
4448
|
totalCount: counts2.total,
|
|
4443
|
-
citationRate
|
|
4449
|
+
citationRate,
|
|
4450
|
+
mentionRate
|
|
4444
4451
|
};
|
|
4445
4452
|
});
|
|
4446
4453
|
return { queries: queryList, providers: providerList, matrix, providerRates };
|
|
@@ -5041,6 +5048,233 @@ function buildRunHistory(runs2, snapshotsByRunId, limit = DEFAULT_RUN_HISTORY_LI
|
|
|
5041
5048
|
});
|
|
5042
5049
|
}
|
|
5043
5050
|
|
|
5051
|
+
// ../intelligence/src/portfolio-change-feed.ts
|
|
5052
|
+
var PORTFOLIO_CHANGE_FEED_LIMIT = 12;
|
|
5053
|
+
var TONE_SEVERITY = {
|
|
5054
|
+
negative: 0,
|
|
5055
|
+
caution: 1,
|
|
5056
|
+
positive: 2,
|
|
5057
|
+
neutral: 3
|
|
5058
|
+
};
|
|
5059
|
+
function pluralizeQueries(n) {
|
|
5060
|
+
return n === 1 ? "query" : "queries";
|
|
5061
|
+
}
|
|
5062
|
+
function pluralizeAnswers(n) {
|
|
5063
|
+
return n === 1 ? "answer" : "answers";
|
|
5064
|
+
}
|
|
5065
|
+
function affectedDetail(queries2, max = 3) {
|
|
5066
|
+
const list = queries2 ?? [];
|
|
5067
|
+
if (list.length === 0) return "";
|
|
5068
|
+
const shown = list.slice(0, max).map((q) => `"${q}"`).join(", ");
|
|
5069
|
+
const remaining = list.length - max;
|
|
5070
|
+
return remaining > 0 ? `${shown} +${remaining} more` : shown;
|
|
5071
|
+
}
|
|
5072
|
+
function buildProjectChanges(input) {
|
|
5073
|
+
const {
|
|
5074
|
+
projectName,
|
|
5075
|
+
projectSlug,
|
|
5076
|
+
citationMovement,
|
|
5077
|
+
mentionMovement,
|
|
5078
|
+
movementComparison,
|
|
5079
|
+
attentionItems,
|
|
5080
|
+
latestRun
|
|
5081
|
+
} = input;
|
|
5082
|
+
const href = `/projects/${encodeURIComponent(projectSlug)}`;
|
|
5083
|
+
const rows = [];
|
|
5084
|
+
if (!latestRun) {
|
|
5085
|
+
rows.push({
|
|
5086
|
+
id: `${projectSlug}:${PortfolioChangeTypes["project-never-run"]}`,
|
|
5087
|
+
projectName,
|
|
5088
|
+
projectSlug,
|
|
5089
|
+
changeType: PortfolioChangeTypes["project-never-run"],
|
|
5090
|
+
tone: "neutral",
|
|
5091
|
+
title: `${projectName} has no completed sweep yet`,
|
|
5092
|
+
detail: `${input.trackedQueryCount} ${pluralizeQueries(input.trackedQueryCount)} tracked \u2014 run a sweep to start measuring.`,
|
|
5093
|
+
occurredAt: input.projectCreatedAt,
|
|
5094
|
+
href,
|
|
5095
|
+
actionLabel: "Run sweep",
|
|
5096
|
+
comparable: false
|
|
5097
|
+
});
|
|
5098
|
+
return rows;
|
|
5099
|
+
}
|
|
5100
|
+
const { runId, occurredAt } = latestRun;
|
|
5101
|
+
const comparable = movementComparison.comparable;
|
|
5102
|
+
if (latestRun.status === RunStatuses.failed) {
|
|
5103
|
+
rows.push({
|
|
5104
|
+
id: `${projectSlug}:${PortfolioChangeTypes["run-failed"]}:${runId}`,
|
|
5105
|
+
projectName,
|
|
5106
|
+
projectSlug,
|
|
5107
|
+
changeType: PortfolioChangeTypes["run-failed"],
|
|
5108
|
+
tone: "negative",
|
|
5109
|
+
title: `${projectName} sweep failed`,
|
|
5110
|
+
detail: latestRun.error ? formatRunErrorOneLine(latestRun.error) : "The latest visibility sweep failed.",
|
|
5111
|
+
occurredAt,
|
|
5112
|
+
href,
|
|
5113
|
+
actionLabel: "View run",
|
|
5114
|
+
comparable
|
|
5115
|
+
});
|
|
5116
|
+
}
|
|
5117
|
+
if (comparable) {
|
|
5118
|
+
if (citationMovement.lost > 0) {
|
|
5119
|
+
rows.push({
|
|
5120
|
+
id: `${projectSlug}:${PortfolioChangeTypes["citation-lost"]}:${runId}`,
|
|
5121
|
+
projectName,
|
|
5122
|
+
projectSlug,
|
|
5123
|
+
changeType: PortfolioChangeTypes["citation-lost"],
|
|
5124
|
+
tone: "negative",
|
|
5125
|
+
title: `${projectName} lost ${citationMovement.lost} cited ${pluralizeQueries(citationMovement.lost)}`,
|
|
5126
|
+
detail: affectedDetail(citationMovement.lostQueries),
|
|
5127
|
+
occurredAt,
|
|
5128
|
+
href,
|
|
5129
|
+
actionLabel: "Open project",
|
|
5130
|
+
comparable: true
|
|
5131
|
+
});
|
|
5132
|
+
}
|
|
5133
|
+
if (citationMovement.gained > 0) {
|
|
5134
|
+
rows.push({
|
|
5135
|
+
id: `${projectSlug}:${PortfolioChangeTypes["citation-gained"]}:${runId}`,
|
|
5136
|
+
projectName,
|
|
5137
|
+
projectSlug,
|
|
5138
|
+
changeType: PortfolioChangeTypes["citation-gained"],
|
|
5139
|
+
tone: "positive",
|
|
5140
|
+
title: `${projectName} gained ${citationMovement.gained} cited ${pluralizeQueries(citationMovement.gained)}`,
|
|
5141
|
+
detail: affectedDetail(citationMovement.gainedQueries),
|
|
5142
|
+
occurredAt,
|
|
5143
|
+
href,
|
|
5144
|
+
actionLabel: "Open project",
|
|
5145
|
+
comparable: true
|
|
5146
|
+
});
|
|
5147
|
+
}
|
|
5148
|
+
if (mentionMovement.lost > 0) {
|
|
5149
|
+
const queries2 = affectedDetail(mentionMovement.lostQueries);
|
|
5150
|
+
rows.push({
|
|
5151
|
+
id: `${projectSlug}:${PortfolioChangeTypes["mention-lost"]}:${runId}`,
|
|
5152
|
+
projectName,
|
|
5153
|
+
projectSlug,
|
|
5154
|
+
changeType: PortfolioChangeTypes["mention-lost"],
|
|
5155
|
+
tone: "negative",
|
|
5156
|
+
title: `${projectName} dropped from ${mentionMovement.lost} ${pluralizeAnswers(mentionMovement.lost)}`,
|
|
5157
|
+
detail: queries2 ? `No longer mentioned for ${queries2}` : "",
|
|
5158
|
+
occurredAt,
|
|
5159
|
+
href,
|
|
5160
|
+
actionLabel: "Open project",
|
|
5161
|
+
comparable: true
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
if (mentionMovement.gained > 0) {
|
|
5165
|
+
rows.push({
|
|
5166
|
+
id: `${projectSlug}:${PortfolioChangeTypes["mention-gained"]}:${runId}`,
|
|
5167
|
+
projectName,
|
|
5168
|
+
projectSlug,
|
|
5169
|
+
changeType: PortfolioChangeTypes["mention-gained"],
|
|
5170
|
+
tone: "positive",
|
|
5171
|
+
title: `${projectName} now mentioned in ${mentionMovement.gained} more ${pluralizeAnswers(mentionMovement.gained)}`,
|
|
5172
|
+
detail: affectedDetail(mentionMovement.gainedQueries),
|
|
5173
|
+
occurredAt,
|
|
5174
|
+
href,
|
|
5175
|
+
actionLabel: "Open project",
|
|
5176
|
+
comparable: true
|
|
5177
|
+
});
|
|
5178
|
+
}
|
|
5179
|
+
} else if (movementComparison.hasPreviousRun && movementComparison.querySetChanged) {
|
|
5180
|
+
rows.push({
|
|
5181
|
+
id: `${projectSlug}:${PortfolioChangeTypes["query-set-changed"]}:${runId}`,
|
|
5182
|
+
projectName,
|
|
5183
|
+
projectSlug,
|
|
5184
|
+
changeType: PortfolioChangeTypes["query-set-changed"],
|
|
5185
|
+
tone: "neutral",
|
|
5186
|
+
title: `${projectName} query set changed`,
|
|
5187
|
+
detail: `+${movementComparison.addedQueryCount} added \xB7 \u2212${movementComparison.removedQueryCount} removed since last sweep \u2014 movement compares the shared queries`,
|
|
5188
|
+
occurredAt,
|
|
5189
|
+
href,
|
|
5190
|
+
actionLabel: "Open project",
|
|
5191
|
+
comparable: false
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
for (const item of attentionItems) {
|
|
5195
|
+
if (item.id.startsWith("insight_")) {
|
|
5196
|
+
const isCritical = item.tone === "negative";
|
|
5197
|
+
const changeType = isCritical ? PortfolioChangeTypes["insight-critical"] : PortfolioChangeTypes["insight-high"];
|
|
5198
|
+
rows.push({
|
|
5199
|
+
id: `${projectSlug}:${changeType}:${item.id}`,
|
|
5200
|
+
projectName,
|
|
5201
|
+
projectSlug,
|
|
5202
|
+
changeType,
|
|
5203
|
+
tone: item.tone,
|
|
5204
|
+
title: item.title,
|
|
5205
|
+
detail: item.detail,
|
|
5206
|
+
occurredAt,
|
|
5207
|
+
href,
|
|
5208
|
+
actionLabel: isCritical ? "Critical" : "High",
|
|
5209
|
+
comparable
|
|
5210
|
+
});
|
|
5211
|
+
} else if (item.id === "stale_visibility") {
|
|
5212
|
+
rows.push({
|
|
5213
|
+
id: `${projectSlug}:${PortfolioChangeTypes["stale-visibility"]}:${runId}`,
|
|
5214
|
+
projectName,
|
|
5215
|
+
projectSlug,
|
|
5216
|
+
changeType: PortfolioChangeTypes["stale-visibility"],
|
|
5217
|
+
tone: "caution",
|
|
5218
|
+
title: `${projectName} visibility data is stale`,
|
|
5219
|
+
detail: "Integration syncs have run since the last sweep.",
|
|
5220
|
+
occurredAt,
|
|
5221
|
+
href,
|
|
5222
|
+
actionLabel: "Re-sweep",
|
|
5223
|
+
comparable
|
|
5224
|
+
});
|
|
5225
|
+
}
|
|
5226
|
+
}
|
|
5227
|
+
return rows;
|
|
5228
|
+
}
|
|
5229
|
+
function resolveEmptyState(projectCount, comparableProjectCount) {
|
|
5230
|
+
if (comparableProjectCount === 0) {
|
|
5231
|
+
return {
|
|
5232
|
+
kind: "awaiting-second-sweep",
|
|
5233
|
+
title: "No changes to compare yet",
|
|
5234
|
+
detail: "Visibility movement appears after a second sweep."
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
return {
|
|
5238
|
+
kind: "all-clear",
|
|
5239
|
+
title: "No changes since the last sweep",
|
|
5240
|
+
detail: `All ${projectCount} project${projectCount === 1 ? "" : "s"} held their mention and citation coverage.`
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
function buildPortfolioChangeFeed(inputs, _nowIso) {
|
|
5244
|
+
const all = [];
|
|
5245
|
+
let comparableProjectCount = 0;
|
|
5246
|
+
let firstSweepProjectCount = 0;
|
|
5247
|
+
for (const input of inputs) {
|
|
5248
|
+
if (input.latestRun) {
|
|
5249
|
+
if (input.movementComparison.comparable) comparableProjectCount += 1;
|
|
5250
|
+
if (!input.movementComparison.hasPreviousRun) firstSweepProjectCount += 1;
|
|
5251
|
+
}
|
|
5252
|
+
all.push(...buildProjectChanges(input));
|
|
5253
|
+
}
|
|
5254
|
+
const byId = /* @__PURE__ */ new Map();
|
|
5255
|
+
for (const row of all) {
|
|
5256
|
+
if (!byId.has(row.id)) byId.set(row.id, row);
|
|
5257
|
+
}
|
|
5258
|
+
const deduped = [...byId.values()];
|
|
5259
|
+
deduped.sort((a, b) => {
|
|
5260
|
+
if (a.occurredAt !== b.occurredAt) return a.occurredAt < b.occurredAt ? 1 : -1;
|
|
5261
|
+
const severity = TONE_SEVERITY[a.tone] - TONE_SEVERITY[b.tone];
|
|
5262
|
+
if (severity !== 0) return severity;
|
|
5263
|
+
if (a.projectName !== b.projectName) return a.projectName < b.projectName ? -1 : 1;
|
|
5264
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
5265
|
+
});
|
|
5266
|
+
const changeFeedTotal = deduped.length;
|
|
5267
|
+
const changeFeed = deduped.slice(0, PORTFOLIO_CHANGE_FEED_LIMIT);
|
|
5268
|
+
const feedEmptyState = changeFeedTotal === 0 ? resolveEmptyState(inputs.length, comparableProjectCount) : null;
|
|
5269
|
+
return {
|
|
5270
|
+
changeFeed,
|
|
5271
|
+
changeFeedTotal,
|
|
5272
|
+
feedEmptyState,
|
|
5273
|
+
comparableProjectCount,
|
|
5274
|
+
firstSweepProjectCount
|
|
5275
|
+
};
|
|
5276
|
+
}
|
|
5277
|
+
|
|
5044
5278
|
// ../intelligence/src/provider-trends.ts
|
|
5045
5279
|
function buildProviderTrends(runs2, snapshotsByRunId, limit = 12) {
|
|
5046
5280
|
const recent = [...runs2].sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, limit).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
@@ -10221,9 +10455,9 @@ function renderWhatsChanged(report, audience) {
|
|
|
10221
10455
|
);
|
|
10222
10456
|
}
|
|
10223
10457
|
const rateTiles = `<div class="metric-grid">
|
|
10224
|
-
${renderRateDeltaTile(isClient ? "AI
|
|
10225
|
-
${renderRateDeltaTile(isClient ? "AI
|
|
10226
|
-
${renderRateDeltaTile(isClient ? "Questions AI
|
|
10458
|
+
${renderRateDeltaTile(isClient ? "AI mentions your name" : "Citation rate", isClient ? w.mentionRate : w.citationRate, "%")}
|
|
10459
|
+
${renderRateDeltaTile(isClient ? "AI links to your website" : "Mention rate", isClient ? w.citationRate : w.mentionRate, "%")}
|
|
10460
|
+
${renderRateDeltaTile(isClient ? "Questions AI mentioned you in" : "Cited queries", isClient ? w.mentionedQueryCount : w.citedQueryCount, "count")}
|
|
10227
10461
|
${renderTrafficDeltaTile(isClient ? "Visitors from Google" : "GSC clicks", w.gscClicksDelta, isClient ? "visits" : "clicks", w.comparisonWindowDays)}
|
|
10228
10462
|
${renderTrafficDeltaTile(isClient ? "Visitors from AI tools" : "AI referral sessions", w.aiReferralsDelta, isClient ? "visits" : "sessions", w.comparisonWindowDays)}
|
|
10229
10463
|
</div>`;
|
|
@@ -11117,9 +11351,9 @@ function renderClientSummary(report) {
|
|
|
11117
11351
|
const s = report.executiveSummary;
|
|
11118
11352
|
const sc = report.citationScorecard;
|
|
11119
11353
|
const totalQ = s.totalQueryCount;
|
|
11120
|
-
const heroNumber = totalQ > 0 ? `${s.
|
|
11121
|
-
const heroSentence = totalQ > 0 ? `When customers asked AI ${totalQ} ${pluralize(totalQ, "question")} about your industry, AI
|
|
11122
|
-
const trend = clientTrendCopy(report.whatsChanged.
|
|
11354
|
+
const heroNumber = totalQ > 0 ? `${s.mentionRate}%` : "\u2014";
|
|
11355
|
+
const heroSentence = totalQ > 0 ? `When customers asked AI ${totalQ} ${pluralize(totalQ, "question")} about your industry, AI mentioned you in ${s.mentionedQueryCount} of ${totalQ === 1 ? "them" : "those answers"}.` : "No AI check has been run yet. Run a check to see how AI tools answer customer questions about your business.";
|
|
11356
|
+
const trend = clientTrendCopy(report.whatsChanged.mentionRate);
|
|
11123
11357
|
const heroTrend = trend ? `<p class="client-hero-trend tone-${trend.tone}"><span style="margin-right:6px;">${trend.arrow}</span>${escapeHtml(trend.text)}</p>` : "";
|
|
11124
11358
|
const hero = `<div class="client-hero">
|
|
11125
11359
|
<div class="client-hero-eyebrow">Overview</div>
|
|
@@ -11159,15 +11393,15 @@ function renderClientSummary(report) {
|
|
|
11159
11393
|
</ol>
|
|
11160
11394
|
</div>` : "";
|
|
11161
11395
|
const providerBars = sc.providerRates.length > 0 ? `<div class="client-card">
|
|
11162
|
-
<h3>How often each AI tool
|
|
11163
|
-
<p class="card-subtitle">Higher is better. Each bar shows the share of customer questions where the AI
|
|
11396
|
+
<h3>How often each AI tool mentions you</h3>
|
|
11397
|
+
<p class="card-subtitle">Higher is better. Each bar shows the share of customer questions where the AI named you in the answer.</p>
|
|
11164
11398
|
<div class="client-bar-list">
|
|
11165
11399
|
${sc.providerRates.map((r) => {
|
|
11166
|
-
const pct = Math.max(r.
|
|
11400
|
+
const pct = Math.max(r.mentionRate, 1.5);
|
|
11167
11401
|
return `<div class="client-bar-row">
|
|
11168
11402
|
<span class="bar-label">${escapeHtml(providerDisplayName(r.provider))}</span>
|
|
11169
11403
|
<div class="bar-track"><div class="bar-fill" style="width:${pct}%"></div></div>
|
|
11170
|
-
<span class="bar-value">${r.
|
|
11404
|
+
<span class="bar-value">${r.mentionRate}% <span class="bar-value-sub">(${r.mentionedCount}/${r.totalCount})</span></span>
|
|
11171
11405
|
</div>`;
|
|
11172
11406
|
}).join("")}
|
|
11173
11407
|
</div>
|
|
@@ -11953,9 +12187,10 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
|
|
|
11953
12187
|
considered++;
|
|
11954
12188
|
if (snap.citationState === CitationStates.cited) citedQueryIds.add(snap.queryId);
|
|
11955
12189
|
if (snap.answerMentioned) mentionedQueryIds.add(snap.queryId);
|
|
11956
|
-
const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
|
|
12190
|
+
const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, mentioned: 0, total: 0 };
|
|
11957
12191
|
counts2.total++;
|
|
11958
12192
|
if (snap.citationState === CitationStates.cited) counts2.cited++;
|
|
12193
|
+
if (snap.answerMentioned === true) counts2.mentioned++;
|
|
11959
12194
|
providerCounts.set(snap.provider, counts2);
|
|
11960
12195
|
}
|
|
11961
12196
|
if (considered === 0) continue;
|
|
@@ -11965,7 +12200,8 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
|
|
|
11965
12200
|
const mentionRate = totalQueries > 0 ? Math.round(mentionedQueryCount / totalQueries * 100) : 0;
|
|
11966
12201
|
const providerRates = [...providerCounts.entries()].map(([provider, counts2]) => ({
|
|
11967
12202
|
provider,
|
|
11968
|
-
citationRate: counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0
|
|
12203
|
+
citationRate: counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0,
|
|
12204
|
+
mentionRate: counts2.total > 0 ? Math.round(counts2.mentioned / counts2.total * 100) : 0
|
|
11969
12205
|
})).sort((a, b) => a.provider.localeCompare(b.provider));
|
|
11970
12206
|
points.push({
|
|
11971
12207
|
runId: run.id,
|
|
@@ -12345,23 +12581,17 @@ function buildReportActionPlan(input) {
|
|
|
12345
12581
|
}
|
|
12346
12582
|
return actions.sort((a, b) => a.priority - b.priority).slice(0, 10);
|
|
12347
12583
|
}
|
|
12348
|
-
function
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
return "Citation coverage declined versus the prior comparable check.";
|
|
12354
|
-
case "flat":
|
|
12355
|
-
return "Citation coverage is flat versus the prior comparable check.";
|
|
12356
|
-
case "unknown":
|
|
12357
|
-
return "There is not enough comparable run history yet to call a trend.";
|
|
12358
|
-
}
|
|
12584
|
+
function mentionTrendSentence(delta) {
|
|
12585
|
+
if (!delta) return "There is not enough comparable run history yet to call a mention trend.";
|
|
12586
|
+
if (delta.direction === "up") return "Mention coverage improved versus the prior comparable checks.";
|
|
12587
|
+
if (delta.direction === "down") return "Mention coverage declined versus the prior comparable checks.";
|
|
12588
|
+
return "Mention coverage is flat versus the prior comparable checks.";
|
|
12359
12589
|
}
|
|
12360
12590
|
function buildClientSummary(reportLike) {
|
|
12361
12591
|
const s = reportLike.executiveSummary;
|
|
12362
12592
|
const queryNoun = s.totalQueryCount === 1 ? "query" : "queries";
|
|
12363
|
-
const headline = s.totalQueryCount > 0 ? `${s.
|
|
12364
|
-
const overview = s.totalQueryCount > 0 ? `${reportLike.canonicalDomain} is
|
|
12593
|
+
const headline = s.totalQueryCount > 0 ? `${s.mentionedQueryCount} of ${s.totalQueryCount} tracked ${queryNoun} mention the brand in AI answers` : "No tracked queries have completed a check yet";
|
|
12594
|
+
const overview = s.totalQueryCount > 0 ? `${reportLike.canonicalDomain} is mentioned on ${s.mentionRate}% of tracked queries and cited on ${s.citationRate}% of tracked queries. ${mentionTrendSentence(reportLike.whatsChanged.mentionRate)}` : "At least one completed check is needed before this can summarize how the brand appears in AI answers.";
|
|
12365
12595
|
const confidenceNotes = [];
|
|
12366
12596
|
if (s.totalQueryCount === 0) {
|
|
12367
12597
|
confidenceNotes.push("Confidence is low until the first tracked query check completes.");
|
|
@@ -12496,6 +12726,11 @@ function buildWhatsChanged(input) {
|
|
|
12496
12726
|
...citedQueryCountSmoothed,
|
|
12497
12727
|
direction: rateDirection(citedQueryCountSmoothed.deltaAbs, COUNT_REAL_MOVEMENT_THRESHOLD)
|
|
12498
12728
|
} : null;
|
|
12729
|
+
const mentionedQueryCountSmoothed = smoothedRunDelta(citationsTrend, (p) => p.mentionedQueryCount);
|
|
12730
|
+
const mentionedQueryCount = enoughHistory && mentionedQueryCountSmoothed ? {
|
|
12731
|
+
...mentionedQueryCountSmoothed,
|
|
12732
|
+
direction: rateDirection(mentionedQueryCountSmoothed.deltaAbs, COUNT_REAL_MOVEMENT_THRESHOLD)
|
|
12733
|
+
} : null;
|
|
12499
12734
|
const providerMovements = [];
|
|
12500
12735
|
if (enoughHistory) {
|
|
12501
12736
|
const priorByProvider = new Map(prior.providerRates.map((p) => [p.provider, p.citationRate]));
|
|
@@ -12531,6 +12766,7 @@ function buildWhatsChanged(input) {
|
|
|
12531
12766
|
citationRate,
|
|
12532
12767
|
mentionRate,
|
|
12533
12768
|
citedQueryCount,
|
|
12769
|
+
mentionedQueryCount,
|
|
12534
12770
|
gscClicksDelta,
|
|
12535
12771
|
aiReferralsDelta,
|
|
12536
12772
|
comparisonWindowDays,
|
|
@@ -12694,6 +12930,7 @@ function buildProjectReport(db, projectName, periodDays) {
|
|
|
12694
12930
|
reportLocation,
|
|
12695
12931
|
executiveSummary,
|
|
12696
12932
|
citationsTrend,
|
|
12933
|
+
whatsChanged,
|
|
12697
12934
|
gsc: gscSection,
|
|
12698
12935
|
actionPlan
|
|
12699
12936
|
});
|
|
@@ -13100,8 +13337,9 @@ async function visibilityStatsRoutes(app) {
|
|
|
13100
13337
|
}
|
|
13101
13338
|
|
|
13102
13339
|
// ../api-routes/src/composites.ts
|
|
13103
|
-
import { eq as eq17, and as and12, desc as desc9, sql as sql7, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
|
|
13340
|
+
import { eq as eq17, and as and12, asc as asc2, desc as desc9, sql as sql7, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
|
|
13104
13341
|
var TOP_INSIGHT_LIMIT = 5;
|
|
13342
|
+
var PORTFOLIO_RECENT_RUNS_LIMIT = 8;
|
|
13105
13343
|
var SEARCH_HIT_HARD_LIMIT = 50;
|
|
13106
13344
|
var SEARCH_SNIPPET_RADIUS = 80;
|
|
13107
13345
|
var INTEGRATION_SYNC_KINDS = /* @__PURE__ */ new Set([
|
|
@@ -13118,6 +13356,14 @@ async function compositeRoutes(app) {
|
|
|
13118
13356
|
const project = resolveProject(app.db, request.params.name);
|
|
13119
13357
|
const filterLocation = (request.query.location ?? "").trim() || null;
|
|
13120
13358
|
const sinceIso = parseSinceFilter(request.query.since);
|
|
13359
|
+
return reply.send(computeProjectOverview(project, { filterLocation, sinceIso }));
|
|
13360
|
+
});
|
|
13361
|
+
app.get("/portfolio", async (_request, reply) => {
|
|
13362
|
+
return reply.send(buildPortfolioOverview());
|
|
13363
|
+
});
|
|
13364
|
+
function computeProjectOverview(project, opts = {}) {
|
|
13365
|
+
const filterLocation = opts.filterLocation ?? null;
|
|
13366
|
+
const sinceIso = opts.sinceIso ?? null;
|
|
13121
13367
|
const allRunsRaw = app.db.select().from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
|
|
13122
13368
|
const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
|
|
13123
13369
|
const totalRuns = allRuns.length;
|
|
@@ -13250,8 +13496,111 @@ async function compositeRoutes(app) {
|
|
|
13250
13496
|
dateRangeLabel: "All time",
|
|
13251
13497
|
contextLabel: `${project.country} / ${project.language.toUpperCase()}`
|
|
13252
13498
|
};
|
|
13253
|
-
return
|
|
13254
|
-
}
|
|
13499
|
+
return result;
|
|
13500
|
+
}
|
|
13501
|
+
function buildPortfolioOverview() {
|
|
13502
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13503
|
+
const projectRows = app.db.select().from(projects).orderBy(asc2(projects.name)).all();
|
|
13504
|
+
const trackedCountRows = app.db.select({ projectId: queries.projectId, total: sql7`COUNT(*)` }).from(queries).groupBy(queries.projectId).all();
|
|
13505
|
+
const trackedCountByProject = new Map(trackedCountRows.map((r) => [r.projectId, Number(r.total)]));
|
|
13506
|
+
const lastSweepRow = app.db.select({ createdAt: runs.createdAt, finishedAt: runs.finishedAt }).from(runs).where(and12(
|
|
13507
|
+
eq17(runs.kind, RunKinds["answer-visibility"]),
|
|
13508
|
+
notProbeRun(),
|
|
13509
|
+
inArray9(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
13510
|
+
)).orderBy(desc9(runs.createdAt), desc9(runs.id)).limit(1).get();
|
|
13511
|
+
const lastSweepAt = lastSweepRow ? lastSweepRow.finishedAt ?? lastSweepRow.createdAt : null;
|
|
13512
|
+
const feedInputs = [];
|
|
13513
|
+
const projectRowDtos = [];
|
|
13514
|
+
const runHistoryById = /* @__PURE__ */ new Map();
|
|
13515
|
+
for (const projectRow of projectRows) {
|
|
13516
|
+
const overview = computeProjectOverview(projectRow);
|
|
13517
|
+
for (const point of overview.runHistory) runHistoryById.set(point.runId, point);
|
|
13518
|
+
const latestVisRunRow = app.db.select().from(runs).where(and12(
|
|
13519
|
+
eq17(runs.projectId, projectRow.id),
|
|
13520
|
+
eq17(runs.kind, RunKinds["answer-visibility"]),
|
|
13521
|
+
notProbeRun()
|
|
13522
|
+
)).orderBy(desc9(runs.createdAt), desc9(runs.id)).limit(1).get();
|
|
13523
|
+
const projectName = projectRow.displayName || projectRow.name;
|
|
13524
|
+
feedInputs.push({
|
|
13525
|
+
projectName,
|
|
13526
|
+
projectSlug: projectRow.name,
|
|
13527
|
+
citationMovement: overview.citationMovement,
|
|
13528
|
+
mentionMovement: overview.mentionMovement,
|
|
13529
|
+
movementComparison: overview.movementComparison,
|
|
13530
|
+
attentionItems: overview.attentionItems,
|
|
13531
|
+
latestRun: latestVisRunRow ? {
|
|
13532
|
+
runId: latestVisRunRow.id,
|
|
13533
|
+
status: latestVisRunRow.status,
|
|
13534
|
+
occurredAt: latestVisRunRow.finishedAt ?? latestVisRunRow.createdAt,
|
|
13535
|
+
error: parseRunError(latestVisRunRow.error)
|
|
13536
|
+
} : null,
|
|
13537
|
+
trackedQueryCount: trackedCountByProject.get(projectRow.id) ?? 0,
|
|
13538
|
+
projectCreatedAt: projectRow.createdAt
|
|
13539
|
+
});
|
|
13540
|
+
const mention = overview.scores.mention;
|
|
13541
|
+
projectRowDtos.push({
|
|
13542
|
+
projectSlug: projectRow.name,
|
|
13543
|
+
projectName,
|
|
13544
|
+
canonicalDomain: projectRow.canonicalDomain,
|
|
13545
|
+
mentionScore: mention.progress ?? 0,
|
|
13546
|
+
mentionTone: mention.tone,
|
|
13547
|
+
mentionedOfTotal: { mentioned: overview.queryCounts.mentionedQueries, total: overview.queryCounts.totalQueries },
|
|
13548
|
+
citedOfTotal: { cited: overview.queryCounts.citedQueries, total: overview.queryCounts.totalQueries },
|
|
13549
|
+
mentionDelta: {
|
|
13550
|
+
gained: overview.mentionMovement.gained,
|
|
13551
|
+
lost: overview.mentionMovement.lost,
|
|
13552
|
+
comparable: overview.movementComparison.comparable
|
|
13553
|
+
},
|
|
13554
|
+
citationDelta: {
|
|
13555
|
+
gained: overview.citationMovement.gained,
|
|
13556
|
+
lost: overview.citationMovement.lost,
|
|
13557
|
+
comparable: overview.movementComparison.comparable
|
|
13558
|
+
},
|
|
13559
|
+
competitorPressureLabel: overview.scores.competitorPressure.value,
|
|
13560
|
+
mentionTrend: mention.trend,
|
|
13561
|
+
lastRunAt: latestVisRunRow ? latestVisRunRow.finishedAt ?? latestVisRunRow.createdAt : null,
|
|
13562
|
+
hasEverRun: latestVisRunRow != null
|
|
13563
|
+
});
|
|
13564
|
+
}
|
|
13565
|
+
const feed = buildPortfolioChangeFeed(feedInputs, generatedAt);
|
|
13566
|
+
const projectById = new Map(projectRows.map((p) => [p.id, p]));
|
|
13567
|
+
const recentRunRows = app.db.select().from(runs).where(and12(eq17(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).limit(PORTFOLIO_RECENT_RUNS_LIMIT).all();
|
|
13568
|
+
const recentRuns = [];
|
|
13569
|
+
for (const run of recentRunRows) {
|
|
13570
|
+
const projectRow = projectById.get(run.projectId);
|
|
13571
|
+
if (!projectRow) continue;
|
|
13572
|
+
const point = runHistoryById.get(run.id);
|
|
13573
|
+
const countable = run.status === RunStatuses.completed || run.status === RunStatuses.partial;
|
|
13574
|
+
const error = parseRunError(run.error);
|
|
13575
|
+
recentRuns.push({
|
|
13576
|
+
runId: run.id,
|
|
13577
|
+
projectName: projectRow.displayName || projectRow.name,
|
|
13578
|
+
projectSlug: projectRow.name,
|
|
13579
|
+
kindLabel: "Answer visibility sweep",
|
|
13580
|
+
status: run.status,
|
|
13581
|
+
createdAt: run.createdAt,
|
|
13582
|
+
startedAt: run.startedAt ?? null,
|
|
13583
|
+
finishedAt: run.finishedAt ?? null,
|
|
13584
|
+
durationMs: run.startedAt && run.finishedAt ? new Date(run.finishedAt).getTime() - new Date(run.startedAt).getTime() : null,
|
|
13585
|
+
mentionedCount: countable && point ? point.mentionedCount : null,
|
|
13586
|
+
citedCount: countable && point ? point.citedCount : null,
|
|
13587
|
+
totalCount: countable && point ? point.totalCount : null,
|
|
13588
|
+
errorSummary: error ? formatRunErrorOneLine(error) : null
|
|
13589
|
+
});
|
|
13590
|
+
}
|
|
13591
|
+
return {
|
|
13592
|
+
generatedAt,
|
|
13593
|
+
lastSweepAt,
|
|
13594
|
+
projectCount: projectRows.length,
|
|
13595
|
+
comparableProjectCount: feed.comparableProjectCount,
|
|
13596
|
+
firstSweepProjectCount: feed.firstSweepProjectCount,
|
|
13597
|
+
changeFeed: feed.changeFeed,
|
|
13598
|
+
changeFeedTotal: feed.changeFeedTotal,
|
|
13599
|
+
feedEmptyState: feed.feedEmptyState,
|
|
13600
|
+
recentRuns,
|
|
13601
|
+
projects: projectRowDtos
|
|
13602
|
+
};
|
|
13603
|
+
}
|
|
13255
13604
|
app.get("/projects/:name/search", async (request, reply) => {
|
|
13256
13605
|
const project = resolveProject(app.db, request.params.name);
|
|
13257
13606
|
const rawQuery = (request.query.q ?? "").trim();
|
|
@@ -13878,6 +14227,7 @@ var SCHEMA_TABLE = {
|
|
|
13878
14227
|
LatestProjectRunDto: latestProjectRunDtoSchema,
|
|
13879
14228
|
LocationContext: locationContextSchema,
|
|
13880
14229
|
NotificationDto: notificationDtoSchema,
|
|
14230
|
+
PortfolioDto: portfolioDtoSchema,
|
|
13881
14231
|
ProjectDto: projectDtoSchema,
|
|
13882
14232
|
ProjectOverviewDto: projectOverviewDtoSchema,
|
|
13883
14233
|
ProjectReportDto: projectReportDtoSchema,
|
|
@@ -17132,6 +17482,17 @@ var routeCatalog = [
|
|
|
17132
17482
|
404: errorResponse("Project not found.")
|
|
17133
17483
|
}
|
|
17134
17484
|
},
|
|
17485
|
+
{
|
|
17486
|
+
method: "get",
|
|
17487
|
+
path: "/api/v1/portfolio",
|
|
17488
|
+
summary: "Cross-project portfolio: what changed, recent runs, project state",
|
|
17489
|
+
description: 'One call for the dashboard Portfolio page and `canonry portfolio`. Returns a server-ordered "what changed" feed (citation/mention gains and losses over each project\'s comparable basket, failed sweeps, critical/high insight echoes, stale-visibility, query-set changes, and never-run projects \u2014 recency then severity), a timestamped recent-runs log with independent mention and cited result counts, and a per-project state table. Probe runs are excluded. `generatedAt` is the freshness anchor for every relative timestamp.',
|
|
17490
|
+
tags: ["intelligence"],
|
|
17491
|
+
parameters: [],
|
|
17492
|
+
responses: {
|
|
17493
|
+
200: jsonResponse("Portfolio overview returned.", "PortfolioDto")
|
|
17494
|
+
}
|
|
17495
|
+
},
|
|
17135
17496
|
{
|
|
17136
17497
|
method: "get",
|
|
17137
17498
|
path: "/api/v1/projects/{name}/overview",
|
|
@@ -21527,7 +21888,7 @@ async function googleRoutes(app, opts) {
|
|
|
21527
21888
|
|
|
21528
21889
|
// ../api-routes/src/ads.ts
|
|
21529
21890
|
import crypto19 from "crypto";
|
|
21530
|
-
import { eq as eq22, and as and15, asc as
|
|
21891
|
+
import { eq as eq22, and as and15, asc as asc3, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
|
|
21531
21892
|
function statusDto(row) {
|
|
21532
21893
|
if (!row) return { connected: false };
|
|
21533
21894
|
return {
|
|
@@ -21728,7 +22089,7 @@ async function adsRoutes(app, opts) {
|
|
|
21728
22089
|
if (entityId) conditions.push(eq22(adsInsightsDaily.entityId, entityId));
|
|
21729
22090
|
if (from) conditions.push(gte3(adsInsightsDaily.date, from));
|
|
21730
22091
|
if (to) conditions.push(lte2(adsInsightsDaily.date, to));
|
|
21731
|
-
const rows = app.db.select().from(adsInsightsDaily).where(and15(...conditions)).orderBy(
|
|
22092
|
+
const rows = app.db.select().from(adsInsightsDaily).where(and15(...conditions)).orderBy(asc3(adsInsightsDaily.date)).all();
|
|
21732
22093
|
const dtoRows = rows.map((row) => ({
|
|
21733
22094
|
level: row.level,
|
|
21734
22095
|
entityId: row.entityId,
|
|
@@ -25137,7 +25498,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
25137
25498
|
|
|
25138
25499
|
// ../api-routes/src/backlinks.ts
|
|
25139
25500
|
import crypto23 from "crypto";
|
|
25140
|
-
import { and as and20, asc as
|
|
25501
|
+
import { and as and20, asc as asc4, desc as desc14, eq as eq26, sql as sql10 } from "drizzle-orm";
|
|
25141
25502
|
|
|
25142
25503
|
// ../integration-commoncrawl/src/constants.ts
|
|
25143
25504
|
import os2 from "os";
|
|
@@ -25896,7 +26257,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
25896
26257
|
async (request, reply) => {
|
|
25897
26258
|
const project = resolveProject(app.db, request.params.name);
|
|
25898
26259
|
const source = parseSourceParam(request.query.source);
|
|
25899
|
-
const rows = app.db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, project.id), eq26(backlinkSummaries.source, source))).orderBy(
|
|
26260
|
+
const rows = app.db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, project.id), eq26(backlinkSummaries.source, source))).orderBy(asc4(backlinkSummaries.queriedAt)).all();
|
|
25900
26261
|
const response = rows.map((r) => ({
|
|
25901
26262
|
release: r.release,
|
|
25902
26263
|
totalLinkingDomains: r.totalLinkingDomains,
|
|
@@ -33861,7 +34222,7 @@ function dedupeStrings(input) {
|
|
|
33861
34222
|
|
|
33862
34223
|
// ../api-routes/src/technical-aeo.ts
|
|
33863
34224
|
import crypto29 from "crypto";
|
|
33864
|
-
import { and as and26, asc as
|
|
34225
|
+
import { and as and26, asc as asc5, count, desc as desc17, eq as eq36, inArray as inArray13 } from "drizzle-orm";
|
|
33865
34226
|
var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
|
|
33866
34227
|
function emptyScore(projectName) {
|
|
33867
34228
|
return {
|
|
@@ -33945,7 +34306,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
33945
34306
|
const total = totalRow?.value ?? 0;
|
|
33946
34307
|
const limit = parsePositiveInt(request.query.limit, 100, 500);
|
|
33947
34308
|
const offset = parsePositiveInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER);
|
|
33948
|
-
const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ?
|
|
34309
|
+
const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ? asc5(siteAuditPages.url) : asc5(siteAuditPages.overallScore);
|
|
33949
34310
|
const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
|
|
33950
34311
|
const pages = rows.map((row) => ({
|
|
33951
34312
|
url: row.url,
|
|
@@ -34880,7 +35241,7 @@ var IntelligenceService = class {
|
|
|
34880
35241
|
// Backfill must not replay probe runs as if they were real sweeps.
|
|
34881
35242
|
ne5(runs.trigger, RunTriggers.probe)
|
|
34882
35243
|
)
|
|
34883
|
-
).orderBy(
|
|
35244
|
+
).orderBy(asc6(runs.finishedAt)).all();
|
|
34884
35245
|
let startIdx = 0;
|
|
34885
35246
|
let endIdx = allRuns.length;
|
|
34886
35247
|
if (opts?.fromRunId) {
|