@ainyc/canonry 4.115.1 → 4.116.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/assets/{BacklinksPage-yUb8oJuq.js → BacklinksPage-Dy6Xc-LT.js} +1 -1
- package/assets/assets/{ChartPrimitives-CdQM3YDG.js → ChartPrimitives-CHJh5M8x.js} +1 -1
- package/assets/assets/ProjectPage-Bjfa4CI5.js +7 -0
- package/assets/assets/{RunRow-BU_x7EcP.js → RunRow-DmvQnWPf.js} +1 -1
- package/assets/assets/{RunsPage-CFIBWaO7.js → RunsPage-BxkxQEKE.js} +1 -1
- package/assets/assets/{SettingsPage-PyMryUad.js → SettingsPage-DCyqHqDh.js} +1 -1
- package/assets/assets/{TrafficPage-471ypAdQ.js → TrafficPage-BT_ae5aO.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CI7TEQge.js → TrafficSourceDetailPage-Cps-tSHn.js} +1 -1
- package/assets/assets/{arrow-left-D2N9u-0i.js → arrow-left-D8vdqhx4.js} +1 -1
- package/assets/assets/{extract-error-message-CAdsqKp3.js → extract-error-message-BOY5txUA.js} +1 -1
- package/assets/assets/{index-CoL5HENE.js → index-DxjY63VC.js} +63 -63
- package/assets/assets/{trash-2-UgQrN5Np.js → trash-2-ap_Fa4zA.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-B2SNXXAJ.js → chunk-GV665WOU.js} +4 -4
- package/dist/{chunk-6LOKMEL3.js → chunk-IQPMK36Y.js} +1285 -1233
- package/dist/{chunk-CW4JISBL.js → chunk-RVH6QPTA.js} +1 -1
- package/dist/{chunk-Z3OGDQHB.js → chunk-WEAXJ25Y.js} +162 -34
- package/dist/cli.js +9 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-4AWB2V7U.js → intelligence-service-HEN5HLUM.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +12 -12
- package/assets/assets/ProjectPage-CHj9fvTs.js +0 -7
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
AGENT_PROVIDER_IDS,
|
|
3
3
|
AI_ENGINE_DOMAINS,
|
|
4
4
|
AI_PROVIDER_INFRA_DOMAINS,
|
|
5
|
+
AiReferralTrafficClasses,
|
|
5
6
|
AppError,
|
|
6
7
|
BacklinkSources,
|
|
7
8
|
CcReleaseSyncStatuses,
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
DiscoveryBuckets,
|
|
21
22
|
DiscoveryCompetitorTypes,
|
|
22
23
|
DiscoverySessionStatuses,
|
|
23
|
-
GA4AiReferralTrafficClasses,
|
|
24
24
|
RunKinds,
|
|
25
25
|
RunStatuses,
|
|
26
26
|
RunTriggers,
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
adsSyncResponseSchema,
|
|
51
51
|
agentProvidersResponseDtoSchema,
|
|
52
52
|
aggregateHarvestedQueries,
|
|
53
|
+
aiReferralClassCounts,
|
|
53
54
|
apiKeyDtoSchema,
|
|
54
55
|
apiKeyListDtoSchema,
|
|
55
56
|
applyHarvestSemanticNovelty,
|
|
@@ -87,8 +88,8 @@ import {
|
|
|
87
88
|
citationStateSchema,
|
|
88
89
|
citationStateToCited,
|
|
89
90
|
citationVisibilityResponseSchema,
|
|
91
|
+
classifyAiReferralTrafficClass,
|
|
90
92
|
classifyCitedSurface,
|
|
91
|
-
classifyGa4AiReferralTrafficClass,
|
|
92
93
|
classifySkillFile,
|
|
93
94
|
classifySurfaceFromCategory,
|
|
94
95
|
classifyTrafficPath,
|
|
@@ -132,6 +133,7 @@ import {
|
|
|
132
133
|
filterBrandedSeedCandidates,
|
|
133
134
|
findDuplicateLocationLabels,
|
|
134
135
|
forbidden,
|
|
136
|
+
formatAiReferralClassSummary,
|
|
135
137
|
formatAverageDelta,
|
|
136
138
|
formatDate,
|
|
137
139
|
formatDateRange,
|
|
@@ -275,7 +277,7 @@ import {
|
|
|
275
277
|
wordpressSchemaDeployResultDtoSchema,
|
|
276
278
|
wordpressSchemaStatusResultDtoSchema,
|
|
277
279
|
wordpressStatusDtoSchema
|
|
278
|
-
} from "./chunk-
|
|
280
|
+
} from "./chunk-IQPMK36Y.js";
|
|
279
281
|
|
|
280
282
|
// src/intelligence-service.ts
|
|
281
283
|
import { eq as eq38, desc as desc18, asc as asc6, and as and30, ne as ne5, or as or6, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
@@ -1100,6 +1102,17 @@ var aiReferralEventsHourly = sqliteTable("ai_referral_events_hourly", {
|
|
|
1100
1102
|
landingPathNormalized: text("landing_path_normalized").notNull(),
|
|
1101
1103
|
status: integer("status").notNull(),
|
|
1102
1104
|
sessionsOrHits: integer("sessions_or_hits").notNull().default(0),
|
|
1105
|
+
/**
|
|
1106
|
+
* Paid/organic splits the MEASURE, not the primary key. The paid marker lives
|
|
1107
|
+
* in the request's UTM tags, which `landing_path_normalized` strips, so one
|
|
1108
|
+
* bucket can legitimately hold both kinds and the class is NOT functionally
|
|
1109
|
+
* determined by the key. Unclassified arrivals are the residual
|
|
1110
|
+
* `sessions_or_hits - paid - organic`, which leaves rows written before the
|
|
1111
|
+
* ingest classifier shipped (both columns 0) visibly unknown rather than
|
|
1112
|
+
* silently organic — the regression migration v95 shipped on the GA4 side.
|
|
1113
|
+
*/
|
|
1114
|
+
paidSessionsOrHits: integer("paid_sessions_or_hits").notNull().default(0),
|
|
1115
|
+
organicSessionsOrHits: integer("organic_sessions_or_hits").notNull().default(0),
|
|
1103
1116
|
usersEstimated: integer("users_estimated"),
|
|
1104
1117
|
createdAt: text("created_at").notNull(),
|
|
1105
1118
|
updatedAt: text("updated_at").notNull()
|
|
@@ -3512,7 +3525,7 @@ var MIGRATION_VERSIONS = [
|
|
|
3512
3525
|
//
|
|
3513
3526
|
// Every row is re-classified, not only the "unclassified" ones: v95's
|
|
3514
3527
|
// default is indistinguishable from a genuinely-organic classification, so
|
|
3515
|
-
// there is no way to tell the two apart. `
|
|
3528
|
+
// there is no way to tell the two apart. `classifyAiReferralTrafficClass`
|
|
3516
3529
|
// is pure and deterministic, so re-running it over already-correct rows
|
|
3517
3530
|
// writes nothing, which is what makes this version safe to replay.
|
|
3518
3531
|
version: 96,
|
|
@@ -3521,6 +3534,27 @@ var MIGRATION_VERSIONS = [
|
|
|
3521
3534
|
run: (tx) => {
|
|
3522
3535
|
backfillGaAiReferralTrafficClass(tx);
|
|
3523
3536
|
}
|
|
3537
|
+
},
|
|
3538
|
+
{
|
|
3539
|
+
// Splits the server-side AI-referral MEASURE by traffic class rather than
|
|
3540
|
+
// adding a class column. Paid-ness lives in the request's UTM tags, which
|
|
3541
|
+
// `landing_path_normalized` strips, so one hourly bucket can legitimately
|
|
3542
|
+
// hold both paid and organic arrivals: a class column outside the 8-column
|
|
3543
|
+
// primary key would silently stamp a mixed bucket with one label, and
|
|
3544
|
+
// SQLite cannot extend a composite key with ADD COLUMN anyway.
|
|
3545
|
+
//
|
|
3546
|
+
// Deliberately NO backfill. The discriminator was never persisted — the
|
|
3547
|
+
// request's query string is dropped before the hourly bucket and before
|
|
3548
|
+
// `raw_event_samples` — so no honest class exists for historical rows.
|
|
3549
|
+
// Leaving both counters at 0 surfaces their whole `sessions_or_hits` as the
|
|
3550
|
+
// unknown residual. An older binary writing rows after a downgrade lands in
|
|
3551
|
+
// that residual too, which is the truth about those rows.
|
|
3552
|
+
version: 97,
|
|
3553
|
+
name: "server-side-ai-referral-traffic-class",
|
|
3554
|
+
statements: [
|
|
3555
|
+
`ALTER TABLE ai_referral_events_hourly ADD COLUMN paid_sessions_or_hits INTEGER NOT NULL DEFAULT 0`,
|
|
3556
|
+
`ALTER TABLE ai_referral_events_hourly ADD COLUMN organic_sessions_or_hits INTEGER NOT NULL DEFAULT 0`
|
|
3557
|
+
]
|
|
3524
3558
|
}
|
|
3525
3559
|
];
|
|
3526
3560
|
function rebuildBacklinkTableWithSource(tx, table) {
|
|
@@ -3615,7 +3649,7 @@ function backfillGaAiReferralTrafficClass(tx) {
|
|
|
3615
3649
|
));
|
|
3616
3650
|
let updated = 0;
|
|
3617
3651
|
for (const row of rows) {
|
|
3618
|
-
const next =
|
|
3652
|
+
const next = classifyAiReferralTrafficClass({
|
|
3619
3653
|
source: row.source,
|
|
3620
3654
|
medium: row.medium,
|
|
3621
3655
|
channelGroup: row.channel_group,
|
|
@@ -11272,6 +11306,7 @@ function renderServerActivity(report, audience) {
|
|
|
11272
11306
|
if (!copy) return "";
|
|
11273
11307
|
return `<span class="tone-${deltaTone(d.deltaPct)}">${escapeHtml(copy)}</span>`;
|
|
11274
11308
|
};
|
|
11309
|
+
const referralSubtitle = [formatDelta(sa.referralArrivals, "sessions"), escapeHtml(sa.referralArrivalsClassSummary)].filter(Boolean).join(" \xB7 ");
|
|
11275
11310
|
if (isClient) {
|
|
11276
11311
|
const crawlerRequests = {
|
|
11277
11312
|
current: sa.verifiedCrawlerHits.current + sa.unverifiedCrawlerHits.current,
|
|
@@ -11310,7 +11345,7 @@ function renderServerActivity(report, audience) {
|
|
|
11310
11345
|
<div class="metric">
|
|
11311
11346
|
<div class="label">AI referral sessions</div>
|
|
11312
11347
|
<div class="value">${formatNumber(sa.referralArrivals.current)}</div>
|
|
11313
|
-
<div class="subtitle">${
|
|
11348
|
+
<div class="subtitle">${referralSubtitle}</div>
|
|
11314
11349
|
</div>
|
|
11315
11350
|
</div>
|
|
11316
11351
|
${clientOperatorRows ? `<div class="chart-card"><h3>By AI tool</h3>
|
|
@@ -11379,7 +11414,7 @@ function renderServerActivity(report, audience) {
|
|
|
11379
11414
|
<div class="metric">
|
|
11380
11415
|
<div class="label">AI-referral sessions (${windowLabel})</div>
|
|
11381
11416
|
<div class="value">${formatNumber(sa.referralArrivals.current)}</div>
|
|
11382
|
-
<div class="subtitle">${
|
|
11417
|
+
<div class="subtitle">${referralSubtitle}</div>
|
|
11383
11418
|
</div>
|
|
11384
11419
|
</div>
|
|
11385
11420
|
${trendChart}
|
|
@@ -11905,7 +11940,7 @@ function safeNum(value) {
|
|
|
11905
11940
|
return 0;
|
|
11906
11941
|
}
|
|
11907
11942
|
function isPaidAiTrafficClass(value) {
|
|
11908
|
-
return value ===
|
|
11943
|
+
return value === AiReferralTrafficClasses.paid;
|
|
11909
11944
|
}
|
|
11910
11945
|
function categorizeQuery(query, projectBrandNames, canonicalDomain) {
|
|
11911
11946
|
return categorizeQueryByIntent(query, buildBrandTokens(canonicalDomain, projectBrandNames));
|
|
@@ -12298,16 +12333,21 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12298
12333
|
)
|
|
12299
12334
|
).get()?.total ?? 0
|
|
12300
12335
|
);
|
|
12301
|
-
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) =>
|
|
12302
|
-
db.select({
|
|
12336
|
+
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => {
|
|
12337
|
+
const row = db.select({
|
|
12338
|
+
total: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
12339
|
+
paid: sql7`COALESCE(SUM(${aiReferralEventsHourly.paidSessionsOrHits}), 0)`,
|
|
12340
|
+
organic: sql7`COALESCE(SUM(${aiReferralEventsHourly.organicSessionsOrHits}), 0)`
|
|
12341
|
+
}).from(aiReferralEventsHourly).where(
|
|
12303
12342
|
and12(
|
|
12304
12343
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12305
12344
|
nonSubresourceReferralPathCondition(),
|
|
12306
12345
|
gte2(aiReferralEventsHourly.tsHour, windowStartIso),
|
|
12307
12346
|
exclusiveEnd ? lt(aiReferralEventsHourly.tsHour, windowEndIso) : lte(aiReferralEventsHourly.tsHour, windowEndIso)
|
|
12308
12347
|
)
|
|
12309
|
-
).get()
|
|
12310
|
-
|
|
12348
|
+
).get();
|
|
12349
|
+
return aiReferralClassCounts(Number(row?.total ?? 0), Number(row?.paid ?? 0), Number(row?.organic ?? 0));
|
|
12350
|
+
};
|
|
12311
12351
|
const sumUserFetches = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
12312
12352
|
db.select({ total: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
|
|
12313
12353
|
and12(
|
|
@@ -12505,7 +12545,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12505
12545
|
return {
|
|
12506
12546
|
windowStart: headlineStart,
|
|
12507
12547
|
windowEnd: headlineEnd,
|
|
12508
|
-
hasData: verifiedCurrent + unverifiedCurrent + userFetchCurrent + referralCurrent + verifiedPrior + unverifiedPrior + userFetchPrior + referralPrior > 0 || byOperator.length > 0 || topCrawledPaths.length > 0 || referralProducts.length > 0,
|
|
12548
|
+
hasData: verifiedCurrent + unverifiedCurrent + userFetchCurrent + referralCurrent.total + verifiedPrior + unverifiedPrior + userFetchPrior + referralPrior.total > 0 || byOperator.length > 0 || topCrawledPaths.length > 0 || referralProducts.length > 0,
|
|
12509
12549
|
verifiedCrawlerHits: {
|
|
12510
12550
|
current: verifiedCurrent,
|
|
12511
12551
|
prior: verifiedPrior,
|
|
@@ -12522,10 +12562,28 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12522
12562
|
deltaPct: deltaPercent(userFetchCurrent, userFetchPrior)
|
|
12523
12563
|
},
|
|
12524
12564
|
referralArrivals: {
|
|
12525
|
-
current: referralCurrent,
|
|
12526
|
-
prior: referralPrior,
|
|
12527
|
-
deltaPct: deltaPercent(referralCurrent, referralPrior)
|
|
12565
|
+
current: referralCurrent.total,
|
|
12566
|
+
prior: referralPrior.total,
|
|
12567
|
+
deltaPct: deltaPercent(referralCurrent.total, referralPrior.total)
|
|
12568
|
+
},
|
|
12569
|
+
referralArrivalsByClass: {
|
|
12570
|
+
paid: {
|
|
12571
|
+
current: referralCurrent.paid,
|
|
12572
|
+
prior: referralPrior.paid,
|
|
12573
|
+
deltaPct: deltaPercent(referralCurrent.paid, referralPrior.paid)
|
|
12574
|
+
},
|
|
12575
|
+
organic: {
|
|
12576
|
+
current: referralCurrent.organic,
|
|
12577
|
+
prior: referralPrior.organic,
|
|
12578
|
+
deltaPct: deltaPercent(referralCurrent.organic, referralPrior.organic)
|
|
12579
|
+
},
|
|
12580
|
+
unclassified: {
|
|
12581
|
+
current: referralCurrent.unknown,
|
|
12582
|
+
prior: referralPrior.unknown,
|
|
12583
|
+
deltaPct: deltaPercent(referralCurrent.unknown, referralPrior.unknown)
|
|
12584
|
+
}
|
|
12528
12585
|
},
|
|
12586
|
+
referralArrivalsClassSummary: formatAiReferralClassSummary(referralCurrent),
|
|
12529
12587
|
byOperator,
|
|
12530
12588
|
topCrawledPaths,
|
|
12531
12589
|
referralProducts,
|
|
@@ -20551,7 +20609,7 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
|
|
|
20551
20609
|
date: row.dimensionValues[0].value,
|
|
20552
20610
|
source,
|
|
20553
20611
|
medium,
|
|
20554
|
-
trafficClass:
|
|
20612
|
+
trafficClass: classifyAiReferralTrafficClass({ source, medium, channelGroup, landingPage }),
|
|
20555
20613
|
channelGroup,
|
|
20556
20614
|
landingPage,
|
|
20557
20615
|
sessions: parseInt(row.metricValues[0].value, 10) || 0,
|
|
@@ -23527,7 +23585,7 @@ function pickWinningDimension(rows, tupleKey) {
|
|
|
23527
23585
|
return [...winners.values()].sort((a, b) => (b.sessions ?? 0) - (a.sessions ?? 0));
|
|
23528
23586
|
}
|
|
23529
23587
|
function normalizeAiTrafficClass(value) {
|
|
23530
|
-
return value ===
|
|
23588
|
+
return value === AiReferralTrafficClasses.paid ? AiReferralTrafficClasses.paid : AiReferralTrafficClasses.organic;
|
|
23531
23589
|
}
|
|
23532
23590
|
function emptyAiCounts() {
|
|
23533
23591
|
return { sessions: 0, users: 0 };
|
|
@@ -23540,7 +23598,7 @@ function summarizeAiReferralCounts(rows) {
|
|
|
23540
23598
|
const paidBySession = emptyAiCounts();
|
|
23541
23599
|
const organicBySession = emptyAiCounts();
|
|
23542
23600
|
for (const row of rows) {
|
|
23543
|
-
const isPaid = normalizeAiTrafficClass(row.trafficClass) ===
|
|
23601
|
+
const isPaid = normalizeAiTrafficClass(row.trafficClass) === AiReferralTrafficClasses.paid;
|
|
23544
23602
|
const sessions = row.sessions ?? 0;
|
|
23545
23603
|
const users = row.users ?? 0;
|
|
23546
23604
|
const key = `${row.date}\0${row.source}\0${row.medium}`;
|
|
@@ -23872,7 +23930,7 @@ async function ga4Routes(app, opts) {
|
|
|
23872
23930
|
date: row.date,
|
|
23873
23931
|
source: row.source,
|
|
23874
23932
|
medium: row.medium,
|
|
23875
|
-
trafficClass: row.trafficClass ??
|
|
23933
|
+
trafficClass: row.trafficClass ?? classifyAiReferralTrafficClass({
|
|
23876
23934
|
source: row.source,
|
|
23877
23935
|
medium: row.medium,
|
|
23878
23936
|
channelGroup: row.channelGroup,
|
|
@@ -29650,6 +29708,33 @@ function utmSourceFromUrl(value) {
|
|
|
29650
29708
|
return null;
|
|
29651
29709
|
}
|
|
29652
29710
|
}
|
|
29711
|
+
function paramsFromQuery(queryString) {
|
|
29712
|
+
if (!queryString) return null;
|
|
29713
|
+
const params = new URLSearchParams(queryString);
|
|
29714
|
+
return [...params.keys()].length > 0 ? params : null;
|
|
29715
|
+
}
|
|
29716
|
+
function paramsFromUrl(value) {
|
|
29717
|
+
if (!value) return null;
|
|
29718
|
+
try {
|
|
29719
|
+
return paramsFromQuery(new URL(value).search.replace(/^\?/, ""));
|
|
29720
|
+
} catch {
|
|
29721
|
+
return null;
|
|
29722
|
+
}
|
|
29723
|
+
}
|
|
29724
|
+
function classifyAiReferralTrafficClassFromEvent(event) {
|
|
29725
|
+
for (const params of [paramsFromQuery(event.queryString), paramsFromUrl(event.referer)]) {
|
|
29726
|
+
if (!params) continue;
|
|
29727
|
+
const trafficClass = classifyAiReferralTrafficClass({
|
|
29728
|
+
source: params.get("utm_source"),
|
|
29729
|
+
medium: params.get("utm_medium"),
|
|
29730
|
+
channelGroup: null,
|
|
29731
|
+
// Carries utm_campaign / utm_content / utm_term to the shared rules.
|
|
29732
|
+
landingPage: `/?${params.toString()}`
|
|
29733
|
+
});
|
|
29734
|
+
if (trafficClass === AiReferralTrafficClasses.paid) return AiReferralTrafficClasses.paid;
|
|
29735
|
+
}
|
|
29736
|
+
return AiReferralTrafficClasses.organic;
|
|
29737
|
+
}
|
|
29653
29738
|
function isSelfTraffic(event) {
|
|
29654
29739
|
const userAgent = event.userAgent?.trim();
|
|
29655
29740
|
if (!userAgent) return false;
|
|
@@ -29693,6 +29778,7 @@ function classifyAiUserFetch(event) {
|
|
|
29693
29778
|
return null;
|
|
29694
29779
|
}
|
|
29695
29780
|
function classifyAiReferral(event) {
|
|
29781
|
+
const trafficClass = classifyAiReferralTrafficClassFromEvent(event);
|
|
29696
29782
|
const refererHost = hostFromUrl(event.referer);
|
|
29697
29783
|
if (refererHost) {
|
|
29698
29784
|
const rule = DEFAULT_AI_REFERRER_RULES.find((candidate) => hostMatches(refererHost, candidate.domain));
|
|
@@ -29701,7 +29787,8 @@ function classifyAiReferral(event) {
|
|
|
29701
29787
|
operator: rule.operator,
|
|
29702
29788
|
product: rule.product,
|
|
29703
29789
|
sourceDomain: refererHost,
|
|
29704
|
-
evidenceType: "referer"
|
|
29790
|
+
evidenceType: "referer",
|
|
29791
|
+
trafficClass
|
|
29705
29792
|
};
|
|
29706
29793
|
}
|
|
29707
29794
|
}
|
|
@@ -29713,7 +29800,8 @@ function classifyAiReferral(event) {
|
|
|
29713
29800
|
operator: rule.operator,
|
|
29714
29801
|
product: rule.product,
|
|
29715
29802
|
sourceDomain: utmSource,
|
|
29716
|
-
evidenceType: "utm"
|
|
29803
|
+
evidenceType: "utm",
|
|
29804
|
+
trafficClass
|
|
29717
29805
|
};
|
|
29718
29806
|
}
|
|
29719
29807
|
}
|
|
@@ -29725,7 +29813,8 @@ function classifyAiReferral(event) {
|
|
|
29725
29813
|
operator: rule.operator,
|
|
29726
29814
|
product: rule.product,
|
|
29727
29815
|
sourceDomain: refererUtmSource,
|
|
29728
|
-
evidenceType: "referer-utm"
|
|
29816
|
+
evidenceType: "referer-utm",
|
|
29817
|
+
trafficClass
|
|
29729
29818
|
};
|
|
29730
29819
|
}
|
|
29731
29820
|
}
|
|
@@ -29828,8 +29917,10 @@ function evidenceRank(evidenceType) {
|
|
|
29828
29917
|
return 1;
|
|
29829
29918
|
}
|
|
29830
29919
|
}
|
|
29831
|
-
function
|
|
29832
|
-
|
|
29920
|
+
function mergeReferralSession(current, next) {
|
|
29921
|
+
const winner = evidenceRank(next.evidenceType) > evidenceRank(current.evidenceType) ? next : current;
|
|
29922
|
+
const paid = current.trafficClass === AiReferralTrafficClasses.paid || next.trafficClass === AiReferralTrafficClasses.paid;
|
|
29923
|
+
return { ...winner, trafficClass: paid ? AiReferralTrafficClasses.paid : winner.trafficClass };
|
|
29833
29924
|
}
|
|
29834
29925
|
function sortCrawlerBuckets(a, b) {
|
|
29835
29926
|
return a.tsHour.localeCompare(b.tsHour) || a.botId.localeCompare(b.botId) || a.pathNormalized.localeCompare(b.pathNormalized) || String(a.status).localeCompare(String(b.status));
|
|
@@ -29862,6 +29953,8 @@ function buildTrafficProbeReport(events, options = {}) {
|
|
|
29862
29953
|
let crawlerHits = 0;
|
|
29863
29954
|
let aiUserFetchHits = 0;
|
|
29864
29955
|
let aiReferralHits = 0;
|
|
29956
|
+
let aiReferralPaidSessions = 0;
|
|
29957
|
+
let aiReferralOrganicSessions = 0;
|
|
29865
29958
|
let unknownHits = 0;
|
|
29866
29959
|
const samples = [];
|
|
29867
29960
|
for (const event of includedEvents) {
|
|
@@ -29943,11 +30036,12 @@ function buildTrafficProbeReport(events, options = {}) {
|
|
|
29943
30036
|
sourceDomain: aiReferral.sourceDomain,
|
|
29944
30037
|
evidenceType: aiReferral.evidenceType,
|
|
29945
30038
|
landingPathNormalized,
|
|
29946
|
-
status: event.status
|
|
30039
|
+
status: event.status,
|
|
30040
|
+
trafficClass: aiReferral.trafficClass
|
|
29947
30041
|
};
|
|
29948
30042
|
const key = aiReferralSessionKey(event, aiReferral, landingPathNormalized, aiReferralSessionWindowMs);
|
|
29949
30043
|
const existing = aiReferralSessions.get(key);
|
|
29950
|
-
aiReferralSessions.set(key, existing ?
|
|
30044
|
+
aiReferralSessions.set(key, existing ? mergeReferralSession(existing, session) : session);
|
|
29951
30045
|
}
|
|
29952
30046
|
}
|
|
29953
30047
|
if (!crawler && !aiUserFetch && !aiReferral) unknownHits += 1;
|
|
@@ -29975,13 +30069,21 @@ function buildTrafficProbeReport(events, options = {}) {
|
|
|
29975
30069
|
session.landingPathNormalized,
|
|
29976
30070
|
session.status ?? "null"
|
|
29977
30071
|
].join(" ");
|
|
30072
|
+
const { trafficClass, ...bucketFields } = session;
|
|
30073
|
+
const paid = trafficClass === AiReferralTrafficClasses.paid;
|
|
30074
|
+
if (paid) aiReferralPaidSessions += 1;
|
|
30075
|
+
else aiReferralOrganicSessions += 1;
|
|
29978
30076
|
const existing = aiReferralBuckets.get(key);
|
|
29979
30077
|
if (existing) {
|
|
29980
30078
|
existing.hits += 1;
|
|
30079
|
+
if (paid) existing.paidHits += 1;
|
|
30080
|
+
else existing.organicHits += 1;
|
|
29981
30081
|
} else {
|
|
29982
30082
|
aiReferralBuckets.set(key, {
|
|
29983
|
-
...
|
|
29984
|
-
hits: 1
|
|
30083
|
+
...bucketFields,
|
|
30084
|
+
hits: 1,
|
|
30085
|
+
paidHits: paid ? 1 : 0,
|
|
30086
|
+
organicHits: paid ? 0 : 1
|
|
29985
30087
|
});
|
|
29986
30088
|
}
|
|
29987
30089
|
incrementBucket(topAiReferrers, session.sourceDomain, {
|
|
@@ -30000,6 +30102,8 @@ function buildTrafficProbeReport(events, options = {}) {
|
|
|
30000
30102
|
crawlerHits,
|
|
30001
30103
|
aiUserFetchHits,
|
|
30002
30104
|
aiReferralSessions: aiReferralSessions.size,
|
|
30105
|
+
aiReferralPaidSessions,
|
|
30106
|
+
aiReferralOrganicSessions,
|
|
30003
30107
|
aiReferralHits,
|
|
30004
30108
|
unknownHits
|
|
30005
30109
|
},
|
|
@@ -30761,6 +30865,8 @@ async function runBackfillTask(options) {
|
|
|
30761
30865
|
landingPathNormalized: bucket.landingPathNormalized,
|
|
30762
30866
|
status: bucket.status ?? 0,
|
|
30763
30867
|
sessionsOrHits: bucket.hits,
|
|
30868
|
+
paidSessionsOrHits: bucket.paidHits,
|
|
30869
|
+
organicSessionsOrHits: bucket.organicHits,
|
|
30764
30870
|
usersEstimated: null,
|
|
30765
30871
|
createdAt: finishedAt,
|
|
30766
30872
|
updatedAt: finishedAt
|
|
@@ -31508,6 +31614,8 @@ async function trafficRoutes(app, opts) {
|
|
|
31508
31614
|
landingPathNormalized: bucket.landingPathNormalized,
|
|
31509
31615
|
status,
|
|
31510
31616
|
sessionsOrHits: bucket.hits,
|
|
31617
|
+
paidSessionsOrHits: bucket.paidHits,
|
|
31618
|
+
organicSessionsOrHits: bucket.organicHits,
|
|
31511
31619
|
usersEstimated: null,
|
|
31512
31620
|
createdAt: finishedAt,
|
|
31513
31621
|
updatedAt: finishedAt
|
|
@@ -31522,8 +31630,13 @@ async function trafficRoutes(app, opts) {
|
|
|
31522
31630
|
aiReferralEventsHourly.landingPathNormalized,
|
|
31523
31631
|
aiReferralEventsHourly.status
|
|
31524
31632
|
],
|
|
31633
|
+
// All three counters accumulate. The class splits the measure, so a
|
|
31634
|
+
// colliding bucket adds its paid and organic arrivals to the ones
|
|
31635
|
+
// already there rather than overwriting a single label.
|
|
31525
31636
|
set: {
|
|
31526
31637
|
sessionsOrHits: sql12`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
|
|
31638
|
+
paidSessionsOrHits: sql12`${aiReferralEventsHourly.paidSessionsOrHits} + ${bucket.paidHits}`,
|
|
31639
|
+
organicSessionsOrHits: sql12`${aiReferralEventsHourly.organicSessionsOrHits} + ${bucket.organicHits}`,
|
|
31527
31640
|
updatedAt: finishedAt
|
|
31528
31641
|
}
|
|
31529
31642
|
}).run();
|
|
@@ -31975,7 +32088,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31975
32088
|
let crawlerTotal = 0;
|
|
31976
32089
|
let crawlerSegments = { content: 0, sitemap: 0, robots: 0, asset: 0, other: 0 };
|
|
31977
32090
|
let aiUserFetchTotal = 0;
|
|
31978
|
-
let
|
|
32091
|
+
let aiReferralCounts = aiReferralClassCounts(0, 0, 0);
|
|
31979
32092
|
if (kind === "all" || kind === TrafficEventKinds.crawler) {
|
|
31980
32093
|
const crawlerFilters = [
|
|
31981
32094
|
eq28(crawlerEventsHourly.projectId, project.id),
|
|
@@ -32041,10 +32154,19 @@ async function trafficRoutes(app, opts) {
|
|
|
32041
32154
|
];
|
|
32042
32155
|
if (sourceIdParam) aiFilters.push(eq28(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
32043
32156
|
const aiWhere = and24(...aiFilters);
|
|
32044
|
-
const total = app.db.select({
|
|
32045
|
-
|
|
32157
|
+
const total = app.db.select({
|
|
32158
|
+
total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
32159
|
+
paid: sql12`COALESCE(SUM(${aiReferralEventsHourly.paidSessionsOrHits}), 0)`,
|
|
32160
|
+
organic: sql12`COALESCE(SUM(${aiReferralEventsHourly.organicSessionsOrHits}), 0)`
|
|
32161
|
+
}).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
32162
|
+
aiReferralCounts = aiReferralClassCounts(
|
|
32163
|
+
Number(total?.total ?? 0),
|
|
32164
|
+
Number(total?.paid ?? 0),
|
|
32165
|
+
Number(total?.organic ?? 0)
|
|
32166
|
+
);
|
|
32046
32167
|
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc15(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
32047
32168
|
for (const r of rows) {
|
|
32169
|
+
const counts2 = aiReferralClassCounts(r.sessionsOrHits, r.paidSessionsOrHits, r.organicSessionsOrHits);
|
|
32048
32170
|
events.push({
|
|
32049
32171
|
kind: TrafficEventKinds["ai-referral"],
|
|
32050
32172
|
sourceId: r.sourceId,
|
|
@@ -32055,7 +32177,10 @@ async function trafficRoutes(app, opts) {
|
|
|
32055
32177
|
evidenceType: r.evidenceType,
|
|
32056
32178
|
landingPathNormalized: r.landingPathNormalized,
|
|
32057
32179
|
status: r.status,
|
|
32058
|
-
hits:
|
|
32180
|
+
hits: counts2.total,
|
|
32181
|
+
paidHits: counts2.paid,
|
|
32182
|
+
organicHits: counts2.organic,
|
|
32183
|
+
unknownHits: counts2.unknown
|
|
32059
32184
|
});
|
|
32060
32185
|
}
|
|
32061
32186
|
}
|
|
@@ -32070,7 +32195,10 @@ async function trafficRoutes(app, opts) {
|
|
|
32070
32195
|
crawlerInfraHits: sumInfraHits(crawlerSegments),
|
|
32071
32196
|
crawlerSegments,
|
|
32072
32197
|
aiUserFetchHits: aiUserFetchTotal,
|
|
32073
|
-
aiReferralHits:
|
|
32198
|
+
aiReferralHits: aiReferralCounts.total,
|
|
32199
|
+
aiReferralPaidHits: aiReferralCounts.paid,
|
|
32200
|
+
aiReferralOrganicHits: aiReferralCounts.organic,
|
|
32201
|
+
aiReferralUnknownHits: aiReferralCounts.unknown
|
|
32074
32202
|
},
|
|
32075
32203
|
events: trimmed
|
|
32076
32204
|
};
|
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setTelemetrySource,
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackEvent
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-GV665WOU.js";
|
|
33
33
|
import {
|
|
34
34
|
CliError,
|
|
35
35
|
EXIT_SYSTEM_ERROR,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
saveConfig,
|
|
47
47
|
saveConfigPatch,
|
|
48
48
|
usageError
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-RVH6QPTA.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-WEAXJ25Y.js";
|
|
58
58
|
import {
|
|
59
59
|
BacklinkSources,
|
|
60
60
|
CcReleaseSyncStatuses,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
providerQuotaPolicySchema,
|
|
81
81
|
resolveProviderInput,
|
|
82
82
|
winnabilityClassSchema
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-IQPMK36Y.js";
|
|
84
84
|
|
|
85
85
|
// src/cli.ts
|
|
86
86
|
import { pathToFileURL } from "url";
|
|
@@ -4698,7 +4698,8 @@ function formatEventLine(event) {
|
|
|
4698
4698
|
event.evidenceType,
|
|
4699
4699
|
event.sourceDomain,
|
|
4700
4700
|
event.landingPathNormalized,
|
|
4701
|
-
`${event.hits} hits
|
|
4701
|
+
`${event.hits} hits`,
|
|
4702
|
+
`(paid ${event.paidHits} \xB7 organic ${event.organicHits} \xB7 unclassified ${event.unknownHits})`
|
|
4702
4703
|
].join(" ");
|
|
4703
4704
|
}
|
|
4704
4705
|
}
|
|
@@ -4743,6 +4744,9 @@ async function trafficEvents(project, opts) {
|
|
|
4743
4744
|
console.log(` Crawler hits total (window): ${result.totals.crawlerHits}`);
|
|
4744
4745
|
console.log(` AI user-fetch hits (window): ${result.totals.aiUserFetchHits}`);
|
|
4745
4746
|
console.log(` AI referral sessions (window): ${result.totals.aiReferralHits}`);
|
|
4747
|
+
console.log(` paid: ${result.totals.aiReferralPaidHits}`);
|
|
4748
|
+
console.log(` organic: ${result.totals.aiReferralOrganicHits}`);
|
|
4749
|
+
console.log(` unclassified: ${result.totals.aiReferralUnknownHits} (ingested before paid/organic classification)`);
|
|
4746
4750
|
console.log("");
|
|
4747
4751
|
if (result.events.length === 0) {
|
|
4748
4752
|
console.log("No events in this window.");
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GV665WOU.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-RVH6QPTA.js";
|
|
7
|
+
import "./chunk-WEAXJ25Y.js";
|
|
8
|
+
import "./chunk-IQPMK36Y.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-RVH6QPTA.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-IQPMK36Y.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp/cli.ts
|
|
12
12
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.116.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
@@ -65,27 +65,27 @@
|
|
|
65
65
|
"@types/node-cron": "^3.0.11",
|
|
66
66
|
"tsup": "^8.5.1",
|
|
67
67
|
"tsx": "^4.19.0",
|
|
68
|
-
"@ainyc/canonry-api-client": "0.0.0",
|
|
69
|
-
"@ainyc/canonry-config": "0.0.0",
|
|
70
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
71
68
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
69
|
+
"@ainyc/canonry-config": "0.0.0",
|
|
70
|
+
"@ainyc/canonry-api-client": "0.0.0",
|
|
72
71
|
"@ainyc/canonry-db": "0.0.0",
|
|
73
72
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
73
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
74
|
+
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
74
75
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
75
|
-
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
76
76
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
77
|
-
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
78
77
|
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
79
|
-
"@ainyc/canonry-integration-
|
|
78
|
+
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
80
79
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
80
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
81
81
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
82
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
83
82
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
84
|
-
"@ainyc/canonry-
|
|
85
|
-
"@ainyc/canonry-
|
|
83
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
84
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
85
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
86
86
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
87
|
-
"@ainyc/canonry-provider-
|
|
88
|
-
"@ainyc/canonry-provider-
|
|
87
|
+
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
88
|
+
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
89
89
|
},
|
|
90
90
|
"scripts": {
|
|
91
91
|
"build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",
|