@ainyc/canonry 4.86.0 → 4.88.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/server-side-traffic.md +19 -0
- package/assets/assets/{BacklinksPage-BNrvc-gV.js → BacklinksPage-Dpx6ylmU.js} +1 -1
- package/assets/assets/{ChartPrimitives-BlIkdUdy.js → ChartPrimitives-CG5EGu62.js} +1 -1
- package/assets/assets/ProjectPage-B_xnYE7W.js +6 -0
- package/assets/assets/{RunRow-CAPnKzi7.js → RunRow-C0V4vf-u.js} +1 -1
- package/assets/assets/{RunsPage-idnuzKBn.js → RunsPage-9gWTjteG.js} +1 -1
- package/assets/assets/{SettingsPage-Bka67uJq.js → SettingsPage-kBwz4Glg.js} +1 -1
- package/assets/assets/{TrafficPage-C_o-rA5o.js → TrafficPage-D4adjs8S.js} +1 -1
- package/assets/assets/TrafficSourceDetailPage-CmURRHaU.js +1 -0
- package/assets/assets/{arrow-left-B-JfzARi.js → arrow-left-DJ0-oFbd.js} +1 -1
- package/assets/assets/{extract-error-message-BhPbjIX6.js → extract-error-message-DfBzETgi.js} +1 -1
- package/assets/assets/{index-uPSrDA8e.js → index-BeuGfMy8.js} +107 -107
- package/assets/assets/index-ClkRAeHL.css +1 -0
- package/assets/assets/{trash-2-BbRvn40h.js → trash-2-Bjf6mCqw.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-SELXBOAP.js → chunk-2VEFNAQ3.js} +4 -4
- package/dist/{chunk-23HGQV22.js → chunk-JZ67YCHT.js} +365 -0
- package/dist/{chunk-LLJPZKHG.js → chunk-RH3QYFX3.js} +2 -2
- package/dist/{chunk-DLBQU3VG.js → chunk-WMZJO56S.js} +213 -57
- package/dist/cli.js +23 -12
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ZHUJKZRO.js → intelligence-service-WTEEV46F.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
- package/assets/assets/ProjectPage-CAyx_xNr.js +0 -6
- package/assets/assets/TrafficSourceDetailPage-D_jvoSTV.js +0 -1
- package/assets/assets/index-BgWgJE7S.css +0 -1
|
@@ -86,6 +86,7 @@ import {
|
|
|
86
86
|
classifyCitedSurface,
|
|
87
87
|
classifySkillFile,
|
|
88
88
|
classifySurfaceFromCategory,
|
|
89
|
+
classifyTrafficPath,
|
|
89
90
|
clusterByCosine,
|
|
90
91
|
coerceSkillManifest,
|
|
91
92
|
competitorBatchRequestSchema,
|
|
@@ -170,6 +171,7 @@ import {
|
|
|
170
171
|
missingDependency,
|
|
171
172
|
normalizeProjectAliases,
|
|
172
173
|
normalizeProjectDomain,
|
|
174
|
+
normalizeQueryText,
|
|
173
175
|
normalizeUrlPath,
|
|
174
176
|
notFound,
|
|
175
177
|
notImplemented,
|
|
@@ -180,6 +182,7 @@ import {
|
|
|
180
182
|
pickClusterRepresentative,
|
|
181
183
|
projectConfigSchema,
|
|
182
184
|
projectDtoSchema,
|
|
185
|
+
projectOverviewDtoSchema,
|
|
183
186
|
projectReportDtoSchema,
|
|
184
187
|
projectUpsertRequestSchema,
|
|
185
188
|
providerError,
|
|
@@ -208,6 +211,7 @@ import {
|
|
|
208
211
|
scheduleDtoSchema,
|
|
209
212
|
scheduleUpsertRequestSchema,
|
|
210
213
|
seedCollapseWarning,
|
|
214
|
+
segmentCrawlerHits,
|
|
211
215
|
serializeRunError,
|
|
212
216
|
settingsDtoSchema,
|
|
213
217
|
siteAuditPagesResponseSchema,
|
|
@@ -220,6 +224,7 @@ import {
|
|
|
220
224
|
snapshotReportSchema,
|
|
221
225
|
snapshotRequestSchema,
|
|
222
226
|
sourceBreakdownDtoSchema,
|
|
227
|
+
sumInfraHits,
|
|
223
228
|
summarizeCheckResults,
|
|
224
229
|
surfaceClassFromCompetitorType,
|
|
225
230
|
surfaceClassLabel,
|
|
@@ -253,7 +258,7 @@ import {
|
|
|
253
258
|
wordpressSchemaDeployResultDtoSchema,
|
|
254
259
|
wordpressSchemaStatusResultDtoSchema,
|
|
255
260
|
wordpressStatusDtoSchema
|
|
256
|
-
} from "./chunk-
|
|
261
|
+
} from "./chunk-JZ67YCHT.js";
|
|
257
262
|
|
|
258
263
|
// src/intelligence-service.ts
|
|
259
264
|
import { eq as eq37, desc as desc18, asc as asc5, and as and27, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
@@ -4532,35 +4537,101 @@ function buildAiSourceOrigin(snapshots, projectDomains, competitorDomains, topDo
|
|
|
4532
4537
|
|
|
4533
4538
|
// ../intelligence/src/movement-summary.ts
|
|
4534
4539
|
function buildMovementSummary(currentSnapshots, previousSnapshots, options = {}) {
|
|
4540
|
+
return buildSignalMovementSummary(
|
|
4541
|
+
currentSnapshots,
|
|
4542
|
+
previousSnapshots,
|
|
4543
|
+
(snapshot) => snapshot.citationState === CitationStates.cited,
|
|
4544
|
+
options
|
|
4545
|
+
);
|
|
4546
|
+
}
|
|
4547
|
+
function buildCitationMovementSummary(currentSnapshots, previousSnapshots, options = {}) {
|
|
4548
|
+
return buildMovementSummary(currentSnapshots, previousSnapshots, options);
|
|
4549
|
+
}
|
|
4550
|
+
function buildMentionMovementSummary(currentSnapshots, previousSnapshots, options = {}) {
|
|
4551
|
+
return buildSignalMovementSummary(
|
|
4552
|
+
currentSnapshots,
|
|
4553
|
+
previousSnapshots,
|
|
4554
|
+
(snapshot) => snapshot.answerMentioned === true,
|
|
4555
|
+
options
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
function buildMovementComparison(currentSnapshots, previousSnapshots, options = {}) {
|
|
4559
|
+
const currentIds = collectQueryIds(currentSnapshots);
|
|
4560
|
+
const previousIds = collectQueryIds(previousSnapshots);
|
|
4561
|
+
const hasPreviousRun = previousSnapshots.length > 0;
|
|
4562
|
+
if (!hasPreviousRun) {
|
|
4563
|
+
return {
|
|
4564
|
+
hasPreviousRun: false,
|
|
4565
|
+
comparable: false,
|
|
4566
|
+
querySetChanged: false,
|
|
4567
|
+
previousRunAt: null,
|
|
4568
|
+
currentQueryCount: currentIds.size,
|
|
4569
|
+
previousQueryCount: 0,
|
|
4570
|
+
comparableQueryCount: 0,
|
|
4571
|
+
addedQueryCount: 0,
|
|
4572
|
+
removedQueryCount: 0,
|
|
4573
|
+
addedQueries: [],
|
|
4574
|
+
removedQueries: []
|
|
4575
|
+
};
|
|
4576
|
+
}
|
|
4577
|
+
const comparableIds = intersection(currentIds, previousIds);
|
|
4578
|
+
const addedIds = difference(currentIds, previousIds);
|
|
4579
|
+
const removedIds = difference(previousIds, currentIds);
|
|
4580
|
+
const querySetChanged = addedIds.size > 0 || removedIds.size > 0;
|
|
4581
|
+
return {
|
|
4582
|
+
hasPreviousRun: true,
|
|
4583
|
+
comparable: !querySetChanged && currentIds.size > 0,
|
|
4584
|
+
querySetChanged,
|
|
4585
|
+
previousRunAt: options.previousRunAt ?? null,
|
|
4586
|
+
currentQueryCount: currentIds.size,
|
|
4587
|
+
previousQueryCount: previousIds.size,
|
|
4588
|
+
comparableQueryCount: comparableIds.size,
|
|
4589
|
+
addedQueryCount: addedIds.size,
|
|
4590
|
+
removedQueryCount: removedIds.size,
|
|
4591
|
+
addedQueries: resolveQueryTexts(addedIds, options.queryLookup),
|
|
4592
|
+
removedQueries: resolveQueryTexts(removedIds, options.queryLookup)
|
|
4593
|
+
};
|
|
4594
|
+
}
|
|
4595
|
+
function buildSignalMovementSummary(currentSnapshots, previousSnapshots, isActive, options) {
|
|
4535
4596
|
if (previousSnapshots.length === 0) {
|
|
4536
|
-
const
|
|
4537
|
-
const citedCount = citedIds.size;
|
|
4538
|
-
const tone2 = citedCount > 0 ? "positive" : "neutral";
|
|
4597
|
+
const activeIds = collectActiveQueryIds(currentSnapshots, isActive);
|
|
4539
4598
|
return withQueryLists(
|
|
4540
|
-
{
|
|
4541
|
-
|
|
4599
|
+
{
|
|
4600
|
+
gained: activeIds.size,
|
|
4601
|
+
lost: 0,
|
|
4602
|
+
tone: activeIds.size > 0 ? "positive" : "neutral",
|
|
4603
|
+
hasPreviousRun: false
|
|
4604
|
+
},
|
|
4605
|
+
activeIds,
|
|
4542
4606
|
/* @__PURE__ */ new Set(),
|
|
4543
4607
|
options.queryLookup
|
|
4544
4608
|
);
|
|
4545
4609
|
}
|
|
4546
|
-
const
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
if (!latestCited.has(id)) lostIds.add(id);
|
|
4555
|
-
}
|
|
4556
|
-
const tone = lostIds.size > gainedIds.size ? "negative" : gainedIds.size > lostIds.size ? "positive" : "neutral";
|
|
4610
|
+
const comparableIds = intersection(
|
|
4611
|
+
collectQueryIds(currentSnapshots),
|
|
4612
|
+
collectQueryIds(previousSnapshots)
|
|
4613
|
+
);
|
|
4614
|
+
const currentActive = intersection(collectActiveQueryIds(currentSnapshots, isActive), comparableIds);
|
|
4615
|
+
const previousActive = intersection(collectActiveQueryIds(previousSnapshots, isActive), comparableIds);
|
|
4616
|
+
const gainedIds = difference(currentActive, previousActive);
|
|
4617
|
+
const lostIds = difference(previousActive, currentActive);
|
|
4557
4618
|
return withQueryLists(
|
|
4558
|
-
{
|
|
4619
|
+
{
|
|
4620
|
+
gained: gainedIds.size,
|
|
4621
|
+
lost: lostIds.size,
|
|
4622
|
+
tone: movementTone(gainedIds.size, lostIds.size),
|
|
4623
|
+
hasPreviousRun: true
|
|
4624
|
+
},
|
|
4559
4625
|
gainedIds,
|
|
4560
4626
|
lostIds,
|
|
4561
4627
|
options.queryLookup
|
|
4562
4628
|
);
|
|
4563
4629
|
}
|
|
4630
|
+
function movementTone(gained, lost) {
|
|
4631
|
+
if (lost > gained) return "negative";
|
|
4632
|
+
if (gained > lost) return "positive";
|
|
4633
|
+
return "neutral";
|
|
4634
|
+
}
|
|
4564
4635
|
function withQueryLists(base, gainedIds, lostIds, lookup) {
|
|
4565
4636
|
if (!lookup) return base;
|
|
4566
4637
|
return {
|
|
@@ -4570,6 +4641,7 @@ function withQueryLists(base, gainedIds, lostIds, lookup) {
|
|
|
4570
4641
|
};
|
|
4571
4642
|
}
|
|
4572
4643
|
function resolveQueryTexts(ids, lookup) {
|
|
4644
|
+
if (!lookup) return [];
|
|
4573
4645
|
const out = [];
|
|
4574
4646
|
for (const id of ids) {
|
|
4575
4647
|
const text2 = lookup.get(id);
|
|
@@ -4577,12 +4649,33 @@ function resolveQueryTexts(ids, lookup) {
|
|
|
4577
4649
|
}
|
|
4578
4650
|
return out.sort();
|
|
4579
4651
|
}
|
|
4580
|
-
function
|
|
4581
|
-
const
|
|
4582
|
-
for (const
|
|
4583
|
-
if (
|
|
4652
|
+
function collectQueryIds(snapshots) {
|
|
4653
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4654
|
+
for (const snapshot of snapshots) {
|
|
4655
|
+
if (snapshot.queryId) ids.add(snapshot.queryId);
|
|
4656
|
+
}
|
|
4657
|
+
return ids;
|
|
4658
|
+
}
|
|
4659
|
+
function collectActiveQueryIds(snapshots, isActive) {
|
|
4660
|
+
const active = /* @__PURE__ */ new Set();
|
|
4661
|
+
for (const snapshot of snapshots) {
|
|
4662
|
+
if (snapshot.queryId && isActive(snapshot)) active.add(snapshot.queryId);
|
|
4584
4663
|
}
|
|
4585
|
-
return
|
|
4664
|
+
return active;
|
|
4665
|
+
}
|
|
4666
|
+
function intersection(left, right) {
|
|
4667
|
+
const out = /* @__PURE__ */ new Set();
|
|
4668
|
+
for (const value of left) {
|
|
4669
|
+
if (right.has(value)) out.add(value);
|
|
4670
|
+
}
|
|
4671
|
+
return out;
|
|
4672
|
+
}
|
|
4673
|
+
function difference(left, right) {
|
|
4674
|
+
const out = /* @__PURE__ */ new Set();
|
|
4675
|
+
for (const value of left) {
|
|
4676
|
+
if (!right.has(value)) out.add(value);
|
|
4677
|
+
}
|
|
4678
|
+
return out;
|
|
4586
4679
|
}
|
|
4587
4680
|
|
|
4588
4681
|
// ../intelligence/src/score-tones.ts
|
|
@@ -5027,12 +5120,12 @@ var DEFAULT_LIMIT = 10;
|
|
|
5027
5120
|
function buildSuggestedQueries(gscRows, options) {
|
|
5028
5121
|
const minImpressions = options.minImpressions ?? DEFAULT_MIN_IMPRESSIONS;
|
|
5029
5122
|
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
5030
|
-
const trackedSet = new Set(options.trackedQueries.map(
|
|
5123
|
+
const trackedSet = new Set(options.trackedQueries.map(normalizeQueryText));
|
|
5031
5124
|
let skippedAlreadyTracked = 0;
|
|
5032
5125
|
const candidates = [];
|
|
5033
5126
|
for (const row of gscRows) {
|
|
5034
5127
|
if (row.impressions < minImpressions) continue;
|
|
5035
|
-
const normalized =
|
|
5128
|
+
const normalized = normalizeQueryText(row.query);
|
|
5036
5129
|
if (normalized.length === 0) continue;
|
|
5037
5130
|
if (trackedSet.has(normalized)) {
|
|
5038
5131
|
skippedAlreadyTracked++;
|
|
@@ -5054,9 +5147,6 @@ function buildSuggestedQueries(gscRows, options) {
|
|
|
5054
5147
|
skippedAlreadyTracked
|
|
5055
5148
|
};
|
|
5056
5149
|
}
|
|
5057
|
-
function normalizeQuery(value) {
|
|
5058
|
-
return value.trim().toLowerCase();
|
|
5059
|
-
}
|
|
5060
5150
|
function buildReason(row) {
|
|
5061
5151
|
const impressionsLabel = formatImpressions2(row.impressions);
|
|
5062
5152
|
if (row.avgPosition <= 10) {
|
|
@@ -12924,18 +13014,31 @@ async function compositeRoutes(app) {
|
|
|
12924
13014
|
const snapshotRunIds = new Set(sparklineRunIds);
|
|
12925
13015
|
for (const run of latestVisRunGroup) snapshotRunIds.add(run.id);
|
|
12926
13016
|
for (const run of previousVisRunGroup) snapshotRunIds.add(run.id);
|
|
12927
|
-
const
|
|
13017
|
+
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
|
|
13018
|
+
const queryIdByText = new Map(projectQueries.map((q) => [normalizeQueryText(q.query), q.id]));
|
|
13019
|
+
const snapshotsByRun = loadSnapshotsByRunIds(app, [...snapshotRunIds], queryIdByText);
|
|
12928
13020
|
const latestSnapshots = latestVisRunGroup.flatMap((r) => snapshotsByRun.get(r.id) ?? []);
|
|
12929
13021
|
const previousSnapshots = previousVisRunGroup.flatMap((r) => snapshotsByRun.get(r.id) ?? []);
|
|
12930
|
-
const
|
|
13022
|
+
const trackedLatest = latestSnapshots.filter((s) => !s.archived);
|
|
13023
|
+
const trackedPrevious = previousSnapshots.filter((s) => !s.archived);
|
|
13024
|
+
const trackedSnapshotsByRun = new Map(
|
|
13025
|
+
[...snapshotsByRun].map(([runId, snaps]) => [runId, snaps.filter((s) => !s.archived)])
|
|
13026
|
+
);
|
|
13027
|
+
const { queryCounts, providers } = summarizeFromSnapshots(trackedLatest);
|
|
12931
13028
|
const transitions = summarizeTransitionsFromSnapshots(
|
|
12932
|
-
|
|
12933
|
-
|
|
13029
|
+
trackedLatest,
|
|
13030
|
+
trackedPrevious,
|
|
12934
13031
|
previousVisibilityRun?.createdAt ?? null
|
|
12935
13032
|
);
|
|
12936
13033
|
const competitorRows = app.db.select().from(competitors).where(eq17(competitors.projectId, project.id)).all();
|
|
12937
|
-
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
|
|
12938
13034
|
const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
|
|
13035
|
+
for (const snapshots of snapshotsByRun.values()) {
|
|
13036
|
+
for (const snapshot of snapshots) {
|
|
13037
|
+
if (snapshot.queryText && !queryLookup.byId.has(snapshot.queryId)) {
|
|
13038
|
+
queryLookup.byId.set(snapshot.queryId, snapshot.queryText);
|
|
13039
|
+
}
|
|
13040
|
+
}
|
|
13041
|
+
}
|
|
12939
13042
|
const configuredApiProviders = project.providers.filter((p) => !p.startsWith("cdp:"));
|
|
12940
13043
|
const mentionShareCompetitors = competitorRows.map((c) => ({
|
|
12941
13044
|
domain: c.domain,
|
|
@@ -12945,32 +13048,39 @@ async function compositeRoutes(app) {
|
|
|
12945
13048
|
brandTokens: [brandLabelFromDomain(c.domain)].filter((t) => t.length >= 3)
|
|
12946
13049
|
}));
|
|
12947
13050
|
const scores = {
|
|
12948
|
-
mention: buildMentionCoverage(
|
|
12949
|
-
visibility: buildVisibilityScore(
|
|
13051
|
+
mention: buildMentionCoverage(trackedLatest, { configuredApiProviders }),
|
|
13052
|
+
visibility: buildVisibilityScore(trackedLatest, { configuredApiProviders }),
|
|
12950
13053
|
mentionShare: buildMentionShare(
|
|
12951
|
-
|
|
13054
|
+
trackedLatest.map((s) => ({
|
|
12952
13055
|
projectMentioned: s.answerMentioned === true,
|
|
12953
13056
|
answerText: s.answerText
|
|
12954
13057
|
})),
|
|
12955
13058
|
{ competitors: mentionShareCompetitors }
|
|
12956
13059
|
),
|
|
12957
|
-
gapQueries: buildGapQueryScore(
|
|
12958
|
-
mentionGaps: buildMentionGapScore(
|
|
13060
|
+
gapQueries: buildGapQueryScore(trackedLatest),
|
|
13061
|
+
mentionGaps: buildMentionGapScore(trackedLatest),
|
|
12959
13062
|
indexCoverage: buildIndexCoverageScore(app, project.id),
|
|
12960
13063
|
competitorPressure: buildCompetitorPressureScore(
|
|
12961
|
-
|
|
13064
|
+
trackedLatest,
|
|
12962
13065
|
competitorRows.map((c) => c.domain),
|
|
12963
13066
|
competitorRows.length
|
|
12964
13067
|
),
|
|
12965
13068
|
runStatus: buildRunStatusScore(allRuns)
|
|
12966
13069
|
};
|
|
12967
|
-
const
|
|
13070
|
+
const citationMovement = buildCitationMovementSummary(latestSnapshots, previousSnapshots, {
|
|
12968
13071
|
queryLookup: queryLookup.byId
|
|
12969
13072
|
});
|
|
12970
|
-
const
|
|
13073
|
+
const mentionMovement = buildMentionMovementSummary(latestSnapshots, previousSnapshots, {
|
|
13074
|
+
queryLookup: queryLookup.byId
|
|
13075
|
+
});
|
|
13076
|
+
const movementComparison = buildMovementComparison(latestSnapshots, previousSnapshots, {
|
|
13077
|
+
queryLookup: queryLookup.byId,
|
|
13078
|
+
previousRunAt: previousVisibilityRun?.createdAt ?? null
|
|
13079
|
+
});
|
|
13080
|
+
const providerScoresBase = buildProviderScores(trackedLatest);
|
|
12971
13081
|
const providerTrends = buildProviderTrends(
|
|
12972
13082
|
visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => ({ id: r.id, createdAt: r.createdAt })),
|
|
12973
|
-
|
|
13083
|
+
trackedSnapshotsByRun,
|
|
12974
13084
|
DEFAULT_RUN_HISTORY_LIMIT
|
|
12975
13085
|
);
|
|
12976
13086
|
const providerScores = providerScoresBase.map((score) => {
|
|
@@ -12978,13 +13088,13 @@ async function compositeRoutes(app) {
|
|
|
12978
13088
|
return trend.length > 1 ? { ...score, trend: trend.map((p) => p.rate) } : score;
|
|
12979
13089
|
});
|
|
12980
13090
|
const overviewCompetitors = buildOverviewCompetitors(
|
|
12981
|
-
|
|
13091
|
+
trackedLatest,
|
|
12982
13092
|
competitorRows.map((c) => ({ id: c.id, domain: c.domain })),
|
|
12983
13093
|
queryLookup
|
|
12984
13094
|
);
|
|
12985
13095
|
const attentionItems = buildAttentionItems(insightRows, allRuns);
|
|
12986
13096
|
const sparklineRuns = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => ({ id: r.id, createdAt: r.createdAt, status: r.status }));
|
|
12987
|
-
const runHistory = buildRunHistory(sparklineRuns,
|
|
13097
|
+
const runHistory = buildRunHistory(sparklineRuns, trackedSnapshotsByRun);
|
|
12988
13098
|
scores.mention.trend = runHistory.map((p) => p.mentionRate);
|
|
12989
13099
|
scores.visibility.trend = runHistory.map((p) => p.citationRate);
|
|
12990
13100
|
const suggestedQueries = buildSuggestedQueriesFromGsc(
|
|
@@ -13001,7 +13111,12 @@ async function compositeRoutes(app) {
|
|
|
13001
13111
|
providers,
|
|
13002
13112
|
transitions,
|
|
13003
13113
|
scores,
|
|
13004
|
-
|
|
13114
|
+
// Keep the legacy citation-only field for API compatibility. New
|
|
13115
|
+
// consumers read the explicitly named siblings below.
|
|
13116
|
+
movementSummary: citationMovement,
|
|
13117
|
+
citationMovement,
|
|
13118
|
+
mentionMovement,
|
|
13119
|
+
movementComparison,
|
|
13005
13120
|
competitors: overviewCompetitors,
|
|
13006
13121
|
providerScores,
|
|
13007
13122
|
attentionItems,
|
|
@@ -13111,12 +13226,13 @@ function summarizeRun(run) {
|
|
|
13111
13226
|
createdAt: run.createdAt
|
|
13112
13227
|
};
|
|
13113
13228
|
}
|
|
13114
|
-
function loadSnapshotsByRunIds(app, runIds) {
|
|
13229
|
+
function loadSnapshotsByRunIds(app, runIds, queryIdByText) {
|
|
13115
13230
|
const result = /* @__PURE__ */ new Map();
|
|
13116
13231
|
if (runIds.length === 0) return result;
|
|
13117
|
-
const rows =
|
|
13232
|
+
const rows = app.db.select({
|
|
13118
13233
|
runId: querySnapshots.runId,
|
|
13119
13234
|
queryId: querySnapshots.queryId,
|
|
13235
|
+
queryText: querySnapshots.queryText,
|
|
13120
13236
|
provider: querySnapshots.provider,
|
|
13121
13237
|
model: querySnapshots.model,
|
|
13122
13238
|
citationState: querySnapshots.citationState,
|
|
@@ -13124,11 +13240,30 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
13124
13240
|
answerText: querySnapshots.answerText,
|
|
13125
13241
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
13126
13242
|
citedDomains: querySnapshots.citedDomains
|
|
13127
|
-
}).from(querySnapshots).where(inArray9(querySnapshots.runId, [...runIds])).all()
|
|
13243
|
+
}).from(querySnapshots).where(inArray9(querySnapshots.runId, [...runIds])).all();
|
|
13128
13244
|
for (const row of rows) {
|
|
13245
|
+
const queryText = row.queryText?.trim() || null;
|
|
13246
|
+
let queryId;
|
|
13247
|
+
let archived = false;
|
|
13248
|
+
if (row.queryId) {
|
|
13249
|
+
queryId = row.queryId;
|
|
13250
|
+
} else if (queryText) {
|
|
13251
|
+
const tracked = queryIdByText.get(normalizeQueryText(queryText));
|
|
13252
|
+
if (tracked) {
|
|
13253
|
+
queryId = tracked;
|
|
13254
|
+
} else {
|
|
13255
|
+
queryId = `archived:${normalizeQueryText(queryText)}`;
|
|
13256
|
+
archived = true;
|
|
13257
|
+
}
|
|
13258
|
+
} else {
|
|
13259
|
+
queryId = null;
|
|
13260
|
+
}
|
|
13261
|
+
if (!queryId) continue;
|
|
13129
13262
|
const list = result.get(row.runId) ?? [];
|
|
13130
13263
|
list.push({
|
|
13131
|
-
queryId
|
|
13264
|
+
queryId,
|
|
13265
|
+
queryText,
|
|
13266
|
+
archived,
|
|
13132
13267
|
provider: row.provider,
|
|
13133
13268
|
model: row.model,
|
|
13134
13269
|
citationState: row.citationState,
|
|
@@ -13618,6 +13753,7 @@ var SCHEMA_TABLE = {
|
|
|
13618
13753
|
LocationContext: locationContextSchema,
|
|
13619
13754
|
NotificationDto: notificationDtoSchema,
|
|
13620
13755
|
ProjectDto: projectDtoSchema,
|
|
13756
|
+
ProjectOverviewDto: projectOverviewDtoSchema,
|
|
13621
13757
|
ProjectReportDto: projectReportDtoSchema,
|
|
13622
13758
|
QueryDto: queryDtoSchema,
|
|
13623
13759
|
RunDetailDto: runDetailDtoSchema,
|
|
@@ -16851,12 +16987,11 @@ var routeCatalog = [
|
|
|
16851
16987
|
method: "get",
|
|
16852
16988
|
path: "/api/v1/projects/{name}/overview",
|
|
16853
16989
|
summary: "Get a composite overview of project health",
|
|
16854
|
-
description: 'Bundles project info, latest run, top undismissed insights,
|
|
16990
|
+
description: 'Bundles project info, latest run, top undismissed insights, health, independent mention and citation coverage, query-basket comparability, and separate mention/citation movement over the shared query cohort. Designed for the "how is project X doing?" question so agents can answer in one call.',
|
|
16855
16991
|
tags: ["intelligence"],
|
|
16856
16992
|
parameters: [nameParameter],
|
|
16857
16993
|
responses: {
|
|
16858
|
-
|
|
16859
|
-
200: rawJsonResponse("Overview returned.", looseObjectSchema),
|
|
16994
|
+
200: jsonResponse("Overview returned.", "ProjectOverviewDto"),
|
|
16860
16995
|
404: errorResponse("Project not found.")
|
|
16861
16996
|
}
|
|
16862
16997
|
},
|
|
@@ -30493,12 +30628,19 @@ async function trafficRoutes(app, opts) {
|
|
|
30493
30628
|
return response;
|
|
30494
30629
|
});
|
|
30495
30630
|
function buildSourceDetail(projectId, row, since) {
|
|
30496
|
-
const
|
|
30631
|
+
const crawlerPathRows = app.db.select({
|
|
30632
|
+
pathNormalized: crawlerEventsHourly.pathNormalized,
|
|
30633
|
+
hits: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
30634
|
+
}).from(crawlerEventsHourly).where(
|
|
30497
30635
|
and21(
|
|
30498
30636
|
eq27(crawlerEventsHourly.sourceId, row.id),
|
|
30499
30637
|
gte4(crawlerEventsHourly.tsHour, since)
|
|
30500
30638
|
)
|
|
30501
|
-
).
|
|
30639
|
+
).groupBy(crawlerEventsHourly.pathNormalized).all();
|
|
30640
|
+
const crawlerSegments = segmentCrawlerHits(
|
|
30641
|
+
crawlerPathRows.map((r) => ({ pathNormalized: r.pathNormalized, hits: Number(r.hits) }))
|
|
30642
|
+
);
|
|
30643
|
+
const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
|
|
30502
30644
|
const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
|
|
30503
30645
|
and21(
|
|
30504
30646
|
eq27(aiUserFetchEventsHourly.sourceId, row.id),
|
|
@@ -30527,7 +30669,10 @@ async function trafficRoutes(app, opts) {
|
|
|
30527
30669
|
return {
|
|
30528
30670
|
...rowToDto(row),
|
|
30529
30671
|
totals24h: {
|
|
30530
|
-
crawlerHits:
|
|
30672
|
+
crawlerHits: crawlerTotal,
|
|
30673
|
+
crawlerContentHits: crawlerSegments.content,
|
|
30674
|
+
crawlerInfraHits: sumInfraHits(crawlerSegments),
|
|
30675
|
+
crawlerSegments,
|
|
30531
30676
|
aiUserFetchHits: Number(aiUserFetchTotals?.total ?? 0),
|
|
30532
30677
|
aiReferralHits: Number(aiTotals?.total ?? 0),
|
|
30533
30678
|
sampleCount: Number(sampleTotals?.total ?? 0)
|
|
@@ -30644,6 +30789,7 @@ async function trafficRoutes(app, opts) {
|
|
|
30644
30789
|
const untilIso = until.toISOString();
|
|
30645
30790
|
const events = [];
|
|
30646
30791
|
let crawlerTotal = 0;
|
|
30792
|
+
let crawlerSegments = { content: 0, sitemap: 0, robots: 0, asset: 0, other: 0 };
|
|
30647
30793
|
let aiUserFetchTotal = 0;
|
|
30648
30794
|
let aiReferralTotal = 0;
|
|
30649
30795
|
if (kind === "all" || kind === TrafficEventKinds.crawler) {
|
|
@@ -30654,8 +30800,14 @@ async function trafficRoutes(app, opts) {
|
|
|
30654
30800
|
];
|
|
30655
30801
|
if (sourceIdParam) crawlerFilters.push(eq27(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
30656
30802
|
const crawlerWhere = and21(...crawlerFilters);
|
|
30657
|
-
const
|
|
30658
|
-
|
|
30803
|
+
const pathTotals = app.db.select({
|
|
30804
|
+
pathNormalized: crawlerEventsHourly.pathNormalized,
|
|
30805
|
+
hits: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
30806
|
+
}).from(crawlerEventsHourly).where(crawlerWhere).groupBy(crawlerEventsHourly.pathNormalized).all();
|
|
30807
|
+
crawlerSegments = segmentCrawlerHits(
|
|
30808
|
+
pathTotals.map((r) => ({ pathNormalized: r.pathNormalized, hits: Number(r.hits) }))
|
|
30809
|
+
);
|
|
30810
|
+
crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
|
|
30659
30811
|
const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc15(crawlerEventsHourly.tsHour)).limit(limit).all();
|
|
30660
30812
|
for (const r of rows) {
|
|
30661
30813
|
events.push({
|
|
@@ -30666,6 +30818,7 @@ async function trafficRoutes(app, opts) {
|
|
|
30666
30818
|
operator: r.operator,
|
|
30667
30819
|
verificationStatus: r.verificationStatus,
|
|
30668
30820
|
pathNormalized: r.pathNormalized,
|
|
30821
|
+
pathClass: classifyTrafficPath(r.pathNormalized),
|
|
30669
30822
|
status: r.status,
|
|
30670
30823
|
hits: r.hits
|
|
30671
30824
|
});
|
|
@@ -30729,6 +30882,9 @@ async function trafficRoutes(app, opts) {
|
|
|
30729
30882
|
windowEnd: untilIso,
|
|
30730
30883
|
totals: {
|
|
30731
30884
|
crawlerHits: crawlerTotal,
|
|
30885
|
+
crawlerContentHits: crawlerSegments.content,
|
|
30886
|
+
crawlerInfraHits: sumInfraHits(crawlerSegments),
|
|
30887
|
+
crawlerSegments,
|
|
30732
30888
|
aiUserFetchHits: aiUserFetchTotal,
|
|
30733
30889
|
aiReferralHits: aiReferralTotal
|
|
30734
30890
|
},
|
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
setTelemetrySource,
|
|
28
28
|
showFirstRunNotice,
|
|
29
29
|
trackEvent
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-2VEFNAQ3.js";
|
|
31
31
|
import {
|
|
32
32
|
CliError,
|
|
33
33
|
EXIT_SYSTEM_ERROR,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
saveConfig,
|
|
45
45
|
saveConfigPatch,
|
|
46
46
|
usageError
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-RH3QYFX3.js";
|
|
48
48
|
import {
|
|
49
49
|
apiKeys,
|
|
50
50
|
createClient,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
projects,
|
|
53
53
|
queries,
|
|
54
54
|
renderReportHtml
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-WMZJO56S.js";
|
|
56
56
|
import {
|
|
57
57
|
BacklinkSources,
|
|
58
58
|
CcReleaseSyncStatuses,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
providerQuotaPolicySchema,
|
|
78
78
|
resolveProviderInput,
|
|
79
79
|
winnabilityClassSchema
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-JZ67YCHT.js";
|
|
81
81
|
|
|
82
82
|
// src/cli.ts
|
|
83
83
|
import { pathToFileURL } from "url";
|
|
@@ -4245,7 +4245,10 @@ async function trafficStatus(project, opts) {
|
|
|
4245
4245
|
console.log(` Status: ${d.status}`);
|
|
4246
4246
|
console.log(` Last synced: ${d.lastSyncedAt ?? "never"}`);
|
|
4247
4247
|
if (d.lastError) console.log(` Last error: ${d.lastError}`);
|
|
4248
|
-
console.log(` 24h
|
|
4248
|
+
console.log(` 24h content: ${d.totals24h.crawlerContentHits} crawls`);
|
|
4249
|
+
console.log(` 24h infra: ${d.totals24h.crawlerInfraHits} sitemap/robots/asset fetches`);
|
|
4250
|
+
console.log(` 24h other: ${d.totals24h.crawlerSegments.other} fetches`);
|
|
4251
|
+
console.log(` 24h crawler: ${d.totals24h.crawlerHits} hits total`);
|
|
4249
4252
|
console.log(` 24h AI referral: ${d.totals24h.aiReferralHits} sessions`);
|
|
4250
4253
|
console.log(` 24h samples: ${d.totals24h.sampleCount}`);
|
|
4251
4254
|
if (d.latestRun) {
|
|
@@ -4268,7 +4271,7 @@ function formatEventLine(event) {
|
|
|
4268
4271
|
event.botId,
|
|
4269
4272
|
event.verificationStatus,
|
|
4270
4273
|
String(event.status),
|
|
4271
|
-
event.pathNormalized
|
|
4274
|
+
`${event.pathNormalized} [${event.pathClass}]`,
|
|
4272
4275
|
`${event.hits} hits`
|
|
4273
4276
|
].join(" ");
|
|
4274
4277
|
case TrafficEventKinds["ai-user-fetch"]:
|
|
@@ -4328,7 +4331,10 @@ async function trafficEvents(project, opts) {
|
|
|
4328
4331
|
return;
|
|
4329
4332
|
}
|
|
4330
4333
|
console.log(`Traffic events for "${project}" ${result.windowStart} \u2192 ${result.windowEnd}`);
|
|
4331
|
-
console.log(`
|
|
4334
|
+
console.log(` Content crawls (window): ${result.totals.crawlerContentHits}`);
|
|
4335
|
+
console.log(` Infra fetches (window): ${result.totals.crawlerInfraHits} (sitemap ${result.totals.crawlerSegments.sitemap} \xB7 robots ${result.totals.crawlerSegments.robots} \xB7 asset ${result.totals.crawlerSegments.asset})`);
|
|
4336
|
+
console.log(` Other fetches (window): ${result.totals.crawlerSegments.other}`);
|
|
4337
|
+
console.log(` Crawler hits total (window): ${result.totals.crawlerHits}`);
|
|
4332
4338
|
console.log(` AI user-fetch hits (window): ${result.totals.aiUserFetchHits}`);
|
|
4333
4339
|
console.log(` AI referral sessions (window): ${result.totals.aiReferralHits}`);
|
|
4334
4340
|
console.log("");
|
|
@@ -9405,7 +9411,9 @@ function renderHuman(overview) {
|
|
|
9405
9411
|
providers,
|
|
9406
9412
|
transitions,
|
|
9407
9413
|
scores,
|
|
9408
|
-
|
|
9414
|
+
citationMovement,
|
|
9415
|
+
mentionMovement,
|
|
9416
|
+
movementComparison,
|
|
9409
9417
|
competitors,
|
|
9410
9418
|
providerScores,
|
|
9411
9419
|
attentionItems,
|
|
@@ -9438,10 +9446,13 @@ function renderHuman(overview) {
|
|
|
9438
9446
|
console.log(`
|
|
9439
9447
|
Queries cited: ${queryCounts.citedQueries}/${queryCounts.totalQueries} (${pct2(queryCounts.citedRate)})`);
|
|
9440
9448
|
console.log(` Queries mentioned: ${queryCounts.mentionedQueries}/${queryCounts.totalQueries} (${pct2(queryCounts.mentionRate)})`);
|
|
9441
|
-
if (
|
|
9442
|
-
|
|
9443
|
-
}
|
|
9444
|
-
console.log(`
|
|
9449
|
+
if (movementComparison.hasPreviousRun) {
|
|
9450
|
+
const comparisonLabel = movementComparison.querySetChanged ? `changed (+${movementComparison.addedQueryCount} added, -${movementComparison.removedQueryCount} removed); movement compares ${movementComparison.comparableQueryCount} shared` : `unchanged; ${movementComparison.comparableQueryCount} comparable`;
|
|
9451
|
+
console.log(` Query basket: ${comparisonLabel}`);
|
|
9452
|
+
console.log(` Citation movement: +${citationMovement.gained} gained, -${citationMovement.lost} lost (${citationMovement.tone})`);
|
|
9453
|
+
console.log(` Mention movement: +${mentionMovement.gained} gained, -${mentionMovement.lost} lost (${mentionMovement.tone})`);
|
|
9454
|
+
} else {
|
|
9455
|
+
console.log(" Movement: first sweep; no comparison yet");
|
|
9445
9456
|
}
|
|
9446
9457
|
if (providers.length > 0) {
|
|
9447
9458
|
console.log("\n Providers:");
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2VEFNAQ3.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-RH3QYFX3.js";
|
|
7
|
+
import "./chunk-WMZJO56S.js";
|
|
8
|
+
import "./chunk-JZ67YCHT.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
|
11
11
|
loadConfig
|
package/dist/mcp.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
PACKAGE_VERSION,
|
|
4
4
|
canonryMcpTools,
|
|
5
5
|
createApiClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RH3QYFX3.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JZ67YCHT.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp/cli.ts
|
|
12
12
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|