@ainyc/canonry 4.13.1 → 4.14.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/{index-BofSsfDl.js → index-B6Mi9Fd1.js} +122 -122
- package/assets/index.html +1 -1
- package/dist/{chunk-LNRDWAG3.js → chunk-5NYG5EC7.js} +1 -1
- package/dist/{chunk-YDGT5CAY.js → chunk-6QTH5NS5.js} +71 -4
- package/dist/{chunk-DCE3B6KD.js → chunk-7HBZCGRL.js} +18 -2
- package/dist/{chunk-RIGQFQJJ.js → chunk-UQHWSCTE.js} +240 -73
- package/dist/cli.js +58 -26
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-NT24OLLA.js → intelligence-service-BCKXIKIL.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
configExists,
|
|
5
5
|
loadConfig,
|
|
6
6
|
saveConfigPatch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5NYG5EC7.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
10
10
|
IntelligenceService,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
schedules,
|
|
66
66
|
trafficSources,
|
|
67
67
|
usageCounters
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-7HBZCGRL.js";
|
|
69
69
|
import {
|
|
70
70
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
71
71
|
AGENT_PROVIDER_IDS,
|
|
@@ -108,6 +108,11 @@ import {
|
|
|
108
108
|
emptyCitationVisibility,
|
|
109
109
|
extractAnswerMentions,
|
|
110
110
|
findDuplicateLocationLabels,
|
|
111
|
+
formatDate,
|
|
112
|
+
formatDateRange,
|
|
113
|
+
formatIsoDate,
|
|
114
|
+
formatNumber,
|
|
115
|
+
formatRatio,
|
|
111
116
|
getProviderLocationHandling,
|
|
112
117
|
hasLocationLabel,
|
|
113
118
|
internalError,
|
|
@@ -146,15 +151,18 @@ import {
|
|
|
146
151
|
visibilityStateFromAnswerMentioned,
|
|
147
152
|
windowCutoff,
|
|
148
153
|
wordpressEnvSchema
|
|
149
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-6QTH5NS5.js";
|
|
150
155
|
|
|
151
156
|
// src/telemetry.ts
|
|
152
157
|
import crypto from "crypto";
|
|
158
|
+
import os from "os";
|
|
153
159
|
import { createRequire } from "module";
|
|
154
160
|
var _require = createRequire(import.meta.url);
|
|
155
161
|
var { version: VERSION } = _require("../package.json");
|
|
156
162
|
var TELEMETRY_ENDPOINT = "https://ainyc.ai/api/telemetry";
|
|
157
163
|
var TIMEOUT_MS = 3e3;
|
|
164
|
+
var ANON_ID_ENV_VAR = "CANONRY_ANONYMOUS_ID";
|
|
165
|
+
var ANON_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
158
166
|
function isTelemetryEnabled() {
|
|
159
167
|
if (process.env.CANONRY_TELEMETRY_DISABLED === "1") return false;
|
|
160
168
|
if (process.env.DO_NOT_TRACK === "1") return false;
|
|
@@ -168,18 +176,66 @@ function isTelemetryEnabled() {
|
|
|
168
176
|
}
|
|
169
177
|
}
|
|
170
178
|
function getOrCreateAnonymousId() {
|
|
171
|
-
|
|
179
|
+
const fromEnv = readEnvAnonymousId();
|
|
180
|
+
if (fromEnv) return fromEnv;
|
|
181
|
+
if (configExists()) {
|
|
182
|
+
try {
|
|
183
|
+
const config = loadConfig();
|
|
184
|
+
if (config.anonymousId) return config.anonymousId;
|
|
185
|
+
const id = crypto.randomUUID();
|
|
186
|
+
config.anonymousId = id;
|
|
187
|
+
try {
|
|
188
|
+
saveConfigPatch(config);
|
|
189
|
+
} catch {
|
|
190
|
+
return getDeterministicAnonymousId();
|
|
191
|
+
}
|
|
192
|
+
return id;
|
|
193
|
+
} catch {
|
|
194
|
+
return getDeterministicAnonymousId();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return getDeterministicAnonymousId();
|
|
198
|
+
}
|
|
199
|
+
function readEnvAnonymousId() {
|
|
200
|
+
const raw = process.env[ANON_ID_ENV_VAR]?.trim();
|
|
201
|
+
if (!raw) return void 0;
|
|
202
|
+
if (!ANON_ID_PATTERN.test(raw)) {
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
return raw.toLowerCase();
|
|
206
|
+
}
|
|
207
|
+
function getDeterministicAnonymousId() {
|
|
172
208
|
try {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
209
|
+
const hostname = os.hostname() || "";
|
|
210
|
+
const mac = firstNonInternalMac();
|
|
211
|
+
const seed = `canonry-anon:${hostname}:${mac}`;
|
|
212
|
+
const hex = crypto.createHash("sha256").update(seed).digest("hex");
|
|
213
|
+
const a = hex.slice(0, 8);
|
|
214
|
+
const b = hex.slice(8, 12);
|
|
215
|
+
const c = "5" + hex.slice(13, 16);
|
|
216
|
+
const dHi = (parseInt(hex.slice(16, 18), 16) & 63 | 128).toString(16).padStart(2, "0");
|
|
217
|
+
const d = dHi + hex.slice(18, 20);
|
|
218
|
+
const e = hex.slice(20, 32);
|
|
219
|
+
return `${a}-${b}-${c}-${d}-${e}`;
|
|
179
220
|
} catch {
|
|
180
221
|
return void 0;
|
|
181
222
|
}
|
|
182
223
|
}
|
|
224
|
+
function firstNonInternalMac() {
|
|
225
|
+
try {
|
|
226
|
+
const interfaces = os.networkInterfaces();
|
|
227
|
+
for (const ifaces of Object.values(interfaces)) {
|
|
228
|
+
if (!ifaces) continue;
|
|
229
|
+
for (const iface of ifaces) {
|
|
230
|
+
if (iface.internal) continue;
|
|
231
|
+
if (!iface.mac || iface.mac === "00:00:00:00:00:00") continue;
|
|
232
|
+
return iface.mac;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
return "no-mac";
|
|
238
|
+
}
|
|
183
239
|
function isFirstRun() {
|
|
184
240
|
if (!configExists()) return false;
|
|
185
241
|
try {
|
|
@@ -2584,16 +2640,6 @@ var COLORS = {
|
|
|
2584
2640
|
function escapeHtml(value) {
|
|
2585
2641
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2586
2642
|
}
|
|
2587
|
-
function formatRatio(value) {
|
|
2588
|
-
if (!Number.isFinite(value) || value === 0) return "0%";
|
|
2589
|
-
return `${(value * 100).toFixed(1)}%`;
|
|
2590
|
-
}
|
|
2591
|
-
function formatNumber(value) {
|
|
2592
|
-
if (!Number.isFinite(value)) return "\u2014";
|
|
2593
|
-
if (Math.abs(value) >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
2594
|
-
if (Math.abs(value) >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
2595
|
-
return value.toLocaleString("en-US");
|
|
2596
|
-
}
|
|
2597
2643
|
function summarizeQueryParams(params) {
|
|
2598
2644
|
const keys = Array.from(params.keys());
|
|
2599
2645
|
const total = keys.length;
|
|
@@ -2636,23 +2682,6 @@ function formatLandingPageHtml(raw) {
|
|
|
2636
2682
|
if (!summary) return pathHtml;
|
|
2637
2683
|
return `${pathHtml}<span class="page-query" title="${escapeHtml(value)}">${escapeHtml(summary)}</span>`;
|
|
2638
2684
|
}
|
|
2639
|
-
function formatDate(iso) {
|
|
2640
|
-
if (!iso) return "\u2014";
|
|
2641
|
-
try {
|
|
2642
|
-
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
|
2643
|
-
const options = { month: "short", day: "numeric", year: "numeric" };
|
|
2644
|
-
const d = dateOnly && dateOnly[1] && dateOnly[2] && dateOnly[3] ? new Date(Date.UTC(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))) : new Date(iso);
|
|
2645
|
-
if (Number.isNaN(d.getTime())) return iso;
|
|
2646
|
-
return d.toLocaleDateString("en-US", dateOnly ? { ...options, timeZone: "UTC" } : options);
|
|
2647
|
-
} catch {
|
|
2648
|
-
return iso;
|
|
2649
|
-
}
|
|
2650
|
-
}
|
|
2651
|
-
function formatDateRange(start, end) {
|
|
2652
|
-
if (!start && !end) return "";
|
|
2653
|
-
if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
|
|
2654
|
-
return formatDate(start || end);
|
|
2655
|
-
}
|
|
2656
2685
|
function gscDateRange(report) {
|
|
2657
2686
|
const summary = report.executiveSummary.gsc;
|
|
2658
2687
|
const gsc = report.gsc;
|
|
@@ -3461,8 +3490,32 @@ table.report-table td .badge {
|
|
|
3461
3490
|
.client-bar-row { grid-template-columns: 100px 1fr 100px; gap: 10px; }
|
|
3462
3491
|
}
|
|
3463
3492
|
@media print {
|
|
3464
|
-
|
|
3465
|
-
|
|
3493
|
+
@page { margin: 0.5in; }
|
|
3494
|
+
html, body {
|
|
3495
|
+
background: ${COLORS.bg};
|
|
3496
|
+
color: ${COLORS.text};
|
|
3497
|
+
-webkit-print-color-adjust: exact;
|
|
3498
|
+
print-color-adjust: exact;
|
|
3499
|
+
}
|
|
3500
|
+
.container { max-width: none; padding: 0; }
|
|
3501
|
+
section.report-section,
|
|
3502
|
+
.executive-hero,
|
|
3503
|
+
.headline-card,
|
|
3504
|
+
.hero-proof,
|
|
3505
|
+
.client-hero,
|
|
3506
|
+
.client-metric-tile,
|
|
3507
|
+
.client-card,
|
|
3508
|
+
.client-note,
|
|
3509
|
+
.chart-card,
|
|
3510
|
+
.action-card,
|
|
3511
|
+
.insight-card,
|
|
3512
|
+
.source-bar-row,
|
|
3513
|
+
.client-bar-row,
|
|
3514
|
+
tr,
|
|
3515
|
+
table { break-inside: avoid; }
|
|
3516
|
+
h1, h2, h3, .eyebrow { break-after: avoid; }
|
|
3517
|
+
.footer { margin-top: 32px; }
|
|
3518
|
+
.footer a { color: ${COLORS.text}; }
|
|
3466
3519
|
}
|
|
3467
3520
|
`;
|
|
3468
3521
|
function section(opts, body) {
|
|
@@ -4634,7 +4687,7 @@ function renderReportHtml(report, opts = {}) {
|
|
|
4634
4687
|
<div class="subtitle">${escapeHtml(report.meta.project.canonicalDomain)} \xB7 ${escapeHtml(report.meta.project.country)} / ${escapeHtml(report.meta.project.language.toUpperCase())}${renderHeaderLocationFragment(report.meta.location)} \xB7 Generated ${formatDate(report.meta.generatedAt)}</div>
|
|
4635
4688
|
</header>
|
|
4636
4689
|
${sections}
|
|
4637
|
-
<footer class="footer">Generated by canonry \xB7 ${escapeHtml(report.meta.generatedAt)}</footer>
|
|
4690
|
+
<footer class="footer">Generated by <a href="https://canonry.ai">canonry</a> \xB7 ${escapeHtml(formatIsoDate(report.meta.generatedAt))}</footer>
|
|
4638
4691
|
</div>
|
|
4639
4692
|
<script type="application/json" id="canonry-report-data">${json}</script>
|
|
4640
4693
|
</body>
|
|
@@ -11025,6 +11078,7 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
|
|
|
11025
11078
|
{ name: "date" },
|
|
11026
11079
|
{ name: sourceDim },
|
|
11027
11080
|
{ name: mediumDim },
|
|
11081
|
+
{ name: "sessionDefaultChannelGroup" },
|
|
11028
11082
|
{ name: "landingPagePlusQueryString" }
|
|
11029
11083
|
],
|
|
11030
11084
|
metrics: [
|
|
@@ -11050,7 +11104,8 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
|
|
|
11050
11104
|
date: row.dimensionValues[0].value,
|
|
11051
11105
|
source: row.dimensionValues[1].value,
|
|
11052
11106
|
medium: row.dimensionValues[2].value,
|
|
11053
|
-
|
|
11107
|
+
channelGroup: row.dimensionValues[3]?.value ?? "(not set)",
|
|
11108
|
+
landingPage: row.dimensionValues[4]?.value ?? "(not set)",
|
|
11054
11109
|
sessions: parseInt(row.metricValues[0].value, 10) || 0,
|
|
11055
11110
|
users: parseInt(row.metricValues[1].value, 10) || 0,
|
|
11056
11111
|
sourceDimension: dimLabel
|
|
@@ -11063,7 +11118,7 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
|
|
|
11063
11118
|
}
|
|
11064
11119
|
const deduped = /* @__PURE__ */ new Map();
|
|
11065
11120
|
for (const row of rows) {
|
|
11066
|
-
const key = `${row.date}::${row.source}::${row.medium}::${row.sourceDimension}::${row.landingPage}`;
|
|
11121
|
+
const key = `${row.date}::${row.source}::${row.medium}::${row.sourceDimension}::${row.channelGroup}::${row.landingPage}`;
|
|
11067
11122
|
const existing = deduped.get(key);
|
|
11068
11123
|
if (!existing) {
|
|
11069
11124
|
deduped.set(key, row);
|
|
@@ -12472,10 +12527,10 @@ async function bingRoutes(app, opts) {
|
|
|
12472
12527
|
// ../api-routes/src/cdp.ts
|
|
12473
12528
|
import fs from "fs";
|
|
12474
12529
|
import path from "path";
|
|
12475
|
-
import
|
|
12530
|
+
import os2 from "os";
|
|
12476
12531
|
import { eq as eq20, and as and8 } from "drizzle-orm";
|
|
12477
12532
|
function getScreenshotDir() {
|
|
12478
|
-
return path.join(
|
|
12533
|
+
return path.join(os2.homedir(), ".canonry", "screenshots");
|
|
12479
12534
|
}
|
|
12480
12535
|
async function cdpRoutes(app, opts) {
|
|
12481
12536
|
app.get("/screenshots/:snapshotId", async (request, reply) => {
|
|
@@ -12657,6 +12712,37 @@ function formatSharePct(numerator, total) {
|
|
|
12657
12712
|
if (rounded === 0) return "<1%";
|
|
12658
12713
|
return `${rounded}%`;
|
|
12659
12714
|
}
|
|
12715
|
+
var SOCIAL_CHANNEL_GROUPS2 = /* @__PURE__ */ new Set(["Organic Social", "Paid Social"]);
|
|
12716
|
+
function buildChannelBreakdown(input) {
|
|
12717
|
+
const aiSessions = [...input.aiSessionsByChannelGroup.values()].reduce((sum, sessions) => sum + sessions, 0);
|
|
12718
|
+
const aiOrganicOverlap = Math.min(input.organicSessions, input.aiSessionsByChannelGroup.get("Organic Search") ?? 0);
|
|
12719
|
+
const aiSocialOverlap = Math.min(
|
|
12720
|
+
input.socialSessions,
|
|
12721
|
+
[...input.aiSessionsByChannelGroup.entries()].filter(([channelGroup]) => SOCIAL_CHANNEL_GROUPS2.has(channelGroup)).reduce((sum, [, sessions]) => sum + sessions, 0)
|
|
12722
|
+
);
|
|
12723
|
+
const aiDirectOverlap = Math.min(input.directSessions, input.aiSessionsByChannelGroup.get("Direct") ?? 0);
|
|
12724
|
+
const organicSessions = Math.max(0, input.organicSessions - aiOrganicOverlap);
|
|
12725
|
+
const socialSessions = Math.max(0, input.socialSessions - aiSocialOverlap);
|
|
12726
|
+
const directSessions = Math.max(0, input.directSessions - aiDirectOverlap);
|
|
12727
|
+
const coveredSessions = organicSessions + socialSessions + directSessions + aiSessions;
|
|
12728
|
+
const otherSessions = Math.max(0, input.totalSessions - coveredSessions);
|
|
12729
|
+
const bucket = (sessions) => ({
|
|
12730
|
+
sessions,
|
|
12731
|
+
sharePct: input.totalSessions > 0 ? Math.round(sessions / input.totalSessions * 100) : 0,
|
|
12732
|
+
sharePctDisplay: formatSharePct(sessions, input.totalSessions)
|
|
12733
|
+
});
|
|
12734
|
+
return {
|
|
12735
|
+
organic: bucket(organicSessions),
|
|
12736
|
+
social: bucket(socialSessions),
|
|
12737
|
+
direct: bucket(directSessions),
|
|
12738
|
+
ai: bucket(aiSessions),
|
|
12739
|
+
other: {
|
|
12740
|
+
sessions: otherSessions,
|
|
12741
|
+
sharePct: input.totalSessions > 0 ? Math.round(otherSessions / input.totalSessions * 100) : 0,
|
|
12742
|
+
sharePctDisplay: input.totalSessions <= 0 && coveredSessions > 0 ? "\u2014" : formatSharePct(otherSessions, input.totalSessions)
|
|
12743
|
+
}
|
|
12744
|
+
};
|
|
12745
|
+
}
|
|
12660
12746
|
function pickWinningDimension(rows, tupleKey) {
|
|
12661
12747
|
const winners = /* @__PURE__ */ new Map();
|
|
12662
12748
|
for (const row of rows) {
|
|
@@ -12945,6 +13031,7 @@ async function ga4Routes(app, opts) {
|
|
|
12945
13031
|
source: row.source,
|
|
12946
13032
|
medium: row.medium,
|
|
12947
13033
|
sourceDimension: row.sourceDimension,
|
|
13034
|
+
channelGroup: row.channelGroup,
|
|
12948
13035
|
landingPage: row.landingPage,
|
|
12949
13036
|
landingPageNormalized: normalizeUrlPath(row.landingPage),
|
|
12950
13037
|
sessions: row.sessions,
|
|
@@ -13136,10 +13223,18 @@ async function ga4Routes(app, opts) {
|
|
|
13136
13223
|
GROUP BY date, source, medium
|
|
13137
13224
|
)`
|
|
13138
13225
|
).get();
|
|
13139
|
-
const
|
|
13226
|
+
const aiBySessionRows = app.db.select({
|
|
13227
|
+
channelGroup: gaAiReferrals.channelGroup,
|
|
13140
13228
|
sessions: sql5`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
13141
13229
|
users: sql5`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
13142
|
-
}).from(gaAiReferrals).where(and9(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).
|
|
13230
|
+
}).from(gaAiReferrals).where(and9(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
|
|
13231
|
+
const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
|
|
13232
|
+
let aiBySessionUsers = 0;
|
|
13233
|
+
for (const row of aiBySessionRows) {
|
|
13234
|
+
aiSessionsByChannelGroup.set(row.channelGroup, row.sessions ?? 0);
|
|
13235
|
+
aiBySessionUsers += row.users ?? 0;
|
|
13236
|
+
}
|
|
13237
|
+
const aiBySessionSessions = [...aiSessionsByChannelGroup.values()].reduce((sum, sessions) => sum + sessions, 0);
|
|
13143
13238
|
const socialReferrals = app.db.select({
|
|
13144
13239
|
source: gaSocialReferrals.source,
|
|
13145
13240
|
medium: gaSocialReferrals.medium,
|
|
@@ -13154,9 +13249,18 @@ async function ga4Routes(app, opts) {
|
|
|
13154
13249
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
13155
13250
|
const total = summaryRow?.totalSessions ?? 0;
|
|
13156
13251
|
const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
|
|
13252
|
+
const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
|
|
13253
|
+
const socialSessions = socialTotals?.sessions ?? 0;
|
|
13254
|
+
const channelBreakdown = buildChannelBreakdown({
|
|
13255
|
+
totalSessions: total,
|
|
13256
|
+
organicSessions: totalOrganicSessions,
|
|
13257
|
+
socialSessions,
|
|
13258
|
+
directSessions: totalDirectSessions,
|
|
13259
|
+
aiSessionsByChannelGroup
|
|
13260
|
+
});
|
|
13157
13261
|
return {
|
|
13158
13262
|
totalSessions: total,
|
|
13159
|
-
totalOrganicSessions
|
|
13263
|
+
totalOrganicSessions,
|
|
13160
13264
|
totalDirectSessions,
|
|
13161
13265
|
totalUsers: summaryRow?.totalUsers ?? 0,
|
|
13162
13266
|
topPages: rows.map((r) => ({
|
|
@@ -13183,8 +13287,8 @@ async function ga4Routes(app, opts) {
|
|
|
13183
13287
|
})),
|
|
13184
13288
|
aiSessionsDeduped: aiDeduped?.sessions ?? 0,
|
|
13185
13289
|
aiUsersDeduped: aiDeduped?.users ?? 0,
|
|
13186
|
-
aiSessionsBySession:
|
|
13187
|
-
aiUsersBySession:
|
|
13290
|
+
aiSessionsBySession: aiBySessionSessions,
|
|
13291
|
+
aiUsersBySession: aiBySessionUsers,
|
|
13188
13292
|
socialReferrals: socialReferrals.map((r) => ({
|
|
13189
13293
|
source: r.source,
|
|
13190
13294
|
medium: r.medium,
|
|
@@ -13192,18 +13296,22 @@ async function ga4Routes(app, opts) {
|
|
|
13192
13296
|
sessions: r.sessions ?? 0,
|
|
13193
13297
|
users: r.users ?? 0
|
|
13194
13298
|
})),
|
|
13195
|
-
socialSessions
|
|
13299
|
+
socialSessions,
|
|
13196
13300
|
socialUsers: socialTotals?.users ?? 0,
|
|
13197
|
-
|
|
13301
|
+
channelBreakdown,
|
|
13302
|
+
organicSharePct: total > 0 ? Math.round(totalOrganicSessions / total * 100) : 0,
|
|
13198
13303
|
aiSharePct: total > 0 ? Math.round((aiDeduped?.sessions ?? 0) / total * 100) : 0,
|
|
13199
|
-
aiSharePctBySession: total > 0 ? Math.round(
|
|
13304
|
+
aiSharePctBySession: total > 0 ? Math.round(aiBySessionSessions / total * 100) : 0,
|
|
13200
13305
|
directSharePct: total > 0 ? Math.round(totalDirectSessions / total * 100) : 0,
|
|
13201
|
-
socialSharePct: total > 0 ? Math.round(
|
|
13202
|
-
|
|
13306
|
+
socialSharePct: total > 0 ? Math.round(socialSessions / total * 100) : 0,
|
|
13307
|
+
otherSessions: channelBreakdown.other.sessions,
|
|
13308
|
+
otherSharePct: channelBreakdown.other.sharePct,
|
|
13309
|
+
otherSharePctDisplay: channelBreakdown.other.sharePctDisplay,
|
|
13310
|
+
organicSharePctDisplay: formatSharePct(totalOrganicSessions, total),
|
|
13203
13311
|
aiSharePctDisplay: formatSharePct(aiDeduped?.sessions ?? 0, total),
|
|
13204
|
-
aiSharePctBySessionDisplay: formatSharePct(
|
|
13312
|
+
aiSharePctBySessionDisplay: formatSharePct(aiBySessionSessions, total),
|
|
13205
13313
|
directSharePctDisplay: formatSharePct(totalDirectSessions, total),
|
|
13206
|
-
socialSharePctDisplay: formatSharePct(
|
|
13314
|
+
socialSharePctDisplay: formatSharePct(socialSessions, total),
|
|
13207
13315
|
lastSyncedAt: latestSync?.syncedAt ?? null,
|
|
13208
13316
|
periodStart: (() => {
|
|
13209
13317
|
const start = cutoffDate ?? summaryMeta?.periodStart ?? null;
|
|
@@ -15049,13 +15157,13 @@ import crypto18 from "crypto";
|
|
|
15049
15157
|
import { and as and11, asc as asc2, desc as desc11, eq as eq22, sql as sql6 } from "drizzle-orm";
|
|
15050
15158
|
|
|
15051
15159
|
// ../integration-commoncrawl/src/constants.ts
|
|
15052
|
-
import
|
|
15160
|
+
import os3 from "os";
|
|
15053
15161
|
import path2 from "path";
|
|
15054
15162
|
var CC_BASE_URL = "https://data.commoncrawl.org/projects/hyperlinkgraph";
|
|
15055
|
-
var PLUGIN_DIR = path2.join(
|
|
15163
|
+
var PLUGIN_DIR = path2.join(os3.homedir(), ".canonry", "plugins");
|
|
15056
15164
|
var PLUGIN_PKG_JSON = path2.join(PLUGIN_DIR, "package.json");
|
|
15057
15165
|
var DUCKDB_SPEC = process.env.CANONRY_DUCKDB_SPEC ?? "@duckdb/node-api@1.4.4-r.3";
|
|
15058
|
-
var CC_CACHE_DIR = process.env.CANONRY_CC_CACHE_DIR ?? path2.join(
|
|
15166
|
+
var CC_CACHE_DIR = process.env.CANONRY_CC_CACHE_DIR ?? path2.join(os3.homedir(), ".canonry", "cache", "commoncrawl");
|
|
15059
15167
|
var RELEASE_ID_REGEX = /^cc-main-(\d{4})-(jan-feb-mar|apr-may-jun|jul-aug-sep|oct-nov-dec)$/;
|
|
15060
15168
|
function ccReleasePaths(release) {
|
|
15061
15169
|
const base = `${CC_BASE_URL}/${release}/domain`;
|
|
@@ -17572,7 +17680,7 @@ async function apiRoutes(app, opts) {
|
|
|
17572
17680
|
}
|
|
17573
17681
|
|
|
17574
17682
|
// src/server.ts
|
|
17575
|
-
import
|
|
17683
|
+
import os6 from "os";
|
|
17576
17684
|
|
|
17577
17685
|
// ../provider-gemini/src/normalize.ts
|
|
17578
17686
|
import { GoogleGenAI } from "@google/genai";
|
|
@@ -18961,7 +19069,7 @@ var localAdapter = {
|
|
|
18961
19069
|
|
|
18962
19070
|
// ../provider-cdp/src/adapter.ts
|
|
18963
19071
|
import path8 from "path";
|
|
18964
|
-
import
|
|
19072
|
+
import os4 from "os";
|
|
18965
19073
|
|
|
18966
19074
|
// ../provider-cdp/src/connection.ts
|
|
18967
19075
|
import CDP from "chrome-remote-interface";
|
|
@@ -19425,7 +19533,7 @@ function getConnection(config) {
|
|
|
19425
19533
|
return conn;
|
|
19426
19534
|
}
|
|
19427
19535
|
function getScreenshotDir2() {
|
|
19428
|
-
return path8.join(
|
|
19536
|
+
return path8.join(os4.homedir(), ".canonry", "screenshots");
|
|
19429
19537
|
}
|
|
19430
19538
|
var cdpChatgptAdapter = {
|
|
19431
19539
|
name: "cdp:chatgpt",
|
|
@@ -20060,7 +20168,7 @@ function removeWordpressConnection(config, projectName) {
|
|
|
20060
20168
|
import crypto21 from "crypto";
|
|
20061
20169
|
import fs7 from "fs";
|
|
20062
20170
|
import path9 from "path";
|
|
20063
|
-
import
|
|
20171
|
+
import os5 from "os";
|
|
20064
20172
|
import { and as and12, eq as eq24, inArray as inArray7, sql as sql8 } from "drizzle-orm";
|
|
20065
20173
|
|
|
20066
20174
|
// src/citation-utils.ts
|
|
@@ -20309,6 +20417,50 @@ function resolveProviderFanout() {
|
|
|
20309
20417
|
const parsed = Number.parseInt(raw, 10);
|
|
20310
20418
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : PROVIDER_FANOUT_DEFAULT;
|
|
20311
20419
|
}
|
|
20420
|
+
function classifyRunAbortReason(message) {
|
|
20421
|
+
if (/^No providers configured\b/.test(message)) return "no_provider";
|
|
20422
|
+
if (/^Project [^ ]+ not found$/.test(message)) return "project_not_found";
|
|
20423
|
+
if (/^Daily quota exceeded\b/.test(message)) return "quota_exceeded";
|
|
20424
|
+
if (/^Run [^ ]+ not found$/.test(message)) return "run_not_found";
|
|
20425
|
+
if (/^Run [^ ]+ is not executable\b/.test(message)) return "run_not_executable";
|
|
20426
|
+
return void 0;
|
|
20427
|
+
}
|
|
20428
|
+
function classifyProviderErrors(errors) {
|
|
20429
|
+
const codes = /* @__PURE__ */ new Set();
|
|
20430
|
+
for (const message of errors.values()) {
|
|
20431
|
+
codes.add(classifyOneProviderError(message));
|
|
20432
|
+
}
|
|
20433
|
+
const priority = [
|
|
20434
|
+
"PROVIDER_AUTH",
|
|
20435
|
+
"RATE_LIMITED",
|
|
20436
|
+
"TIMEOUT",
|
|
20437
|
+
"NETWORK",
|
|
20438
|
+
"PARSE_ERROR",
|
|
20439
|
+
"UNKNOWN"
|
|
20440
|
+
];
|
|
20441
|
+
for (const code of priority) {
|
|
20442
|
+
if (codes.has(code)) return code;
|
|
20443
|
+
}
|
|
20444
|
+
return "UNKNOWN";
|
|
20445
|
+
}
|
|
20446
|
+
function classifyOneProviderError(message) {
|
|
20447
|
+
if (/\b401\b|\b403\b|unauthorized|forbidden|invalid[_ -]?api[_ -]?key|missing[_ -]?api[_ -]?key|authentication/i.test(message)) {
|
|
20448
|
+
return "PROVIDER_AUTH";
|
|
20449
|
+
}
|
|
20450
|
+
if (/\b429\b|rate[_ -]?limit|too many requests|quota[_ -]?exceeded/i.test(message)) {
|
|
20451
|
+
return "RATE_LIMITED";
|
|
20452
|
+
}
|
|
20453
|
+
if (/timeout|timed out|ETIMEDOUT/i.test(message)) {
|
|
20454
|
+
return "TIMEOUT";
|
|
20455
|
+
}
|
|
20456
|
+
if (/ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|network|fetch failed|socket hang up/i.test(message)) {
|
|
20457
|
+
return "NETWORK";
|
|
20458
|
+
}
|
|
20459
|
+
if (/parse|unexpected token|invalid json|malformed|JSON\.parse/i.test(message)) {
|
|
20460
|
+
return "PARSE_ERROR";
|
|
20461
|
+
}
|
|
20462
|
+
return "UNKNOWN";
|
|
20463
|
+
}
|
|
20312
20464
|
var JobRunner = class {
|
|
20313
20465
|
db;
|
|
20314
20466
|
registry;
|
|
@@ -20451,7 +20603,7 @@ var JobRunner = class {
|
|
|
20451
20603
|
let screenshotRelPath = null;
|
|
20452
20604
|
if (raw.screenshotPath && fs7.existsSync(raw.screenshotPath)) {
|
|
20453
20605
|
const snapshotId = crypto21.randomUUID();
|
|
20454
|
-
const screenshotDir = path9.join(
|
|
20606
|
+
const screenshotDir = path9.join(os5.homedir(), ".canonry", "screenshots", runId);
|
|
20455
20607
|
if (!fs7.existsSync(screenshotDir)) fs7.mkdirSync(screenshotDir, { recursive: true });
|
|
20456
20608
|
const destPath = path9.join(screenshotDir, `${snapshotId}.png`);
|
|
20457
20609
|
fs7.renameSync(raw.screenshotPath, destPath);
|
|
@@ -20540,12 +20692,14 @@ var JobRunner = class {
|
|
|
20540
20692
|
}
|
|
20541
20693
|
this.flushProviderUsage(projectId, providerDispatchCounts);
|
|
20542
20694
|
const finalStatus = allFailed ? "failed" : someFailed ? "partial" : "completed";
|
|
20695
|
+
const failureCode = providerErrors.size > 0 ? classifyProviderErrors(providerErrors) : void 0;
|
|
20543
20696
|
trackEvent("run.completed", {
|
|
20544
20697
|
status: finalStatus,
|
|
20545
20698
|
providerCount: executionContext.providerCount,
|
|
20546
20699
|
providers: executionContext.providers,
|
|
20547
20700
|
queryCount: executionContext.queryCount,
|
|
20548
20701
|
durationMs: Date.now() - startTime,
|
|
20702
|
+
...failureCode ? { errorCode: failureCode } : {},
|
|
20549
20703
|
...executionContext.location ? { location: executionContext.location } : {}
|
|
20550
20704
|
});
|
|
20551
20705
|
this.incrementUsage(projectId, "runs", 1);
|
|
@@ -20573,14 +20727,27 @@ var JobRunner = class {
|
|
|
20573
20727
|
error: errorMessage
|
|
20574
20728
|
}).where(eq24(runs.id, runId)).run();
|
|
20575
20729
|
this.flushProviderUsage(projectId, providerDispatchCounts);
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20730
|
+
const abortReason = classifyRunAbortReason(errorMessage);
|
|
20731
|
+
if (abortReason) {
|
|
20732
|
+
trackEvent("run.aborted", {
|
|
20733
|
+
reason: abortReason,
|
|
20734
|
+
providerCount: executionContext.providerCount,
|
|
20735
|
+
providers: executionContext.providers,
|
|
20736
|
+
queryCount: executionContext.queryCount,
|
|
20737
|
+
durationMs: Date.now() - startTime,
|
|
20738
|
+
...executionContext.location ? { location: executionContext.location } : {}
|
|
20739
|
+
});
|
|
20740
|
+
} else {
|
|
20741
|
+
trackEvent("run.completed", {
|
|
20742
|
+
status: "failed",
|
|
20743
|
+
errorCode: "UNKNOWN",
|
|
20744
|
+
providerCount: executionContext.providerCount,
|
|
20745
|
+
providers: executionContext.providers,
|
|
20746
|
+
queryCount: executionContext.queryCount,
|
|
20747
|
+
durationMs: Date.now() - startTime,
|
|
20748
|
+
...executionContext.location ? { location: executionContext.location } : {}
|
|
20749
|
+
});
|
|
20750
|
+
}
|
|
20584
20751
|
if (this.onRunCompleted) {
|
|
20585
20752
|
this.onRunCompleted(runId, projectId).catch((notifErr) => {
|
|
20586
20753
|
log.error("notification.callback-failed", { runId, error: notifErr instanceof Error ? notifErr.message : String(notifErr) });
|
|
@@ -24072,7 +24239,7 @@ async function createServer(opts) {
|
|
|
24072
24239
|
);
|
|
24073
24240
|
jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
|
|
24074
24241
|
const snapshotService = new SnapshotService(registry);
|
|
24075
|
-
const orphanedOpenClawDir = path14.join(
|
|
24242
|
+
const orphanedOpenClawDir = path14.join(os6.homedir(), ".openclaw-aero");
|
|
24076
24243
|
if (fs12.existsSync(orphanedOpenClawDir)) {
|
|
24077
24244
|
app.log.warn(
|
|
24078
24245
|
{ path: orphanedOpenClawDir },
|