@ainyc/canonry 4.41.1 → 4.43.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/README.md +1 -1
- package/assets/assets/index-jvmhx0du.css +1 -0
- package/assets/assets/{index-JiyuncvN.js → index-kRilKhIW.js} +57 -57
- package/assets/index.html +2 -2
- package/dist/{chunk-PQ2IKBT5.js → chunk-IXP4WGLQ.js} +50 -12
- package/dist/{chunk-3I6Y2PSQ.js → chunk-MITJYCHG.js} +169 -87
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{intelligence-service-FFCEIEOU.js → intelligence-service-HWWNLBZM.js} +1 -1
- package/package.json +8 -8
- package/assets/assets/index-Ca1Lty3H.css +0 -1
package/assets/index.html
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
16
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-kRilKhIW.js"></script>
|
|
16
|
+
<link rel="stylesheet" crossorigin href="./assets/index-jvmhx0du.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|
|
@@ -32,12 +32,13 @@ import {
|
|
|
32
32
|
buildMentionCoverage,
|
|
33
33
|
buildMentionGapScore,
|
|
34
34
|
buildMentionLandscape,
|
|
35
|
+
buildMentionShare,
|
|
35
36
|
buildMovementSummary,
|
|
36
37
|
buildOverviewCompetitors,
|
|
37
38
|
buildProviderScores,
|
|
38
39
|
buildProviderTrends,
|
|
39
40
|
buildRunHistory,
|
|
40
|
-
|
|
41
|
+
buildSuggestedQueries,
|
|
41
42
|
buildVisibilityScore,
|
|
42
43
|
categorizeQueryByIntent,
|
|
43
44
|
ccReleaseSyncs,
|
|
@@ -78,7 +79,7 @@ import {
|
|
|
78
79
|
schedules,
|
|
79
80
|
trafficSources,
|
|
80
81
|
usageCounters
|
|
81
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-MITJYCHG.js";
|
|
82
83
|
import {
|
|
83
84
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
84
85
|
AGENT_PROVIDER_IDS,
|
|
@@ -6428,7 +6429,7 @@ function buildReportActionPlan(input) {
|
|
|
6428
6429
|
action: "Review the recurring external source domains and add the true competitors before the next check.",
|
|
6429
6430
|
why: [
|
|
6430
6431
|
"The report can identify repeated external sources, but it cannot separate competitors from publishers until competitors are configured.",
|
|
6431
|
-
"A clean competitor set makes future share
|
|
6432
|
+
"A clean competitor set makes future mention-share and content-gap reporting easier to explain to clients."
|
|
6432
6433
|
],
|
|
6433
6434
|
evidence: topDomains.map((d) => `${d.domain} appeared in ${d.count} cited source${d.count === 1 ? "" : "s"}`),
|
|
6434
6435
|
successMetric: "Next report separates tracked competitors from independent source domains in the competitor landscape.",
|
|
@@ -7239,17 +7240,23 @@ async function compositeRoutes(app) {
|
|
|
7239
7240
|
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq15(queries.projectId, project.id)).all();
|
|
7240
7241
|
const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
|
|
7241
7242
|
const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
|
|
7242
|
-
const
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7243
|
+
const mentionShareCompetitors = competitorRows.map((c) => ({
|
|
7244
|
+
domain: c.domain,
|
|
7245
|
+
// Single brand token derived from the registrable domain (e.g.
|
|
7246
|
+
// "offers.roofle.com" → "roofle"). Future PR can layer operator-curated
|
|
7247
|
+
// aliases on top via a `competitor_aliases` column.
|
|
7248
|
+
brandTokens: [brandLabelFromDomain(c.domain)].filter((t) => t.length >= 3)
|
|
7249
|
+
}));
|
|
7246
7250
|
const scores = {
|
|
7247
7251
|
mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
|
|
7248
7252
|
visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
+
mentionShare: buildMentionShare(
|
|
7254
|
+
latestSnapshots.map((s) => ({
|
|
7255
|
+
projectMentioned: s.answerMentioned === true,
|
|
7256
|
+
answerText: s.answerText
|
|
7257
|
+
})),
|
|
7258
|
+
{ competitors: mentionShareCompetitors }
|
|
7259
|
+
),
|
|
7253
7260
|
gapQueries: buildGapQueryScore(latestSnapshots),
|
|
7254
7261
|
mentionGaps: buildMentionGapScore(latestSnapshots),
|
|
7255
7262
|
indexCoverage: buildIndexCoverageScore(app, project.id),
|
|
@@ -7281,6 +7288,11 @@ async function compositeRoutes(app) {
|
|
|
7281
7288
|
const attentionItems = buildAttentionItems(insightRows, allRuns);
|
|
7282
7289
|
const sparklineRuns = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => ({ id: r.id, createdAt: r.createdAt, status: r.status }));
|
|
7283
7290
|
const runHistory = buildRunHistory(sparklineRuns, snapshotsByRun);
|
|
7291
|
+
const suggestedQueries = buildSuggestedQueriesFromGsc(
|
|
7292
|
+
app,
|
|
7293
|
+
project.id,
|
|
7294
|
+
projectQueries.map((q) => q.query)
|
|
7295
|
+
);
|
|
7284
7296
|
const result = {
|
|
7285
7297
|
project: formatProject2(project),
|
|
7286
7298
|
latestRun,
|
|
@@ -7295,6 +7307,7 @@ async function compositeRoutes(app) {
|
|
|
7295
7307
|
providerScores,
|
|
7296
7308
|
attentionItems,
|
|
7297
7309
|
runHistory,
|
|
7310
|
+
suggestedQueries,
|
|
7298
7311
|
dateRangeLabel: "All time",
|
|
7299
7312
|
contextLabel: `${project.country} / ${project.language.toUpperCase()}`
|
|
7300
7313
|
};
|
|
@@ -7412,6 +7425,7 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7412
7425
|
model: querySnapshots.model,
|
|
7413
7426
|
citationState: querySnapshots.citationState,
|
|
7414
7427
|
answerMentioned: querySnapshots.answerMentioned,
|
|
7428
|
+
answerText: querySnapshots.answerText,
|
|
7415
7429
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7416
7430
|
citedDomains: querySnapshots.citedDomains
|
|
7417
7431
|
}).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
|
|
@@ -7423,6 +7437,7 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7423
7437
|
model: row.model,
|
|
7424
7438
|
citationState: row.citationState,
|
|
7425
7439
|
answerMentioned: row.answerMentioned,
|
|
7440
|
+
answerText: row.answerText,
|
|
7426
7441
|
competitorOverlap: parseJsonColumn(row.competitorOverlap, []),
|
|
7427
7442
|
citedDomains: parseJsonColumn(row.citedDomains, [])
|
|
7428
7443
|
});
|
|
@@ -7494,6 +7509,29 @@ function summarizeTransitionsFromSnapshots(latest, previous, since) {
|
|
|
7494
7509
|
}
|
|
7495
7510
|
return { since, gained, lost, emerging };
|
|
7496
7511
|
}
|
|
7512
|
+
function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
|
|
7513
|
+
const cutoff = new Date(Date.now() - 28 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
|
|
7514
|
+
const rows = app.db.select({
|
|
7515
|
+
query: gscSearchData.query,
|
|
7516
|
+
impressions: sql6`COALESCE(SUM(${gscSearchData.impressions}), 0)`,
|
|
7517
|
+
clicks: sql6`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
|
|
7518
|
+
// Weighted average: SUM(position * impressions) / SUM(impressions).
|
|
7519
|
+
// NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
|
|
7520
|
+
// which the JS coerces to 0 — caught by the impression floor anyway).
|
|
7521
|
+
avgPosition: sql6`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
|
|
7522
|
+
}).from(gscSearchData).where(and7(
|
|
7523
|
+
eq15(gscSearchData.projectId, projectId),
|
|
7524
|
+
sql6`${gscSearchData.date} >= ${cutoff}`,
|
|
7525
|
+
sql6`${gscSearchData.impressions} > 0`
|
|
7526
|
+
)).groupBy(gscSearchData.query).orderBy(sql6`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
|
|
7527
|
+
const gscRows = rows.map((r) => ({
|
|
7528
|
+
query: r.query,
|
|
7529
|
+
impressions: Number(r.impressions),
|
|
7530
|
+
clicks: Number(r.clicks),
|
|
7531
|
+
avgPosition: Number(r.avgPosition)
|
|
7532
|
+
}));
|
|
7533
|
+
return buildSuggestedQueries(gscRows, { trackedQueries });
|
|
7534
|
+
}
|
|
7497
7535
|
function buildIndexCoverageScore(app, projectId) {
|
|
7498
7536
|
const tooltip = "Percentage of inspected URLs currently indexed. Google Search Console is preferred when available, otherwise Bing Webmaster Tools is used.";
|
|
7499
7537
|
const empty = {
|
|
@@ -26271,7 +26309,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
26271
26309
|
return result;
|
|
26272
26310
|
}
|
|
26273
26311
|
async function backfillInsightsCommand(project, opts) {
|
|
26274
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
26312
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-HWWNLBZM.js");
|
|
26275
26313
|
const config = loadConfig();
|
|
26276
26314
|
const db = createClient(config.database);
|
|
26277
26315
|
migrate(db);
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ContentActions,
|
|
4
4
|
RunKinds,
|
|
5
5
|
__export,
|
|
6
|
+
brandKeyFromText,
|
|
6
7
|
brandLabelFromDomain,
|
|
7
8
|
categorizeSourceWithCompetitors,
|
|
8
9
|
categoryLabel,
|
|
@@ -3589,92 +3590,6 @@ function buildRunHistory(runs2, snapshotsByRunId, limit = DEFAULT_RUN_HISTORY_LI
|
|
|
3589
3590
|
});
|
|
3590
3591
|
}
|
|
3591
3592
|
|
|
3592
|
-
// ../intelligence/src/share-of-voice.ts
|
|
3593
|
-
function sovTone(score) {
|
|
3594
|
-
if (score >= 30) return "positive";
|
|
3595
|
-
if (score >= 10) return "caution";
|
|
3596
|
-
return "negative";
|
|
3597
|
-
}
|
|
3598
|
-
function buildShareOfVoice(snapshots, options) {
|
|
3599
|
-
const tooltip = "Your domain's share of every distinct cited-source slot across the latest run. Subdomain-aware (cited docs.you.com counts for you.com). Distinct from Citation Coverage \u2014 SoV measures how much of the answer real-estate you own, not just whether you appear.";
|
|
3600
|
-
if (snapshots.length === 0) {
|
|
3601
|
-
return {
|
|
3602
|
-
label: "Share of Voice",
|
|
3603
|
-
value: "No data",
|
|
3604
|
-
delta: "Run a sweep first",
|
|
3605
|
-
tone: "neutral",
|
|
3606
|
-
description: "No SoV data yet. Trigger a run to start tracking.",
|
|
3607
|
-
tooltip,
|
|
3608
|
-
trend: []
|
|
3609
|
-
};
|
|
3610
|
-
}
|
|
3611
|
-
const breakdown = computeShareOfVoiceBreakdown(snapshots, options);
|
|
3612
|
-
if (breakdown.totalSlots === 0) {
|
|
3613
|
-
return {
|
|
3614
|
-
label: "Share of Voice",
|
|
3615
|
-
value: "0",
|
|
3616
|
-
delta: "No citations in this run",
|
|
3617
|
-
tone: "neutral",
|
|
3618
|
-
description: "The latest run produced no source-list citations across any provider, so SoV cannot be measured. (Mention Coverage may still be non-zero \u2014 answers can mention you without grounding to a URL.)",
|
|
3619
|
-
tooltip,
|
|
3620
|
-
trend: [],
|
|
3621
|
-
progress: 0
|
|
3622
|
-
};
|
|
3623
|
-
}
|
|
3624
|
-
const { projectSlots, competitorSlots, otherSlots, totalSlots } = breakdown;
|
|
3625
|
-
const score = Math.round(projectSlots / totalSlots * 100);
|
|
3626
|
-
const competitorShare = Math.round(competitorSlots / totalSlots * 100);
|
|
3627
|
-
const otherShare = Math.max(0, 100 - score - competitorShare);
|
|
3628
|
-
const hasCompetitorsConfigured = options.competitorDomains.length > 0;
|
|
3629
|
-
const description = describeBreakdown({
|
|
3630
|
-
projectSlots,
|
|
3631
|
-
competitorSlots,
|
|
3632
|
-
otherSlots,
|
|
3633
|
-
totalSlots,
|
|
3634
|
-
score,
|
|
3635
|
-
competitorShare,
|
|
3636
|
-
otherShare,
|
|
3637
|
-
hasCompetitorsConfigured
|
|
3638
|
-
});
|
|
3639
|
-
return {
|
|
3640
|
-
label: "Share of Voice",
|
|
3641
|
-
value: `${score}`,
|
|
3642
|
-
delta: `${projectSlots} of ${totalSlots} cited slots`,
|
|
3643
|
-
tone: sovTone(score),
|
|
3644
|
-
description,
|
|
3645
|
-
tooltip,
|
|
3646
|
-
trend: [],
|
|
3647
|
-
progress: score
|
|
3648
|
-
};
|
|
3649
|
-
}
|
|
3650
|
-
function computeShareOfVoiceBreakdown(snapshots, options) {
|
|
3651
|
-
let totalSlots = 0;
|
|
3652
|
-
let projectSlots = 0;
|
|
3653
|
-
let competitorSlots = 0;
|
|
3654
|
-
for (const snap of snapshots) {
|
|
3655
|
-
for (const domain of snap.citedDomains) {
|
|
3656
|
-
totalSlots++;
|
|
3657
|
-
if (citedDomainBelongsToProject(domain, options.projectDomains)) {
|
|
3658
|
-
projectSlots++;
|
|
3659
|
-
} else if (citedDomainBelongsToProject(domain, options.competitorDomains)) {
|
|
3660
|
-
competitorSlots++;
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
}
|
|
3664
|
-
const otherSlots = totalSlots - projectSlots - competitorSlots;
|
|
3665
|
-
return { projectSlots, competitorSlots, otherSlots, totalSlots };
|
|
3666
|
-
}
|
|
3667
|
-
function describeBreakdown(parts) {
|
|
3668
|
-
const { projectSlots, competitorSlots, totalSlots, score, competitorShare, otherShare, hasCompetitorsConfigured } = parts;
|
|
3669
|
-
if (!hasCompetitorsConfigured) {
|
|
3670
|
-
return `${projectSlots} of ${totalSlots} cited slots were yours (${score}%). Add tracked competitors to break out the rest.`;
|
|
3671
|
-
}
|
|
3672
|
-
if (competitorSlots === 0) {
|
|
3673
|
-
return `${projectSlots} of ${totalSlots} cited slots were yours (${score}%); no tracked competitors surfaced in the run. The remaining ${otherShare}% goes to unrelated sources.`;
|
|
3674
|
-
}
|
|
3675
|
-
return `You own ${score}% of cited slots; tracked competitors hold ${competitorShare}%; the remaining ${otherShare}% goes to non-competitive sources.`;
|
|
3676
|
-
}
|
|
3677
|
-
|
|
3678
3593
|
// ../intelligence/src/provider-trends.ts
|
|
3679
3594
|
function buildProviderTrends(runs2, snapshotsByRunId, limit = 12) {
|
|
3680
3595
|
const recent = [...runs2].sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, limit).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
@@ -3712,6 +3627,172 @@ function collectProviderKeys(perRun) {
|
|
|
3712
3627
|
return keys;
|
|
3713
3628
|
}
|
|
3714
3629
|
|
|
3630
|
+
// ../intelligence/src/mention-share.ts
|
|
3631
|
+
function buildMentionShare(snapshots, options) {
|
|
3632
|
+
const tooltip = 'When AI answers your tracked queries and names a brand, the % of brand-name-drops that are you vs your tracked competitors. Cleaner than Citation Coverage for "am I winning the conversation".';
|
|
3633
|
+
const emptyBreakdown = {
|
|
3634
|
+
projectMentionSnapshots: 0,
|
|
3635
|
+
competitorMentionSnapshots: 0,
|
|
3636
|
+
perCompetitor: [],
|
|
3637
|
+
snapshotsWithAnswerText: 0,
|
|
3638
|
+
snapshotsTotal: snapshots.length
|
|
3639
|
+
};
|
|
3640
|
+
if (snapshots.length === 0) {
|
|
3641
|
+
return {
|
|
3642
|
+
label: "Mention Share",
|
|
3643
|
+
value: "No data",
|
|
3644
|
+
delta: "Run a sweep first",
|
|
3645
|
+
tone: "neutral",
|
|
3646
|
+
description: "No mention share data yet. Trigger a run to start tracking.",
|
|
3647
|
+
tooltip,
|
|
3648
|
+
trend: [],
|
|
3649
|
+
breakdown: emptyBreakdown
|
|
3650
|
+
};
|
|
3651
|
+
}
|
|
3652
|
+
if (options.competitors.length === 0) {
|
|
3653
|
+
return {
|
|
3654
|
+
label: "Mention Share",
|
|
3655
|
+
value: "Add competitors",
|
|
3656
|
+
delta: "No competitors configured",
|
|
3657
|
+
tone: "neutral",
|
|
3658
|
+
description: "Mention Share is a head-to-head competitive metric \u2014 add tracked competitors to compare brand mention rates.",
|
|
3659
|
+
tooltip,
|
|
3660
|
+
trend: [],
|
|
3661
|
+
breakdown: emptyBreakdown
|
|
3662
|
+
};
|
|
3663
|
+
}
|
|
3664
|
+
let projectMentionSnapshots = 0;
|
|
3665
|
+
let snapshotsWithAnswerText = 0;
|
|
3666
|
+
const competitorCounts = /* @__PURE__ */ new Map();
|
|
3667
|
+
for (const c of options.competitors) competitorCounts.set(c.domain, 0);
|
|
3668
|
+
for (const snap of snapshots) {
|
|
3669
|
+
const text2 = snap.answerText ?? "";
|
|
3670
|
+
if (text2.length === 0) continue;
|
|
3671
|
+
snapshotsWithAnswerText++;
|
|
3672
|
+
if (snap.projectMentioned) projectMentionSnapshots++;
|
|
3673
|
+
const answerBrandKey = brandKeyFromText(text2);
|
|
3674
|
+
for (const competitor of options.competitors) {
|
|
3675
|
+
if (competitorMentioned(text2, answerBrandKey, competitor.brandTokens)) {
|
|
3676
|
+
competitorCounts.set(competitor.domain, (competitorCounts.get(competitor.domain) ?? 0) + 1);
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
const competitorMentionSnapshots = [...competitorCounts.values()].reduce((a, b) => a + b, 0);
|
|
3681
|
+
const denom = projectMentionSnapshots + competitorMentionSnapshots;
|
|
3682
|
+
const score = denom > 0 ? Math.round(projectMentionSnapshots / denom * 100) : 0;
|
|
3683
|
+
const perCompetitor = options.competitors.map((c) => ({
|
|
3684
|
+
domain: c.domain,
|
|
3685
|
+
mentionSnapshots: competitorCounts.get(c.domain) ?? 0,
|
|
3686
|
+
shareOfCompetitiveTotal: competitorMentionSnapshots > 0 ? Math.round((competitorCounts.get(c.domain) ?? 0) / competitorMentionSnapshots * 1e3) / 10 : 0
|
|
3687
|
+
})).filter((row) => row.mentionSnapshots > 0).sort((a, b) => b.mentionSnapshots - a.mentionSnapshots);
|
|
3688
|
+
const breakdown = {
|
|
3689
|
+
projectMentionSnapshots,
|
|
3690
|
+
competitorMentionSnapshots,
|
|
3691
|
+
perCompetitor,
|
|
3692
|
+
snapshotsWithAnswerText,
|
|
3693
|
+
snapshotsTotal: snapshots.length
|
|
3694
|
+
};
|
|
3695
|
+
const description = describe({
|
|
3696
|
+
score,
|
|
3697
|
+
projectMentionSnapshots,
|
|
3698
|
+
competitorMentionSnapshots,
|
|
3699
|
+
perCompetitor
|
|
3700
|
+
});
|
|
3701
|
+
return {
|
|
3702
|
+
label: "Mention Share",
|
|
3703
|
+
value: denom > 0 ? `${score}` : "0",
|
|
3704
|
+
delta: denom > 0 ? `${projectMentionSnapshots} of ${denom} brand mentions` : "No brand mentions in this run",
|
|
3705
|
+
tone: denom > 0 ? mentionShareTone(score) : "neutral",
|
|
3706
|
+
description,
|
|
3707
|
+
tooltip,
|
|
3708
|
+
trend: [],
|
|
3709
|
+
progress: denom > 0 ? score : 0,
|
|
3710
|
+
breakdown
|
|
3711
|
+
};
|
|
3712
|
+
}
|
|
3713
|
+
function mentionShareTone(score) {
|
|
3714
|
+
if (score >= 50) return "positive";
|
|
3715
|
+
if (score >= 25) return "caution";
|
|
3716
|
+
return "negative";
|
|
3717
|
+
}
|
|
3718
|
+
function competitorMentioned(text2, answerBrandKey, brandTokens) {
|
|
3719
|
+
for (const token of brandTokens) {
|
|
3720
|
+
if (token.length < 3) continue;
|
|
3721
|
+
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3722
|
+
if (new RegExp(`\\b${escaped}\\b`, "i").test(text2)) return true;
|
|
3723
|
+
const tokenBrandKey = brandKeyFromText(token);
|
|
3724
|
+
if (tokenBrandKey.length >= 3 && answerBrandKey.includes(tokenBrandKey)) return true;
|
|
3725
|
+
}
|
|
3726
|
+
return false;
|
|
3727
|
+
}
|
|
3728
|
+
function describe(parts) {
|
|
3729
|
+
const { score, projectMentionSnapshots, competitorMentionSnapshots, perCompetitor } = parts;
|
|
3730
|
+
if (projectMentionSnapshots === 0 && competitorMentionSnapshots === 0) {
|
|
3731
|
+
return "No brand mentions detected for you or your tracked competitors in this run.";
|
|
3732
|
+
}
|
|
3733
|
+
if (competitorMentionSnapshots === 0) {
|
|
3734
|
+
return `${projectMentionSnapshots} brand mentions of you, zero competitor mentions \u2014 you own the conversation.`;
|
|
3735
|
+
}
|
|
3736
|
+
const top = perCompetitor[0];
|
|
3737
|
+
if (!top) {
|
|
3738
|
+
return `${score}% of brand mentions are you (${projectMentionSnapshots} of ${projectMentionSnapshots + competitorMentionSnapshots}).`;
|
|
3739
|
+
}
|
|
3740
|
+
return `${score}% of brand mentions are you. Top competitor: ${top.domain} (${top.mentionSnapshots} mentions).`;
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3743
|
+
// ../intelligence/src/suggested-queries.ts
|
|
3744
|
+
var DEFAULT_MIN_IMPRESSIONS = 10;
|
|
3745
|
+
var DEFAULT_LIMIT = 10;
|
|
3746
|
+
function buildSuggestedQueries(gscRows, options) {
|
|
3747
|
+
const minImpressions = options.minImpressions ?? DEFAULT_MIN_IMPRESSIONS;
|
|
3748
|
+
const limit = options.limit ?? DEFAULT_LIMIT;
|
|
3749
|
+
const trackedSet = new Set(options.trackedQueries.map(normalizeQuery));
|
|
3750
|
+
let skippedAlreadyTracked = 0;
|
|
3751
|
+
const candidates = [];
|
|
3752
|
+
for (const row of gscRows) {
|
|
3753
|
+
if (row.impressions < minImpressions) continue;
|
|
3754
|
+
const normalized = normalizeQuery(row.query);
|
|
3755
|
+
if (normalized.length === 0) continue;
|
|
3756
|
+
if (trackedSet.has(normalized)) {
|
|
3757
|
+
skippedAlreadyTracked++;
|
|
3758
|
+
continue;
|
|
3759
|
+
}
|
|
3760
|
+
candidates.push({
|
|
3761
|
+
query: row.query,
|
|
3762
|
+
impressions: row.impressions,
|
|
3763
|
+
clicks: row.clicks,
|
|
3764
|
+
avgPosition: row.avgPosition,
|
|
3765
|
+
reason: buildReason(row)
|
|
3766
|
+
});
|
|
3767
|
+
}
|
|
3768
|
+
candidates.sort((a, b) => b.impressions - a.impressions);
|
|
3769
|
+
const rows = candidates.slice(0, limit);
|
|
3770
|
+
return {
|
|
3771
|
+
rows,
|
|
3772
|
+
totalCandidates: candidates.length,
|
|
3773
|
+
skippedAlreadyTracked
|
|
3774
|
+
};
|
|
3775
|
+
}
|
|
3776
|
+
function normalizeQuery(value) {
|
|
3777
|
+
return value.trim().toLowerCase();
|
|
3778
|
+
}
|
|
3779
|
+
function buildReason(row) {
|
|
3780
|
+
const impressionsLabel = formatImpressions2(row.impressions);
|
|
3781
|
+
if (row.avgPosition <= 10) {
|
|
3782
|
+
return `${impressionsLabel} impressions \xB7 ranks #${Math.round(row.avgPosition)} on Google`;
|
|
3783
|
+
}
|
|
3784
|
+
if (row.avgPosition <= 20) {
|
|
3785
|
+
return `${impressionsLabel} impressions \xB7 ranks #${Math.round(row.avgPosition)} \u2014 close to top 10`;
|
|
3786
|
+
}
|
|
3787
|
+
return `${impressionsLabel} impressions \xB7 ranks #${Math.round(row.avgPosition)}`;
|
|
3788
|
+
}
|
|
3789
|
+
function formatImpressions2(value) {
|
|
3790
|
+
if (value >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
3791
|
+
if (value >= 1e4) return `${Math.round(value / 1e3)}K`;
|
|
3792
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
3793
|
+
return value.toString();
|
|
3794
|
+
}
|
|
3795
|
+
|
|
3715
3796
|
// src/intelligence-service.ts
|
|
3716
3797
|
import crypto from "crypto";
|
|
3717
3798
|
|
|
@@ -4238,9 +4319,10 @@ export {
|
|
|
4238
4319
|
buildProviderScores,
|
|
4239
4320
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
4240
4321
|
buildRunHistory,
|
|
4241
|
-
buildShareOfVoice,
|
|
4242
4322
|
buildProviderTrends,
|
|
4243
4323
|
providerKey,
|
|
4324
|
+
buildMentionShare,
|
|
4325
|
+
buildSuggestedQueries,
|
|
4244
4326
|
createLogger,
|
|
4245
4327
|
IntelligenceService
|
|
4246
4328
|
};
|
package/dist/cli.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
setTelemetrySource,
|
|
22
22
|
showFirstRunNotice,
|
|
23
23
|
trackEvent
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-IXP4WGLQ.js";
|
|
25
25
|
import {
|
|
26
26
|
CliError,
|
|
27
27
|
EXIT_SYSTEM_ERROR,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
migrate,
|
|
45
45
|
projects,
|
|
46
46
|
queries
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-MITJYCHG.js";
|
|
48
48
|
import {
|
|
49
49
|
CcReleaseSyncStatuses,
|
|
50
50
|
CheckScopes,
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IXP4WGLQ.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
6
|
} from "./chunk-JZ2VJW4U.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-MITJYCHG.js";
|
|
8
8
|
import "./chunk-Q7XFJO2V.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.43.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",
|
|
@@ -61,22 +61,22 @@
|
|
|
61
61
|
"tsup": "^8.5.1",
|
|
62
62
|
"tsx": "^4.19.0",
|
|
63
63
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
64
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
65
|
+
"@ainyc/canonry-intelligence": "0.0.0",
|
|
64
66
|
"@ainyc/canonry-config": "0.0.0",
|
|
65
67
|
"@ainyc/canonry-db": "0.0.0",
|
|
66
|
-
"@ainyc/canonry-intelligence": "0.0.0",
|
|
67
|
-
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
68
68
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
69
|
-
"@ainyc/canonry-
|
|
70
|
-
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
69
|
+
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
71
70
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
72
71
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
73
72
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
74
|
-
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
75
73
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
76
74
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
77
|
-
"@ainyc/canonry-provider-
|
|
75
|
+
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
76
|
+
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
77
|
+
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
78
78
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
79
|
-
"@ainyc/canonry-provider-
|
|
79
|
+
"@ainyc/canonry-provider-openai": "0.0.0"
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",
|