@ainyc/canonry 2.14.1 → 2.15.1
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/{index-BwFUCV6e.js → index-D0BgyJJT.js} +99 -99
- package/assets/assets/{index-U2SLimrz.css → index-DMx3Oy9W.css} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-3T64Y7GR.js → chunk-KANIG6ES.js} +69 -20
- package/dist/{chunk-FV6PY5UE.js → chunk-NEDRCOOL.js} +14 -1
- package/dist/{chunk-QTS7VZXN.js → chunk-OX24LLIH.js} +25 -3
- package/dist/cli.js +29 -12
- package/dist/index.js +3 -3
- package/dist/{intelligence-service-AEI46KC5.js → intelligence-service-6S5YKANX.js} +1 -1
- package/dist/mcp.js +2 -2
- package/package.json +4 -4
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-D0BgyJJT.js"></script>
|
|
16
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DMx3Oy9W.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
visibilityStateFromAnswerMentioned,
|
|
61
61
|
windowCutoff,
|
|
62
62
|
wordpressEnvSchema
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-OX24LLIH.js";
|
|
64
64
|
import {
|
|
65
65
|
IntelligenceService,
|
|
66
66
|
agentMemory,
|
|
@@ -98,7 +98,7 @@ import {
|
|
|
98
98
|
runs,
|
|
99
99
|
schedules,
|
|
100
100
|
usageCounters
|
|
101
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-NEDRCOOL.js";
|
|
102
102
|
|
|
103
103
|
// src/telemetry.ts
|
|
104
104
|
import crypto from "crypto";
|
|
@@ -6662,6 +6662,8 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
|
|
|
6662
6662
|
sessions: parseInt(row.metricValues[0].value, 10) || 0,
|
|
6663
6663
|
organicSessions: 0,
|
|
6664
6664
|
// populated by organic-only pass below
|
|
6665
|
+
directSessions: 0,
|
|
6666
|
+
// populated by direct-only pass below
|
|
6665
6667
|
users: parseInt(row.metricValues[1].value, 10) || 0
|
|
6666
6668
|
}));
|
|
6667
6669
|
rows.push(...pageRows);
|
|
@@ -6697,9 +6699,38 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
|
|
|
6697
6699
|
organicOffset += (organicResponse.rows ?? []).length;
|
|
6698
6700
|
if ((organicResponse.rows ?? []).length < 1e4 || organicOffset >= total) break;
|
|
6699
6701
|
}
|
|
6702
|
+
const directMap = /* @__PURE__ */ new Map();
|
|
6703
|
+
let directOffset = 0;
|
|
6704
|
+
let directPageCount = 0;
|
|
6705
|
+
while (directPageCount < GA4_MAX_PAGES) {
|
|
6706
|
+
directPageCount++;
|
|
6707
|
+
const directRequest = {
|
|
6708
|
+
dateRanges: [{ startDate: formatDate(startDate), endDate: formatDate(endDate) }],
|
|
6709
|
+
dimensions: [{ name: "date" }, { name: "landingPagePlusQueryString" }],
|
|
6710
|
+
metrics: [{ name: "sessions" }],
|
|
6711
|
+
dimensionFilter: {
|
|
6712
|
+
filter: {
|
|
6713
|
+
fieldName: "sessionDefaultChannelGrouping",
|
|
6714
|
+
stringFilter: { matchType: "EXACT", value: "Direct" }
|
|
6715
|
+
}
|
|
6716
|
+
},
|
|
6717
|
+
limit: 1e4,
|
|
6718
|
+
offset: directOffset
|
|
6719
|
+
};
|
|
6720
|
+
const directResponse = await runReport(accessToken, propertyId, directRequest);
|
|
6721
|
+
if (!directResponse) break;
|
|
6722
|
+
for (const row of directResponse.rows ?? []) {
|
|
6723
|
+
const key = `${row.dimensionValues[0].value}::${row.dimensionValues[1].value}`;
|
|
6724
|
+
directMap.set(key, parseInt(row.metricValues[0].value, 10) || 0);
|
|
6725
|
+
}
|
|
6726
|
+
const total = directResponse.rowCount ?? 0;
|
|
6727
|
+
directOffset += (directResponse.rows ?? []).length;
|
|
6728
|
+
if ((directResponse.rows ?? []).length < 1e4 || directOffset >= total) break;
|
|
6729
|
+
}
|
|
6700
6730
|
for (const row of rows) {
|
|
6701
6731
|
const key = `${row.date}::${row.landingPage}`;
|
|
6702
6732
|
row.organicSessions = organicMap.get(key) ?? 0;
|
|
6733
|
+
row.directSessions = directMap.get(key) ?? 0;
|
|
6703
6734
|
}
|
|
6704
6735
|
for (const row of rows) {
|
|
6705
6736
|
if (row.date.length === 8 && !row.date.includes("-")) {
|
|
@@ -8665,6 +8696,7 @@ async function ga4Routes(app, opts) {
|
|
|
8665
8696
|
landingPageNormalized: normalizeUrlPath(row.landingPage),
|
|
8666
8697
|
sessions: row.sessions,
|
|
8667
8698
|
organicSessions: row.organicSessions,
|
|
8699
|
+
directSessions: row.directSessions,
|
|
8668
8700
|
users: row.users,
|
|
8669
8701
|
syncedAt: now,
|
|
8670
8702
|
syncRunId: runId
|
|
@@ -8782,6 +8814,9 @@ async function ga4Routes(app, opts) {
|
|
|
8782
8814
|
totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
|
|
8783
8815
|
totalUsers: gaTrafficSummaries.totalUsers
|
|
8784
8816
|
}).from(gaTrafficSummaries).where(eq19(gaTrafficSummaries.projectId, project.id)).get();
|
|
8817
|
+
const directTotalRow = app.db.select({
|
|
8818
|
+
totalDirectSessions: sql5`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
|
|
8819
|
+
}).from(gaTrafficSnapshots).where(and8(...snapshotConditions)).get();
|
|
8785
8820
|
const summaryMeta = app.db.select({
|
|
8786
8821
|
periodStart: gaTrafficSummaries.periodStart,
|
|
8787
8822
|
periodEnd: gaTrafficSummaries.periodEnd
|
|
@@ -8790,6 +8825,7 @@ async function ga4Routes(app, opts) {
|
|
|
8790
8825
|
landingPage: sql5`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
8791
8826
|
sessions: sql5`SUM(${gaTrafficSnapshots.sessions})`,
|
|
8792
8827
|
organicSessions: sql5`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
8828
|
+
directSessions: sql5`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
|
|
8793
8829
|
users: sql5`SUM(${gaTrafficSnapshots.users})`
|
|
8794
8830
|
}).from(gaTrafficSnapshots).where(and8(...snapshotConditions)).groupBy(sql5`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql5`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
|
|
8795
8831
|
const aiReferrals = app.db.select({
|
|
@@ -8812,6 +8848,10 @@ async function ga4Routes(app, opts) {
|
|
|
8812
8848
|
GROUP BY date, source, medium
|
|
8813
8849
|
)`
|
|
8814
8850
|
).get();
|
|
8851
|
+
const aiBySession = app.db.select({
|
|
8852
|
+
sessions: sql5`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
8853
|
+
users: sql5`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
8854
|
+
}).from(gaAiReferrals).where(and8(...aiConditions, eq19(gaAiReferrals.sourceDimension, "session"))).get();
|
|
8815
8855
|
const socialReferrals = app.db.select({
|
|
8816
8856
|
source: gaSocialReferrals.source,
|
|
8817
8857
|
medium: gaSocialReferrals.medium,
|
|
@@ -8825,14 +8865,17 @@ async function ga4Routes(app, opts) {
|
|
|
8825
8865
|
}).from(gaSocialReferrals).where(and8(...socialConditions)).get();
|
|
8826
8866
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq19(gaTrafficSummaries.projectId, project.id)).orderBy(desc9(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
8827
8867
|
const total = summaryRow?.totalSessions ?? 0;
|
|
8868
|
+
const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
|
|
8828
8869
|
return {
|
|
8829
8870
|
totalSessions: total,
|
|
8830
8871
|
totalOrganicSessions: summaryRow?.totalOrganicSessions ?? 0,
|
|
8872
|
+
totalDirectSessions,
|
|
8831
8873
|
totalUsers: summaryRow?.totalUsers ?? 0,
|
|
8832
8874
|
topPages: rows.map((r) => ({
|
|
8833
8875
|
landingPage: r.landingPage,
|
|
8834
8876
|
sessions: r.sessions ?? 0,
|
|
8835
8877
|
organicSessions: r.organicSessions ?? 0,
|
|
8878
|
+
directSessions: r.directSessions ?? 0,
|
|
8836
8879
|
users: r.users ?? 0
|
|
8837
8880
|
})),
|
|
8838
8881
|
aiReferrals: aiReferrals.map((r) => ({
|
|
@@ -8844,6 +8887,8 @@ async function ga4Routes(app, opts) {
|
|
|
8844
8887
|
})),
|
|
8845
8888
|
aiSessionsDeduped: aiDeduped?.sessions ?? 0,
|
|
8846
8889
|
aiUsersDeduped: aiDeduped?.users ?? 0,
|
|
8890
|
+
aiSessionsBySession: aiBySession?.sessions ?? 0,
|
|
8891
|
+
aiUsersBySession: aiBySession?.users ?? 0,
|
|
8847
8892
|
socialReferrals: socialReferrals.map((r) => ({
|
|
8848
8893
|
source: r.source,
|
|
8849
8894
|
medium: r.medium,
|
|
@@ -8855,6 +8900,8 @@ async function ga4Routes(app, opts) {
|
|
|
8855
8900
|
socialUsers: socialTotals?.users ?? 0,
|
|
8856
8901
|
organicSharePct: total > 0 ? Math.round((summaryRow?.totalOrganicSessions ?? 0) / total * 100) : 0,
|
|
8857
8902
|
aiSharePct: total > 0 ? Math.round((aiDeduped?.sessions ?? 0) / total * 100) : 0,
|
|
8903
|
+
aiSharePctBySession: total > 0 ? Math.round((aiBySession?.sessions ?? 0) / total * 100) : 0,
|
|
8904
|
+
directSharePct: total > 0 ? Math.round(totalDirectSessions / total * 100) : 0,
|
|
8858
8905
|
socialSharePct: total > 0 ? Math.round((socialTotals?.sessions ?? 0) / total * 100) : 0,
|
|
8859
8906
|
lastSyncedAt: latestSync?.syncedAt ?? null,
|
|
8860
8907
|
periodStart: (() => {
|
|
@@ -8973,12 +9020,13 @@ async function ga4Routes(app, opts) {
|
|
|
8973
9020
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
8974
9021
|
const sumTotal = (from, to) => app.db.select({ sessions: sql5`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and8(eq19(gaTrafficSnapshots.projectId, project.id), sql5`${gaTrafficSnapshots.date} >= ${from}`, sql5`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
8975
9022
|
const sumOrganic = (from, to) => app.db.select({ sessions: sql5`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and8(eq19(gaTrafficSnapshots.projectId, project.id), sql5`${gaTrafficSnapshots.date} >= ${from}`, sql5`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
8976
|
-
const
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
9023
|
+
const sumDirect = (from, to) => app.db.select({ sessions: sql5`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and8(eq19(gaTrafficSnapshots.projectId, project.id), sql5`${gaTrafficSnapshots.date} >= ${from}`, sql5`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
9024
|
+
const sumAi = (from, to) => app.db.select({ sessions: sql5`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and8(
|
|
9025
|
+
eq19(gaAiReferrals.projectId, project.id),
|
|
9026
|
+
sql5`${gaAiReferrals.date} >= ${from}`,
|
|
9027
|
+
sql5`${gaAiReferrals.date} < ${to}`,
|
|
9028
|
+
eq19(gaAiReferrals.sourceDimension, "session")
|
|
9029
|
+
)).get();
|
|
8982
9030
|
const sumSocial = (from, to) => app.db.select({ sessions: sql5`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and8(eq19(gaSocialReferrals.projectId, project.id), sql5`${gaSocialReferrals.date} >= ${from}`, sql5`${gaSocialReferrals.date} < ${to}`)).get();
|
|
8983
9031
|
const todayStr = fmt(today);
|
|
8984
9032
|
const buildTrend = (sum) => {
|
|
@@ -8988,18 +9036,18 @@ async function ga4Routes(app, opts) {
|
|
|
8988
9036
|
const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
|
|
8989
9037
|
return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
|
|
8990
9038
|
};
|
|
8991
|
-
const aiSourceCurrent = app.db.select({ source:
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
const aiSourcePrev = app.db.select({ source:
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9039
|
+
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql5`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and8(
|
|
9040
|
+
eq19(gaAiReferrals.projectId, project.id),
|
|
9041
|
+
sql5`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
|
|
9042
|
+
sql5`${gaAiReferrals.date} < ${todayStr}`,
|
|
9043
|
+
eq19(gaAiReferrals.sourceDimension, "session")
|
|
9044
|
+
)).groupBy(gaAiReferrals.source).all();
|
|
9045
|
+
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql5`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and8(
|
|
9046
|
+
eq19(gaAiReferrals.projectId, project.id),
|
|
9047
|
+
sql5`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
|
|
9048
|
+
sql5`${gaAiReferrals.date} < ${daysAgo2(7)}`,
|
|
9049
|
+
eq19(gaAiReferrals.sourceDimension, "session")
|
|
9050
|
+
)).groupBy(gaAiReferrals.source).all();
|
|
9003
9051
|
const findBiggestMover = (current, prev) => {
|
|
9004
9052
|
const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
|
|
9005
9053
|
let mover = null;
|
|
@@ -9021,6 +9069,7 @@ async function ga4Routes(app, opts) {
|
|
|
9021
9069
|
organic: buildTrend(sumOrganic),
|
|
9022
9070
|
ai: buildTrend(sumAi),
|
|
9023
9071
|
social: buildTrend(sumSocial),
|
|
9072
|
+
direct: buildTrend(sumDirect),
|
|
9024
9073
|
aiBiggestMover: findBiggestMover(aiSourceCurrent, aiSourcePrev),
|
|
9025
9074
|
socialBiggestMover: findBiggestMover(socialSourceCurrent, socialSourcePrev)
|
|
9026
9075
|
};
|
|
@@ -320,6 +320,13 @@ var gaTrafficSnapshots = sqliteTable("ga_traffic_snapshots", {
|
|
|
320
320
|
landingPageNormalized: text("landing_page_normalized"),
|
|
321
321
|
sessions: integer("sessions").notNull().default(0),
|
|
322
322
|
organicSessions: integer("organic_sessions").notNull().default(0),
|
|
323
|
+
/**
|
|
324
|
+
* Per-page Direct channel sessions. Nullable so existing rows survive
|
|
325
|
+
* the migration; new GA4 sync writes populate it. Distinct from
|
|
326
|
+
* `sessions - organicSessions` because that residual lumps Direct
|
|
327
|
+
* together with social, referral, paid, and email.
|
|
328
|
+
*/
|
|
329
|
+
directSessions: integer("direct_sessions"),
|
|
323
330
|
users: integer("users").notNull().default(0),
|
|
324
331
|
syncedAt: text("synced_at").notNull(),
|
|
325
332
|
syncRunId: text("sync_run_id").references(() => runs.id, { onDelete: "cascade" })
|
|
@@ -1068,7 +1075,13 @@ var MIGRATIONS = [
|
|
|
1068
1075
|
// See plans/ai-attribution-research.md "Step 1 — data hygiene".
|
|
1069
1076
|
`ALTER TABLE ga_traffic_snapshots ADD COLUMN landing_page_normalized TEXT`,
|
|
1070
1077
|
`CREATE INDEX IF NOT EXISTS idx_ga_traffic_page_normalized
|
|
1071
|
-
ON ga_traffic_snapshots(project_id, date, landing_page_normalized)
|
|
1078
|
+
ON ga_traffic_snapshots(project_id, date, landing_page_normalized)`,
|
|
1079
|
+
// v45: Per-page Direct channel sessions on ga_traffic_snapshots. Nullable
|
|
1080
|
+
// so existing rows survive; populated by the GA4 sync writer in a
|
|
1081
|
+
// separate commit. Unblocks an honest channel breakdown for the project
|
|
1082
|
+
// dashboard (organic / social / direct / known-AI) — see
|
|
1083
|
+
// plans/ai-attribution-research.md scope A.
|
|
1084
|
+
`ALTER TABLE ga_traffic_snapshots ADD COLUMN direct_sessions INTEGER`
|
|
1072
1085
|
];
|
|
1073
1086
|
function isDuplicateColumnError(err) {
|
|
1074
1087
|
if (!(err instanceof Error)) return false;
|
|
@@ -1298,18 +1298,26 @@ var ga4SocialReferralDtoSchema = z12.object({
|
|
|
1298
1298
|
var ga4TrafficSummaryDtoSchema = z12.object({
|
|
1299
1299
|
totalSessions: z12.number(),
|
|
1300
1300
|
totalOrganicSessions: z12.number(),
|
|
1301
|
+
/** Direct-channel sessions (sessions with no source — bookmarks, typed URLs, AI-driven traffic with stripped referrer). 0 for legacy rows from before the column was added. */
|
|
1302
|
+
totalDirectSessions: z12.number(),
|
|
1301
1303
|
totalUsers: z12.number(),
|
|
1302
1304
|
topPages: z12.array(z12.object({
|
|
1303
1305
|
landingPage: z12.string(),
|
|
1304
1306
|
sessions: z12.number(),
|
|
1305
1307
|
organicSessions: z12.number(),
|
|
1308
|
+
/** Per-page Direct-channel sessions. 0 for legacy rows. */
|
|
1309
|
+
directSessions: z12.number(),
|
|
1306
1310
|
users: z12.number()
|
|
1307
1311
|
})),
|
|
1308
1312
|
aiReferrals: z12.array(ga4AiReferralDtoSchema),
|
|
1309
|
-
/** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. */
|
|
1313
|
+
/** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. Cross-cutting: can overlap with Direct/Organic/Social via firstUserSource. */
|
|
1310
1314
|
aiSessionsDeduped: z12.number(),
|
|
1311
1315
|
/** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
|
|
1312
1316
|
aiUsersDeduped: z12.number(),
|
|
1317
|
+
/** AI sessions whose CURRENT sessionSource matched an AI engine. Disjoint from Direct/Organic/Social — safe for the channel breakdown. */
|
|
1318
|
+
aiSessionsBySession: z12.number(),
|
|
1319
|
+
/** AI users whose CURRENT sessionSource matched an AI engine. Disjoint from Direct/Organic/Social — safe for the channel breakdown. */
|
|
1320
|
+
aiUsersBySession: z12.number(),
|
|
1313
1321
|
socialReferrals: z12.array(ga4SocialReferralDtoSchema),
|
|
1314
1322
|
/** Total social sessions (session-scoped, no cross-dimension dedup needed). */
|
|
1315
1323
|
socialSessions: z12.number(),
|
|
@@ -1317,8 +1325,12 @@ var ga4TrafficSummaryDtoSchema = z12.object({
|
|
|
1317
1325
|
socialUsers: z12.number(),
|
|
1318
1326
|
/** Organic sessions as a percentage of total sessions (0–100, rounded). */
|
|
1319
1327
|
organicSharePct: z12.number(),
|
|
1320
|
-
/** Deduped AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
1328
|
+
/** Deduped AI sessions as a percentage of total sessions (0–100, rounded). Cross-cutting: can overlap with Direct/Organic/Social. */
|
|
1321
1329
|
aiSharePct: z12.number(),
|
|
1330
|
+
/** Session-source-only AI sessions as a percentage of total sessions (0–100, rounded). Disjoint from Direct/Organic/Social. */
|
|
1331
|
+
aiSharePctBySession: z12.number(),
|
|
1332
|
+
/** Direct-channel sessions as a percentage of total sessions (0–100, rounded). */
|
|
1333
|
+
directSharePct: z12.number(),
|
|
1322
1334
|
/** Social sessions as a percentage of total sessions (0–100, rounded). */
|
|
1323
1335
|
socialSharePct: z12.number(),
|
|
1324
1336
|
lastSyncedAt: z12.string().nullable()
|
|
@@ -1841,9 +1853,19 @@ function dropTrailingSlash(path2) {
|
|
|
1841
1853
|
}
|
|
1842
1854
|
function normalizeUrlPath(input) {
|
|
1843
1855
|
if (input == null) return null;
|
|
1844
|
-
|
|
1856
|
+
let trimmed = input.trim();
|
|
1845
1857
|
if (trimmed === "") return null;
|
|
1858
|
+
trimmed = trimmed.replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
1859
|
+
if (trimmed === "" || trimmed === "/") return "/";
|
|
1846
1860
|
if (trimmed === "(not set)") return null;
|
|
1861
|
+
trimmed = trimmed.replace(/([a-zA-Z0-9])([).]+)$/, "$1");
|
|
1862
|
+
if (trimmed.startsWith("/)") || trimmed.startsWith("/ ")) {
|
|
1863
|
+
trimmed = "/";
|
|
1864
|
+
}
|
|
1865
|
+
if (trimmed.includes(" ")) {
|
|
1866
|
+
trimmed = trimmed.split(" ")[0];
|
|
1867
|
+
}
|
|
1868
|
+
if (trimmed === "" || trimmed === "/") return "/";
|
|
1847
1869
|
let pathPart;
|
|
1848
1870
|
let queryPart;
|
|
1849
1871
|
if (/^https?:\/\//i.test(trimmed)) {
|
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
setGoogleAuthConfig,
|
|
18
18
|
showFirstRunNotice,
|
|
19
19
|
trackEvent
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KANIG6ES.js";
|
|
21
21
|
import {
|
|
22
22
|
CcReleaseSyncStatuses,
|
|
23
23
|
CheckScopes,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
saveConfig,
|
|
46
46
|
saveConfigPatch,
|
|
47
47
|
usageError
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-OX24LLIH.js";
|
|
49
49
|
import {
|
|
50
50
|
apiKeys,
|
|
51
51
|
competitors,
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
projects,
|
|
57
57
|
querySnapshots,
|
|
58
58
|
runs
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-NEDRCOOL.js";
|
|
60
60
|
import "./chunk-MLKGABMK.js";
|
|
61
61
|
|
|
62
62
|
// src/cli.ts
|
|
@@ -162,7 +162,7 @@ Usage: ${spec.usage}`, {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
// src/commands/backfill.ts
|
|
165
|
-
import { and, eq, inArray
|
|
165
|
+
import { and, eq, inArray } from "drizzle-orm";
|
|
166
166
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
167
167
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
168
168
|
const config = loadConfig();
|
|
@@ -304,14 +304,15 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
304
304
|
console.log(` Errors: ${providerErrors}`);
|
|
305
305
|
}
|
|
306
306
|
function backfillNormalizedPaths(db, opts) {
|
|
307
|
-
const baseConditions = [
|
|
307
|
+
const baseConditions = [];
|
|
308
308
|
if (opts?.projectId) {
|
|
309
309
|
baseConditions.push(eq(gaTrafficSnapshots.projectId, opts.projectId));
|
|
310
310
|
}
|
|
311
311
|
const rows = db.select({
|
|
312
312
|
id: gaTrafficSnapshots.id,
|
|
313
|
-
landingPage: gaTrafficSnapshots.landingPage
|
|
314
|
-
|
|
313
|
+
landingPage: gaTrafficSnapshots.landingPage,
|
|
314
|
+
landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
|
|
315
|
+
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and(...baseConditions) : void 0).all();
|
|
315
316
|
let updated = 0;
|
|
316
317
|
let unchanged = 0;
|
|
317
318
|
if (rows.length > 0) {
|
|
@@ -322,6 +323,10 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
322
323
|
unchanged++;
|
|
323
324
|
continue;
|
|
324
325
|
}
|
|
326
|
+
if (row.landingPageNormalized === next) {
|
|
327
|
+
unchanged++;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
325
330
|
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq(gaTrafficSnapshots.id, row.id)).run();
|
|
326
331
|
updated++;
|
|
327
332
|
}
|
|
@@ -371,7 +376,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
371
376
|
console.log(` Unchanged: ${unchanged}`);
|
|
372
377
|
}
|
|
373
378
|
async function backfillInsightsCommand(project, opts) {
|
|
374
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
379
|
+
const { IntelligenceService } = await import("./intelligence-service-6S5YKANX.js");
|
|
375
380
|
const config = loadConfig();
|
|
376
381
|
const db = createClient(config.database);
|
|
377
382
|
migrate(db);
|
|
@@ -2105,11 +2110,16 @@ async function gaAttribution(project, opts) {
|
|
|
2105
2110
|
organicSessions: traffic.totalOrganicSessions,
|
|
2106
2111
|
aiSessions: traffic.aiSessionsDeduped,
|
|
2107
2112
|
aiUsers: traffic.aiUsersDeduped,
|
|
2113
|
+
aiSessionsBySession: traffic.aiSessionsBySession,
|
|
2114
|
+
aiUsersBySession: traffic.aiUsersBySession,
|
|
2108
2115
|
socialSessions: traffic.socialSessions,
|
|
2109
2116
|
socialUsers: traffic.socialUsers,
|
|
2117
|
+
directSessions: traffic.totalDirectSessions,
|
|
2110
2118
|
aiSharePct: traffic.aiSharePct,
|
|
2119
|
+
aiSharePctBySession: traffic.aiSharePctBySession,
|
|
2111
2120
|
socialSharePct: traffic.socialSharePct,
|
|
2112
2121
|
organicSharePct: traffic.organicSharePct,
|
|
2122
|
+
directSharePct: traffic.directSharePct,
|
|
2113
2123
|
aiReferrals: traffic.aiReferrals,
|
|
2114
2124
|
socialReferrals: traffic.socialReferrals,
|
|
2115
2125
|
trend
|
|
@@ -2127,9 +2137,10 @@ async function gaAttribution(project, opts) {
|
|
|
2127
2137
|
console.log();
|
|
2128
2138
|
console.log(" CHANNEL BREAKDOWN 7d trend 30d trend");
|
|
2129
2139
|
console.log(` Organic Search: ${String(traffic.totalOrganicSessions).padEnd(6)} (${String(traffic.organicSharePct).padStart(2)}%) ${fmtTrend(trend.organic.trend7dPct).padEnd(12)} ${fmtTrend(trend.organic.trend30dPct)}`);
|
|
2130
|
-
console.log(` AI Referrals: ${String(traffic.aiSessionsDeduped).padEnd(6)} (${String(traffic.aiSharePct).padStart(2)}%) ${fmtTrend(trend.ai.trend7dPct).padEnd(12)} ${fmtTrend(trend.ai.trend30dPct)}`);
|
|
2131
2140
|
console.log(` Social: ${String(traffic.socialSessions).padEnd(6)} (${String(traffic.socialSharePct).padStart(2)}%) ${fmtTrend(trend.social.trend7dPct).padEnd(12)} ${fmtTrend(trend.social.trend30dPct)}`);
|
|
2132
|
-
|
|
2141
|
+
console.log(` Direct: ${String(traffic.totalDirectSessions).padEnd(6)} (${String(traffic.directSharePct).padStart(2)}%) ${fmtTrend(trend.direct.trend7dPct).padEnd(12)} ${fmtTrend(trend.direct.trend30dPct)}`);
|
|
2142
|
+
console.log(` AI Referrals: ${String(traffic.aiSessionsBySession).padEnd(6)} (${String(traffic.aiSharePctBySession).padStart(2)}%) ${fmtTrend(trend.ai.trend7dPct).padEnd(12)} ${fmtTrend(trend.ai.trend30dPct)} (lower bound \u2014 sessionSource only; referrer-stripped traffic falls under Direct)`);
|
|
2143
|
+
const otherSessions2 = traffic.totalSessions - traffic.totalOrganicSessions - traffic.aiSessionsBySession - traffic.socialSessions - traffic.totalDirectSessions;
|
|
2133
2144
|
if (otherSessions2 > 0) {
|
|
2134
2145
|
const otherPct = traffic.totalSessions > 0 ? Math.round(otherSessions2 / traffic.totalSessions * 100) : 0;
|
|
2135
2146
|
console.log(` Other: ${String(otherSessions2).padEnd(6)} (${String(otherPct).padStart(2)}%)`);
|
|
@@ -2161,11 +2172,16 @@ async function gaAttribution(project, opts) {
|
|
|
2161
2172
|
organicSessions: traffic.totalOrganicSessions,
|
|
2162
2173
|
aiSessions: traffic.aiSessionsDeduped,
|
|
2163
2174
|
aiUsers: traffic.aiUsersDeduped,
|
|
2175
|
+
aiSessionsBySession: traffic.aiSessionsBySession,
|
|
2176
|
+
aiUsersBySession: traffic.aiUsersBySession,
|
|
2164
2177
|
socialSessions: traffic.socialSessions,
|
|
2165
2178
|
socialUsers: traffic.socialUsers,
|
|
2179
|
+
directSessions: traffic.totalDirectSessions,
|
|
2166
2180
|
aiSharePct: traffic.aiSharePct,
|
|
2181
|
+
aiSharePctBySession: traffic.aiSharePctBySession,
|
|
2167
2182
|
socialSharePct: traffic.socialSharePct,
|
|
2168
2183
|
organicSharePct: traffic.organicSharePct,
|
|
2184
|
+
directSharePct: traffic.directSharePct,
|
|
2169
2185
|
aiReferrals: traffic.aiReferrals,
|
|
2170
2186
|
socialReferrals: traffic.socialReferrals,
|
|
2171
2187
|
periodStart: traffic.periodStart,
|
|
@@ -2184,9 +2200,10 @@ async function gaAttribution(project, opts) {
|
|
|
2184
2200
|
console.log();
|
|
2185
2201
|
console.log(" CHANNEL BREAKDOWN");
|
|
2186
2202
|
console.log(` Organic Search: ${traffic.totalOrganicSessions} sessions (${traffic.organicSharePct}%)`);
|
|
2187
|
-
console.log(` AI Referrals: ${traffic.aiSessionsDeduped} sessions (${traffic.aiSharePct}%)`);
|
|
2188
2203
|
console.log(` Social: ${traffic.socialSessions} sessions (${traffic.socialSharePct}%)`);
|
|
2189
|
-
|
|
2204
|
+
console.log(` Direct: ${traffic.totalDirectSessions} sessions (${traffic.directSharePct}%)`);
|
|
2205
|
+
console.log(` AI Referrals: ${traffic.aiSessionsBySession} sessions (${traffic.aiSharePctBySession}%) (lower bound \u2014 sessionSource only; referrer-stripped traffic falls under Direct)`);
|
|
2206
|
+
const otherSessions = traffic.totalSessions - traffic.totalOrganicSessions - traffic.aiSessionsBySession - traffic.socialSessions - traffic.totalDirectSessions;
|
|
2190
2207
|
if (otherSessions > 0) {
|
|
2191
2208
|
const otherPct = traffic.totalSessions > 0 ? Math.round(otherSessions / traffic.totalSessions * 100) : 0;
|
|
2192
2209
|
console.log(` Other: ${otherSessions} sessions (${otherPct}%)`);
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KANIG6ES.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-OX24LLIH.js";
|
|
7
|
+
import "./chunk-NEDRCOOL.js";
|
|
8
8
|
import "./chunk-MLKGABMK.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
package/dist/mcp.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
projectUpsertRequestSchema,
|
|
11
11
|
runTriggerRequestSchema,
|
|
12
12
|
scheduleUpsertRequestSchema
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OX24LLIH.js";
|
|
14
14
|
import "./chunk-MLKGABMK.js";
|
|
15
15
|
|
|
16
16
|
// src/mcp/cli.ts
|
|
@@ -580,7 +580,7 @@ var canonryMcpTools = [
|
|
|
580
580
|
defineTool({
|
|
581
581
|
name: "canonry_ga_attribution_trend",
|
|
582
582
|
title: "Get GA attribution trend",
|
|
583
|
-
description: "Get per-channel attribution trends for organic, AI, social, and total sessions.",
|
|
583
|
+
description: "Get per-channel attribution trends for organic, AI, social, direct, and total sessions.",
|
|
584
584
|
access: "read",
|
|
585
585
|
tier: "ga",
|
|
586
586
|
inputSchema: projectInputSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.1",
|
|
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",
|
|
@@ -60,17 +60,17 @@
|
|
|
60
60
|
"tsup": "^8.5.1",
|
|
61
61
|
"tsx": "^4.19.0",
|
|
62
62
|
"@ainyc/canonry-config": "0.0.0",
|
|
63
|
-
"@ainyc/canonry-api-routes": "0.0.0",
|
|
64
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
65
63
|
"@ainyc/canonry-db": "0.0.0",
|
|
66
64
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
65
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
66
|
+
"@ainyc/canonry-api-routes": "0.0.0",
|
|
67
67
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
68
68
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
69
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
69
70
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
70
71
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
71
72
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
72
73
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
73
|
-
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
74
74
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
75
75
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
76
76
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|