@ainyc/canonry 4.35.0 → 4.39.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-u7ZXZ5mA.js → index-BNTDaIuT.js} +116 -116
- package/assets/assets/index-J4ya3gFy.css +1 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-MLS5KJWK.js → chunk-DOUV45KI.js} +295 -27
- package/dist/{chunk-B3FBOECD.js → chunk-JS6KRBBZ.js} +33 -1
- package/dist/{chunk-NLV4MZZF.js → chunk-Q57CMOL6.js} +531 -296
- package/dist/{chunk-EM5GVF3C.js → chunk-XJVYVURK.js} +3 -1
- package/dist/cli.js +126 -36
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-WAJOEOJV.js → intelligence-service-CIGYPPMR.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -7
- package/assets/assets/index-Cfv0_lwq.css +0 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadConfig,
|
|
6
6
|
loadConfigRaw,
|
|
7
7
|
saveConfigPatch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JS6KRBBZ.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
11
11
|
IntelligenceService,
|
|
@@ -29,11 +29,14 @@ import {
|
|
|
29
29
|
buildContentTargetRows,
|
|
30
30
|
buildGapQueryScore,
|
|
31
31
|
buildInventory,
|
|
32
|
+
buildMentionCoverage,
|
|
33
|
+
buildMentionGapScore,
|
|
32
34
|
buildMentionLandscape,
|
|
33
35
|
buildMovementSummary,
|
|
34
36
|
buildOverviewCompetitors,
|
|
35
37
|
buildProviderScores,
|
|
36
38
|
buildRunHistory,
|
|
39
|
+
buildShareOfVoice,
|
|
37
40
|
buildVisibilityScore,
|
|
38
41
|
categorizeQueryByIntent,
|
|
39
42
|
ccReleaseSyncs,
|
|
@@ -73,7 +76,7 @@ import {
|
|
|
73
76
|
schedules,
|
|
74
77
|
trafficSources,
|
|
75
78
|
usageCounters
|
|
76
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-DOUV45KI.js";
|
|
77
80
|
import {
|
|
78
81
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
79
82
|
AGENT_PROVIDER_IDS,
|
|
@@ -186,7 +189,7 @@ import {
|
|
|
186
189
|
visibilityStateFromAnswerMentioned,
|
|
187
190
|
windowCutoff,
|
|
188
191
|
wordpressEnvSchema
|
|
189
|
-
} from "./chunk-
|
|
192
|
+
} from "./chunk-XJVYVURK.js";
|
|
190
193
|
|
|
191
194
|
// src/telemetry.ts
|
|
192
195
|
import crypto from "crypto";
|
|
@@ -475,8 +478,8 @@ function checkLatestVersionForServer(opts) {
|
|
|
475
478
|
// src/server.ts
|
|
476
479
|
import { createRequire as createRequire4 } from "module";
|
|
477
480
|
import crypto34 from "crypto";
|
|
478
|
-
import
|
|
479
|
-
import
|
|
481
|
+
import fs13 from "fs";
|
|
482
|
+
import path15 from "path";
|
|
480
483
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
481
484
|
import { eq as eq41 } from "drizzle-orm";
|
|
482
485
|
import Fastify from "fastify";
|
|
@@ -548,7 +551,7 @@ async function authPlugin(app, opts = {}) {
|
|
|
548
551
|
|
|
549
552
|
// ../api-routes/src/projects.ts
|
|
550
553
|
import crypto4 from "crypto";
|
|
551
|
-
import { eq as eq3 } from "drizzle-orm";
|
|
554
|
+
import { eq as eq3, sql as sql2 } from "drizzle-orm";
|
|
552
555
|
|
|
553
556
|
// ../api-routes/src/helpers.ts
|
|
554
557
|
import crypto3 from "crypto";
|
|
@@ -726,6 +729,30 @@ async function projectRoutes(app, opts) {
|
|
|
726
729
|
const project = resolveProject(app.db, request.params.name);
|
|
727
730
|
return reply.send(formatProject(project));
|
|
728
731
|
});
|
|
732
|
+
app.get("/projects/:name/delete-preview", async (request, reply) => {
|
|
733
|
+
const project = resolveProject(app.db, request.params.name);
|
|
734
|
+
const pid = project.id;
|
|
735
|
+
const count = (n) => n ?? 0;
|
|
736
|
+
const queryCount = app.db.select({ n: sql2`count(*)` }).from(queries).where(eq3(queries.projectId, pid)).get();
|
|
737
|
+
const competitorCount = app.db.select({ n: sql2`count(*)` }).from(competitors).where(eq3(competitors.projectId, pid)).get();
|
|
738
|
+
const runCount = app.db.select({ n: sql2`count(*)` }).from(runs).where(eq3(runs.projectId, pid)).get();
|
|
739
|
+
const snapshotCount = app.db.select({ n: sql2`count(*)` }).from(querySnapshots).innerJoin(runs, eq3(querySnapshots.runId, runs.id)).where(eq3(runs.projectId, pid)).get();
|
|
740
|
+
const insightCount = app.db.select({ n: sql2`count(*)` }).from(insights).where(eq3(insights.projectId, pid)).get();
|
|
741
|
+
const auditLogCount = app.db.select({ n: sql2`count(*)` }).from(auditLog).where(eq3(auditLog.projectId, pid)).get();
|
|
742
|
+
return reply.send({
|
|
743
|
+
project: { id: project.id, name: project.name },
|
|
744
|
+
cascadeRows: {
|
|
745
|
+
queries: count(queryCount?.n),
|
|
746
|
+
competitors: count(competitorCount?.n),
|
|
747
|
+
runs: count(runCount?.n),
|
|
748
|
+
snapshots: count(snapshotCount?.n),
|
|
749
|
+
insights: count(insightCount?.n)
|
|
750
|
+
},
|
|
751
|
+
detachedRows: {
|
|
752
|
+
auditLog: count(auditLogCount?.n)
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
});
|
|
729
756
|
app.delete("/projects/:name", async (request, reply) => {
|
|
730
757
|
const project = resolveProject(app.db, request.params.name);
|
|
731
758
|
writeAuditLog(app.db, {
|
|
@@ -901,7 +928,7 @@ function aliasArraysEqual(a, b) {
|
|
|
901
928
|
|
|
902
929
|
// ../api-routes/src/queries.ts
|
|
903
930
|
import crypto5 from "crypto";
|
|
904
|
-
import { eq as eq4 } from "drizzle-orm";
|
|
931
|
+
import { eq as eq4, inArray, sql as sql3 } from "drizzle-orm";
|
|
905
932
|
async function queryRoutes(app, opts) {
|
|
906
933
|
app.get("/projects/:name/queries", async (request, reply) => {
|
|
907
934
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -937,6 +964,36 @@ async function queryRoutes(app, opts) {
|
|
|
937
964
|
const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
938
965
|
return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
|
|
939
966
|
});
|
|
967
|
+
app.post("/projects/:name/queries/replace-preview", async (request, reply) => {
|
|
968
|
+
const project = resolveProject(app.db, request.params.name);
|
|
969
|
+
const body = request.body;
|
|
970
|
+
if (!body || !Array.isArray(body.queries)) {
|
|
971
|
+
throw validationError('Body must contain a "queries" array');
|
|
972
|
+
}
|
|
973
|
+
const currentRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
974
|
+
const currentTexts = currentRows.map((r) => r.query);
|
|
975
|
+
const currentSet = new Set(currentTexts);
|
|
976
|
+
const proposedSet = new Set(body.queries);
|
|
977
|
+
const removed = currentTexts.filter((q) => !proposedSet.has(q));
|
|
978
|
+
const added = body.queries.filter((q) => !currentSet.has(q));
|
|
979
|
+
const unchanged = currentTexts.filter((q) => proposedSet.has(q));
|
|
980
|
+
const currentIds = currentRows.map((r) => r.id);
|
|
981
|
+
let snapshotsDetached = 0;
|
|
982
|
+
let affectedQueries = 0;
|
|
983
|
+
if (currentIds.length > 0) {
|
|
984
|
+
const snapshotCount = app.db.select({ n: sql3`count(*)` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
985
|
+
snapshotsDetached = snapshotCount?.n ?? 0;
|
|
986
|
+
const distinctAffected = app.db.select({ n: sql3`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
987
|
+
affectedQueries = distinctAffected?.n ?? 0;
|
|
988
|
+
}
|
|
989
|
+
return reply.send({
|
|
990
|
+
project: { id: project.id, name: project.name },
|
|
991
|
+
current: currentTexts,
|
|
992
|
+
proposed: body.queries,
|
|
993
|
+
diff: { added, removed, unchanged },
|
|
994
|
+
snapshotImpact: { affectedQueries, snapshotsDetached }
|
|
995
|
+
});
|
|
996
|
+
});
|
|
940
997
|
app.delete("/projects/:name/queries", async (request, reply) => {
|
|
941
998
|
const project = resolveProject(app.db, request.params.name);
|
|
942
999
|
const body = request.body;
|
|
@@ -1300,7 +1357,7 @@ function parseCompetitorBatch(value) {
|
|
|
1300
1357
|
|
|
1301
1358
|
// ../api-routes/src/runs.ts
|
|
1302
1359
|
import crypto8 from "crypto";
|
|
1303
|
-
import { and as and2, eq as eq7, asc, desc, or as or2, sql as
|
|
1360
|
+
import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql4 } from "drizzle-orm";
|
|
1304
1361
|
|
|
1305
1362
|
// ../api-routes/src/run-queue.ts
|
|
1306
1363
|
import crypto7 from "crypto";
|
|
@@ -1472,7 +1529,7 @@ async function runRoutes(app, opts) {
|
|
|
1472
1529
|
});
|
|
1473
1530
|
app.get("/projects/:name/runs/latest", async (request, reply) => {
|
|
1474
1531
|
const project = resolveProject(app.db, request.params.name);
|
|
1475
|
-
const countRow = app.db.select({ count:
|
|
1532
|
+
const countRow = app.db.select({ count: sql4`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
|
|
1476
1533
|
const totalRuns = countRow?.count ?? 0;
|
|
1477
1534
|
const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
|
|
1478
1535
|
if (!latestRun) {
|
|
@@ -1800,7 +1857,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
1800
1857
|
const body = JSON.stringify(payload);
|
|
1801
1858
|
const isHttps = target.url.protocol === "https:";
|
|
1802
1859
|
const port = target.url.port ? Number(target.url.port) : isHttps ? 443 : 80;
|
|
1803
|
-
const
|
|
1860
|
+
const path16 = `${target.url.pathname}${target.url.search}`;
|
|
1804
1861
|
const headers = {
|
|
1805
1862
|
"Content-Length": String(Buffer.byteLength(body)),
|
|
1806
1863
|
"Content-Type": "application/json",
|
|
@@ -1816,7 +1873,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
1816
1873
|
headers,
|
|
1817
1874
|
hostname: target.address,
|
|
1818
1875
|
method: "POST",
|
|
1819
|
-
path:
|
|
1876
|
+
path: path16,
|
|
1820
1877
|
port,
|
|
1821
1878
|
timeout: REQUEST_TIMEOUT_MS
|
|
1822
1879
|
};
|
|
@@ -2201,7 +2258,7 @@ function normalizeCompetitorList2(domains) {
|
|
|
2201
2258
|
}
|
|
2202
2259
|
|
|
2203
2260
|
// ../api-routes/src/history.ts
|
|
2204
|
-
import { eq as eq9, desc as desc2, inArray } from "drizzle-orm";
|
|
2261
|
+
import { eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
|
|
2205
2262
|
|
|
2206
2263
|
// ../api-routes/src/notification-redaction.ts
|
|
2207
2264
|
var REDACTED_URL = {
|
|
@@ -2277,7 +2334,7 @@ async function historyRoutes(app) {
|
|
|
2277
2334
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
2278
2335
|
location: querySnapshots.location,
|
|
2279
2336
|
createdAt: querySnapshots.createdAt
|
|
2280
|
-
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(
|
|
2337
|
+
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
|
|
2281
2338
|
const locationFilter = request.query.location;
|
|
2282
2339
|
const filtered = locationFilter !== void 0 ? allSnapshots.filter((s) => s.location === (locationFilter || null)) : allSnapshots;
|
|
2283
2340
|
const total = filtered.length;
|
|
@@ -2312,7 +2369,7 @@ async function historyRoutes(app) {
|
|
|
2312
2369
|
return reply.send([]);
|
|
2313
2370
|
}
|
|
2314
2371
|
const runIds = new Set(projectRuns.map((r) => r.id));
|
|
2315
|
-
const rawSnapshots = app.db.select().from(querySnapshots).where(
|
|
2372
|
+
const rawSnapshots = app.db.select().from(querySnapshots).where(inArray2(querySnapshots.runId, [...runIds])).all();
|
|
2316
2373
|
const timelineLocationFilter = request.query.location;
|
|
2317
2374
|
const filteredSnapshots = timelineLocationFilter !== void 0 ? rawSnapshots.filter((s) => s.location === (timelineLocationFilter || null)) : rawSnapshots;
|
|
2318
2375
|
const allSnapshots = filteredSnapshots.map((snapshot) => ({
|
|
@@ -2492,7 +2549,7 @@ function formatAuditEntry(row) {
|
|
|
2492
2549
|
}
|
|
2493
2550
|
|
|
2494
2551
|
// ../api-routes/src/analytics.ts
|
|
2495
|
-
import { eq as eq10, desc as desc3, inArray as
|
|
2552
|
+
import { eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
|
|
2496
2553
|
async function analyticsRoutes(app) {
|
|
2497
2554
|
app.get("/projects/:name/analytics/metrics", async (request, reply) => {
|
|
2498
2555
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -2519,7 +2576,7 @@ async function analyticsRoutes(app) {
|
|
|
2519
2576
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2520
2577
|
answerText: querySnapshots.answerText,
|
|
2521
2578
|
createdAt: querySnapshots.createdAt
|
|
2522
|
-
}).from(querySnapshots).where(
|
|
2579
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, runIds)).all());
|
|
2523
2580
|
const allSnapshots = rawSnapshots.map((s) => ({
|
|
2524
2581
|
...s,
|
|
2525
2582
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2564,7 +2621,7 @@ async function analyticsRoutes(app) {
|
|
|
2564
2621
|
citationState: querySnapshots.citationState,
|
|
2565
2622
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2566
2623
|
answerText: querySnapshots.answerText
|
|
2567
|
-
}).from(querySnapshots).where(
|
|
2624
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, windowRunIds)).all());
|
|
2568
2625
|
for (const s of allWindowSnaps) {
|
|
2569
2626
|
const timePoint = runIdToCreatedAt.get(s.runId) ?? s.runId;
|
|
2570
2627
|
let entry = consistencyMap.get(s.queryId);
|
|
@@ -2585,7 +2642,7 @@ async function analyticsRoutes(app) {
|
|
|
2585
2642
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2586
2643
|
answerText: querySnapshots.answerText,
|
|
2587
2644
|
competitorOverlap: querySnapshots.competitorOverlap
|
|
2588
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2645
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, latestGroupRunIds)).all());
|
|
2589
2646
|
const snapshots = rawSnapshots.map((s) => ({
|
|
2590
2647
|
...s,
|
|
2591
2648
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2680,7 +2737,7 @@ async function analyticsRoutes(app) {
|
|
|
2680
2737
|
queryId: querySnapshots.queryId,
|
|
2681
2738
|
query: queries.query,
|
|
2682
2739
|
rawResponse: querySnapshots.rawResponse
|
|
2683
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2740
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, windowRunIds)).all();
|
|
2684
2741
|
const overallCounts = /* @__PURE__ */ new Map();
|
|
2685
2742
|
const byQuery = {};
|
|
2686
2743
|
for (const snap of snapshots) {
|
|
@@ -2839,7 +2896,7 @@ function buildCategoryCounts(counts) {
|
|
|
2839
2896
|
}
|
|
2840
2897
|
|
|
2841
2898
|
// ../api-routes/src/intelligence.ts
|
|
2842
|
-
import { eq as eq11, desc as desc4, and as and4, inArray as
|
|
2899
|
+
import { eq as eq11, desc as desc4, and as and4, inArray as inArray4 } from "drizzle-orm";
|
|
2843
2900
|
function emptyHealthSnapshot(projectId) {
|
|
2844
2901
|
return {
|
|
2845
2902
|
id: `no-data:${projectId}`,
|
|
@@ -2955,14 +3012,14 @@ async function intelligenceRoutes(app) {
|
|
|
2955
3012
|
const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and4(
|
|
2956
3013
|
eq11(runs.projectId, project.id),
|
|
2957
3014
|
eq11(runs.kind, RunKinds["answer-visibility"]),
|
|
2958
|
-
|
|
3015
|
+
inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
2959
3016
|
)).orderBy(desc4(runs.createdAt), desc4(runs.id)).all();
|
|
2960
3017
|
const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
|
|
2961
3018
|
const latestGroupRunIds = latestGroup.map((r) => r.id);
|
|
2962
3019
|
if (latestGroupRunIds.length > 0) {
|
|
2963
3020
|
const groupRows = app.db.select().from(healthSnapshots).where(and4(
|
|
2964
3021
|
eq11(healthSnapshots.projectId, project.id),
|
|
2965
|
-
|
|
3022
|
+
inArray4(healthSnapshots.runId, latestGroupRunIds)
|
|
2966
3023
|
)).all();
|
|
2967
3024
|
if (groupRows.length > 0) {
|
|
2968
3025
|
return reply.send(aggregateHealthSnapshots(project.id, groupRows));
|
|
@@ -2984,7 +3041,7 @@ async function intelligenceRoutes(app) {
|
|
|
2984
3041
|
}
|
|
2985
3042
|
|
|
2986
3043
|
// ../api-routes/src/report.ts
|
|
2987
|
-
import { and as and6, desc as desc6, eq as eq13, gte, inArray as
|
|
3044
|
+
import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray6, lt, lte, ne, or as or3, sql as sql5 } from "drizzle-orm";
|
|
2988
3045
|
|
|
2989
3046
|
// ../api-routes/src/report-renderer.ts
|
|
2990
3047
|
var COLORS = {
|
|
@@ -3033,9 +3090,9 @@ function inferAdSource(params) {
|
|
|
3033
3090
|
function formatLandingPageHtml(raw) {
|
|
3034
3091
|
const value = raw ?? "";
|
|
3035
3092
|
const queryIdx = value.indexOf("?");
|
|
3036
|
-
const
|
|
3093
|
+
const path16 = queryIdx === -1 ? value : value.slice(0, queryIdx);
|
|
3037
3094
|
const query = queryIdx === -1 ? "" : value.slice(queryIdx + 1);
|
|
3038
|
-
const pathHtml = `<span class="page-path">${escapeHtml(
|
|
3095
|
+
const pathHtml = `<span class="page-path">${escapeHtml(path16 || "/")}</span>`;
|
|
3039
3096
|
if (!query) return pathHtml;
|
|
3040
3097
|
let summary = "";
|
|
3041
3098
|
try {
|
|
@@ -4389,7 +4446,7 @@ function renderLineChart(points, color, title, height = 200) {
|
|
|
4389
4446
|
y: padY + usableH - p.y / max * usableH,
|
|
4390
4447
|
raw: p
|
|
4391
4448
|
}));
|
|
4392
|
-
const
|
|
4449
|
+
const path16 = xy.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(" ");
|
|
4393
4450
|
const dots = xy.map((p) => `<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="3" fill="${color}" />`).join("");
|
|
4394
4451
|
const xLabels = xy.map((p, i) => {
|
|
4395
4452
|
if (points.length > 8 && i % Math.ceil(points.length / 6) !== 0 && i !== points.length - 1) return "";
|
|
@@ -4401,7 +4458,7 @@ function renderLineChart(points, color, title, height = 200) {
|
|
|
4401
4458
|
<line x1="${padX}" y1="${padY + usableH}" x2="${padX + usableW}" y2="${padY + usableH}" stroke="${COLORS.border}" stroke-width="1" />
|
|
4402
4459
|
<text x="${padX - 6}" y="${(padY + 4).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">${formatNumber(max)}</text>
|
|
4403
4460
|
<text x="${padX - 6}" y="${(padY + usableH).toFixed(1)}" fill="${COLORS.textFaint}" font-size="9" text-anchor="end">0</text>
|
|
4404
|
-
<path d="${
|
|
4461
|
+
<path d="${path16}" stroke="${color}" stroke-width="2" fill="none" />
|
|
4405
4462
|
${dots}
|
|
4406
4463
|
${xLabels}
|
|
4407
4464
|
</svg>
|
|
@@ -5220,7 +5277,7 @@ function renderReportHtml(report, opts = {}) {
|
|
|
5220
5277
|
}
|
|
5221
5278
|
|
|
5222
5279
|
// ../api-routes/src/content-data.ts
|
|
5223
|
-
import { and as and5, eq as eq12, desc as desc5, inArray as
|
|
5280
|
+
import { and as and5, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
|
|
5224
5281
|
var RECENT_RUNS_WINDOW = 5;
|
|
5225
5282
|
function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
5226
5283
|
const projectId = project.id;
|
|
@@ -5350,7 +5407,7 @@ function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter)
|
|
|
5350
5407
|
// Queued/running/failed/cancelled runs may have partial or no
|
|
5351
5408
|
// snapshots; including them risks pointing latestRunId at a run with
|
|
5352
5409
|
// no usable evidence.
|
|
5353
|
-
|
|
5410
|
+
inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
5354
5411
|
)
|
|
5355
5412
|
).orderBy(desc5(runs.createdAt)).all();
|
|
5356
5413
|
const filtered = locationFilter === void 0 ? rows : rows.filter((r) => (r.location ?? null) === locationFilter);
|
|
@@ -5375,9 +5432,9 @@ function buildGaTrafficByPage(db, projectId) {
|
|
|
5375
5432
|
}).from(gaTrafficSnapshots).where(eq12(gaTrafficSnapshots.projectId, projectId)).all();
|
|
5376
5433
|
const map = /* @__PURE__ */ new Map();
|
|
5377
5434
|
for (const row of rows) {
|
|
5378
|
-
const
|
|
5379
|
-
if (!
|
|
5380
|
-
map.set(
|
|
5435
|
+
const path16 = extractPath(row.landingPage);
|
|
5436
|
+
if (!path16) continue;
|
|
5437
|
+
map.set(path16, (map.get(path16) ?? 0) + (row.sessions ?? 0));
|
|
5381
5438
|
}
|
|
5382
5439
|
return map;
|
|
5383
5440
|
}
|
|
@@ -5392,7 +5449,7 @@ function buildCandidateQueries(opts) {
|
|
|
5392
5449
|
const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq12(queries.projectId, opts.projectId)).all();
|
|
5393
5450
|
const queryIdByText = new Map(queryRows.map((r) => [r.text, r.id]));
|
|
5394
5451
|
const candidateQueryIds = opts.candidateQueryStrings.map((q) => queryIdByText.get(q)).filter((id) => Boolean(id));
|
|
5395
|
-
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(
|
|
5452
|
+
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
|
|
5396
5453
|
const snapshotsByQuery = /* @__PURE__ */ new Map();
|
|
5397
5454
|
for (const row of snapshotRows) {
|
|
5398
5455
|
const list = snapshotsByQuery.get(row.queryId) ?? [];
|
|
@@ -5572,8 +5629,8 @@ function normalizeDomain(domain) {
|
|
|
5572
5629
|
function extractPath(url) {
|
|
5573
5630
|
if (!url) return "";
|
|
5574
5631
|
const match = /^https?:\/\/[^/]+(.*)$/.exec(url.trim());
|
|
5575
|
-
const
|
|
5576
|
-
const stripped =
|
|
5632
|
+
const path16 = match ? match[1] : url.trim();
|
|
5633
|
+
const stripped = path16.replace(/\/+$/, "");
|
|
5577
5634
|
return stripped || "/";
|
|
5578
5635
|
}
|
|
5579
5636
|
|
|
@@ -5610,7 +5667,7 @@ function loadSnapshotsForRun(db, runId) {
|
|
|
5610
5667
|
}
|
|
5611
5668
|
function loadSnapshotsForRunIds(db, runIds) {
|
|
5612
5669
|
if (runIds.length === 0) return [];
|
|
5613
|
-
const rows = db.select().from(querySnapshots).where(
|
|
5670
|
+
const rows = db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, [...runIds])).all();
|
|
5614
5671
|
return rows.filter((r) => r.queryId !== null).map((r) => ({
|
|
5615
5672
|
id: r.id,
|
|
5616
5673
|
runId: r.runId,
|
|
@@ -5860,7 +5917,7 @@ function buildAiReferrals(db, projectId) {
|
|
|
5860
5917
|
return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
|
|
5861
5918
|
}
|
|
5862
5919
|
function nonSubresourceReferralPathCondition() {
|
|
5863
|
-
return
|
|
5920
|
+
return sql5`
|
|
5864
5921
|
LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
|
|
5865
5922
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
|
|
5866
5923
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
|
|
@@ -5899,7 +5956,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5899
5956
|
const priorStart = new Date(priorStartMs).toISOString();
|
|
5900
5957
|
const trendStart = new Date(trendStartMs).toISOString();
|
|
5901
5958
|
const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5902
|
-
db.select({ total:
|
|
5959
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5903
5960
|
and6(
|
|
5904
5961
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5905
5962
|
eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5909,7 +5966,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5909
5966
|
).get()?.total ?? 0
|
|
5910
5967
|
);
|
|
5911
5968
|
const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5912
|
-
db.select({ total:
|
|
5969
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5913
5970
|
and6(
|
|
5914
5971
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5915
5972
|
ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5919,7 +5976,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5919
5976
|
).get()?.total ?? 0
|
|
5920
5977
|
);
|
|
5921
5978
|
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5922
|
-
db.select({ total:
|
|
5979
|
+
db.select({ total: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
5923
5980
|
and6(
|
|
5924
5981
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
5925
5982
|
nonSubresourceReferralPathCondition(),
|
|
@@ -5937,7 +5994,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5937
5994
|
const crawlerByOperatorRows = db.select({
|
|
5938
5995
|
operator: crawlerEventsHourly.operator,
|
|
5939
5996
|
verificationStatus: crawlerEventsHourly.verificationStatus,
|
|
5940
|
-
hits:
|
|
5997
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5941
5998
|
}).from(crawlerEventsHourly).where(
|
|
5942
5999
|
and6(
|
|
5943
6000
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5947,7 +6004,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5947
6004
|
).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
|
|
5948
6005
|
const crawlerByOperatorPriorRows = db.select({
|
|
5949
6006
|
operator: crawlerEventsHourly.operator,
|
|
5950
|
-
hits:
|
|
6007
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5951
6008
|
}).from(crawlerEventsHourly).where(
|
|
5952
6009
|
and6(
|
|
5953
6010
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5958,7 +6015,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5958
6015
|
).groupBy(crawlerEventsHourly.operator).all();
|
|
5959
6016
|
const referralByOperatorRows = db.select({
|
|
5960
6017
|
operator: aiReferralEventsHourly.operator,
|
|
5961
|
-
hits:
|
|
6018
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
5962
6019
|
}).from(aiReferralEventsHourly).where(
|
|
5963
6020
|
and6(
|
|
5964
6021
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -5998,8 +6055,8 @@ function buildServerActivity(db, projectId) {
|
|
|
5998
6055
|
);
|
|
5999
6056
|
const topPathsRows = db.select({
|
|
6000
6057
|
path: crawlerEventsHourly.pathNormalized,
|
|
6001
|
-
hits:
|
|
6002
|
-
operators:
|
|
6058
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
|
|
6059
|
+
operators: sql5`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
|
|
6003
6060
|
}).from(crawlerEventsHourly).where(
|
|
6004
6061
|
and6(
|
|
6005
6062
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6007,7 +6064,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6007
6064
|
gte(crawlerEventsHourly.tsHour, headlineStart),
|
|
6008
6065
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6009
6066
|
)
|
|
6010
|
-
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(
|
|
6067
|
+
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql5`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6011
6068
|
const topCrawledPaths = topPathsRows.map((r) => ({
|
|
6012
6069
|
path: r.path,
|
|
6013
6070
|
verifiedHits: Number(r.hits),
|
|
@@ -6015,8 +6072,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6015
6072
|
}));
|
|
6016
6073
|
const referralProductsRows = db.select({
|
|
6017
6074
|
product: aiReferralEventsHourly.product,
|
|
6018
|
-
arrivals:
|
|
6019
|
-
landingPaths:
|
|
6075
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6076
|
+
landingPaths: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
|
|
6020
6077
|
}).from(aiReferralEventsHourly).where(
|
|
6021
6078
|
and6(
|
|
6022
6079
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6024,7 +6081,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6024
6081
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6025
6082
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6026
6083
|
)
|
|
6027
|
-
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(
|
|
6084
|
+
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
|
|
6028
6085
|
const referralProducts = referralProductsRows.map((r) => ({
|
|
6029
6086
|
product: r.product,
|
|
6030
6087
|
arrivals: Number(r.arrivals),
|
|
@@ -6032,8 +6089,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6032
6089
|
}));
|
|
6033
6090
|
const topReferralRows = db.select({
|
|
6034
6091
|
path: aiReferralEventsHourly.landingPathNormalized,
|
|
6035
|
-
arrivals:
|
|
6036
|
-
products:
|
|
6092
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6093
|
+
products: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
|
|
6037
6094
|
}).from(aiReferralEventsHourly).where(
|
|
6038
6095
|
and6(
|
|
6039
6096
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6041,15 +6098,15 @@ function buildServerActivity(db, projectId) {
|
|
|
6041
6098
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6042
6099
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6043
6100
|
)
|
|
6044
|
-
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(
|
|
6101
|
+
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6045
6102
|
const topReferralLandingPaths = topReferralRows.map((r) => ({
|
|
6046
6103
|
path: r.path,
|
|
6047
6104
|
arrivals: Number(r.arrivals),
|
|
6048
6105
|
distinctProducts: Number(r.products)
|
|
6049
6106
|
}));
|
|
6050
6107
|
const crawlerTrendRows = db.select({
|
|
6051
|
-
date:
|
|
6052
|
-
hits:
|
|
6108
|
+
date: sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
|
|
6109
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
6053
6110
|
}).from(crawlerEventsHourly).where(
|
|
6054
6111
|
and6(
|
|
6055
6112
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6057,10 +6114,10 @@ function buildServerActivity(db, projectId) {
|
|
|
6057
6114
|
gte(crawlerEventsHourly.tsHour, trendStart),
|
|
6058
6115
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6059
6116
|
)
|
|
6060
|
-
).groupBy(
|
|
6117
|
+
).groupBy(sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
|
|
6061
6118
|
const referralTrendRows = db.select({
|
|
6062
|
-
date:
|
|
6063
|
-
hits:
|
|
6119
|
+
date: sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
|
|
6120
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
6064
6121
|
}).from(aiReferralEventsHourly).where(
|
|
6065
6122
|
and6(
|
|
6066
6123
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6068,7 +6125,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6068
6125
|
gte(aiReferralEventsHourly.tsHour, trendStart),
|
|
6069
6126
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6070
6127
|
)
|
|
6071
|
-
).groupBy(
|
|
6128
|
+
).groupBy(sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
|
|
6072
6129
|
const dailyTrendMap = /* @__PURE__ */ new Map();
|
|
6073
6130
|
for (const r of crawlerTrendRows) {
|
|
6074
6131
|
const e = dailyTrendMap.get(r.date) ?? { verifiedCrawlerHits: 0, referralArrivals: 0 };
|
|
@@ -6190,7 +6247,7 @@ function buildInsightList(db, projectId, locationFilter) {
|
|
|
6190
6247
|
)
|
|
6191
6248
|
).orderBy(desc6(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
|
|
6192
6249
|
if (recentRunIds.length === 0) return [];
|
|
6193
|
-
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId),
|
|
6250
|
+
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
|
|
6194
6251
|
const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
6195
6252
|
const flat = rows.filter((r) => !r.dismissed).map((r) => {
|
|
6196
6253
|
const recommendation = parseJsonColumn(r.recommendation, null);
|
|
@@ -6963,7 +7020,7 @@ async function reportRoutes(app) {
|
|
|
6963
7020
|
}
|
|
6964
7021
|
|
|
6965
7022
|
// ../api-routes/src/citations.ts
|
|
6966
|
-
import { eq as eq14, inArray as
|
|
7023
|
+
import { eq as eq14, inArray as inArray7 } from "drizzle-orm";
|
|
6967
7024
|
async function citationRoutes(app) {
|
|
6968
7025
|
app.get("/projects/:name/citations/visibility", async (request, reply) => {
|
|
6969
7026
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -6987,7 +7044,7 @@ async function citationRoutes(app) {
|
|
|
6987
7044
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
6988
7045
|
answerMentioned: querySnapshots.answerMentioned,
|
|
6989
7046
|
createdAt: querySnapshots.createdAt
|
|
6990
|
-
}).from(querySnapshots).where(
|
|
7047
|
+
}).from(querySnapshots).where(inArray7(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
|
|
6991
7048
|
if (rawSnapshots.length === 0) {
|
|
6992
7049
|
return reply.send(emptyCitationVisibility("no-runs-yet"));
|
|
6993
7050
|
}
|
|
@@ -7128,7 +7185,7 @@ function normalizeDomain2(domain) {
|
|
|
7128
7185
|
}
|
|
7129
7186
|
|
|
7130
7187
|
// ../api-routes/src/composites.ts
|
|
7131
|
-
import { eq as eq15, and as and7, desc as desc7, sql as
|
|
7188
|
+
import { eq as eq15, and as and7, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
|
|
7132
7189
|
var TOP_INSIGHT_LIMIT = 5;
|
|
7133
7190
|
var SEARCH_HIT_HARD_LIMIT = 50;
|
|
7134
7191
|
var SEARCH_SNIPPET_RADIUS = 80;
|
|
@@ -7180,9 +7237,19 @@ async function compositeRoutes(app) {
|
|
|
7180
7237
|
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq15(queries.projectId, project.id)).all();
|
|
7181
7238
|
const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
|
|
7182
7239
|
const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
|
|
7240
|
+
const projectDomains = effectiveDomains({
|
|
7241
|
+
canonicalDomain: project.canonicalDomain,
|
|
7242
|
+
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
7243
|
+
});
|
|
7183
7244
|
const scores = {
|
|
7245
|
+
mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
|
|
7184
7246
|
visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
|
|
7247
|
+
shareOfVoice: buildShareOfVoice(latestSnapshots, {
|
|
7248
|
+
projectDomains,
|
|
7249
|
+
competitorDomains: competitorRows.map((c) => c.domain)
|
|
7250
|
+
}),
|
|
7185
7251
|
gapQueries: buildGapQueryScore(latestSnapshots),
|
|
7252
|
+
mentionGaps: buildMentionGapScore(latestSnapshots),
|
|
7186
7253
|
indexCoverage: buildIndexCoverageScore(app, project.id),
|
|
7187
7254
|
competitorPressure: buildCompetitorPressureScore(
|
|
7188
7255
|
latestSnapshots,
|
|
@@ -7191,7 +7258,9 @@ async function compositeRoutes(app) {
|
|
|
7191
7258
|
),
|
|
7192
7259
|
runStatus: buildRunStatusScore(allRuns)
|
|
7193
7260
|
};
|
|
7194
|
-
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots
|
|
7261
|
+
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots, {
|
|
7262
|
+
queryLookup: queryLookup.byId
|
|
7263
|
+
});
|
|
7195
7264
|
const providerScores = buildProviderScores(latestSnapshots);
|
|
7196
7265
|
const overviewCompetitors = buildOverviewCompetitors(
|
|
7197
7266
|
latestSnapshots,
|
|
@@ -7245,9 +7314,9 @@ async function compositeRoutes(app) {
|
|
|
7245
7314
|
and7(
|
|
7246
7315
|
eq15(queries.projectId, project.id),
|
|
7247
7316
|
or4(
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7317
|
+
sql6`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
|
|
7318
|
+
sql6`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
|
|
7319
|
+
sql6`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
|
|
7251
7320
|
like(queries.query, pattern)
|
|
7252
7321
|
)
|
|
7253
7322
|
)
|
|
@@ -7258,8 +7327,8 @@ async function compositeRoutes(app) {
|
|
|
7258
7327
|
or4(
|
|
7259
7328
|
like(insights.title, pattern),
|
|
7260
7329
|
like(insights.query, pattern),
|
|
7261
|
-
|
|
7262
|
-
|
|
7330
|
+
sql6`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
|
|
7331
|
+
sql6`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
|
|
7263
7332
|
)
|
|
7264
7333
|
)
|
|
7265
7334
|
).orderBy(desc7(insights.createdAt)).limit(limit + 1).all();
|
|
@@ -7331,9 +7400,10 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7331
7400
|
provider: querySnapshots.provider,
|
|
7332
7401
|
model: querySnapshots.model,
|
|
7333
7402
|
citationState: querySnapshots.citationState,
|
|
7403
|
+
answerMentioned: querySnapshots.answerMentioned,
|
|
7334
7404
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7335
7405
|
citedDomains: querySnapshots.citedDomains
|
|
7336
|
-
}).from(querySnapshots).where(
|
|
7406
|
+
}).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
|
|
7337
7407
|
for (const row of rows) {
|
|
7338
7408
|
const list = result.get(row.runId) ?? [];
|
|
7339
7409
|
list.push({
|
|
@@ -7341,6 +7411,7 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7341
7411
|
provider: row.provider,
|
|
7342
7412
|
model: row.model,
|
|
7343
7413
|
citationState: row.citationState,
|
|
7414
|
+
answerMentioned: row.answerMentioned,
|
|
7344
7415
|
competitorOverlap: parseJsonColumn(row.competitorOverlap, []),
|
|
7345
7416
|
citedDomains: parseJsonColumn(row.citedDomains, [])
|
|
7346
7417
|
});
|
|
@@ -7936,6 +8007,18 @@ var routeCatalog = [
|
|
|
7936
8007
|
404: { description: "Project not found." }
|
|
7937
8008
|
}
|
|
7938
8009
|
},
|
|
8010
|
+
{
|
|
8011
|
+
method: "get",
|
|
8012
|
+
path: "/api/v1/projects/{name}/delete-preview",
|
|
8013
|
+
summary: "Preview the cascade impact of deleting a project",
|
|
8014
|
+
description: "Read-only impact summary backing `canonry project delete --dry-run`. Returns counts of rows that would cascade-delete (queries, competitors, runs, snapshots, insights) and rows that would be detached (audit_log \u2014 `project_id` set to NULL).",
|
|
8015
|
+
tags: ["projects"],
|
|
8016
|
+
parameters: [nameParameter],
|
|
8017
|
+
responses: {
|
|
8018
|
+
200: { description: "Preview of cascade impact." },
|
|
8019
|
+
404: { description: "Project not found." }
|
|
8020
|
+
}
|
|
8021
|
+
},
|
|
7939
8022
|
{
|
|
7940
8023
|
method: "post",
|
|
7941
8024
|
path: "/api/v1/projects/{name}/locations",
|
|
@@ -8099,6 +8182,32 @@ var routeCatalog = [
|
|
|
8099
8182
|
200: { description: "Queries appended." }
|
|
8100
8183
|
}
|
|
8101
8184
|
},
|
|
8185
|
+
{
|
|
8186
|
+
method: "post",
|
|
8187
|
+
path: "/api/v1/projects/{name}/queries/replace-preview",
|
|
8188
|
+
summary: "Preview the impact of replacing tracked queries",
|
|
8189
|
+
description: "Read-only impact summary backing `canonry query replace --dry-run`. Returns current vs proposed query sets, the added/removed/unchanged diff, and the count of snapshots that would detach (queryId \u2192 NULL; queryText preserved).",
|
|
8190
|
+
tags: ["queries"],
|
|
8191
|
+
parameters: [nameParameter],
|
|
8192
|
+
requestBody: {
|
|
8193
|
+
required: true,
|
|
8194
|
+
content: {
|
|
8195
|
+
"application/json": {
|
|
8196
|
+
schema: {
|
|
8197
|
+
type: "object",
|
|
8198
|
+
required: ["queries"],
|
|
8199
|
+
properties: {
|
|
8200
|
+
queries: stringArraySchema
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
8203
|
+
}
|
|
8204
|
+
}
|
|
8205
|
+
},
|
|
8206
|
+
responses: {
|
|
8207
|
+
200: { description: "Replace preview returned." },
|
|
8208
|
+
404: { description: "Project not found." }
|
|
8209
|
+
}
|
|
8210
|
+
},
|
|
8102
8211
|
{
|
|
8103
8212
|
method: "post",
|
|
8104
8213
|
path: "/api/v1/projects/{name}/queries/generate",
|
|
@@ -10941,8 +11050,8 @@ async function openApiRoutes(app, opts = {}) {
|
|
|
10941
11050
|
return reply.type("application/json").send(buildOpenApiDocument(opts));
|
|
10942
11051
|
});
|
|
10943
11052
|
}
|
|
10944
|
-
function buildOperationId(method,
|
|
10945
|
-
const parts =
|
|
11053
|
+
function buildOperationId(method, path16) {
|
|
11054
|
+
const parts = path16.split("/").filter(Boolean).map((part) => {
|
|
10946
11055
|
if (part.startsWith("{") && part.endsWith("}")) {
|
|
10947
11056
|
return `by-${part.slice(1, -1)}`;
|
|
10948
11057
|
}
|
|
@@ -11378,7 +11487,7 @@ function formatNotification(row) {
|
|
|
11378
11487
|
|
|
11379
11488
|
// ../api-routes/src/google.ts
|
|
11380
11489
|
import crypto14 from "crypto";
|
|
11381
|
-
import { eq as eq18, and as and9, desc as desc8, sql as
|
|
11490
|
+
import { eq as eq18, and as and9, desc as desc8, sql as sql7 } from "drizzle-orm";
|
|
11382
11491
|
|
|
11383
11492
|
// ../integration-google/src/constants.ts
|
|
11384
11493
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
@@ -12604,11 +12713,11 @@ async function googleRoutes(app, opts) {
|
|
|
12604
12713
|
const { startDate, endDate, query, page, limit, offset } = request.query;
|
|
12605
12714
|
const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
|
|
12606
12715
|
const conditions = [eq18(gscSearchData.projectId, project.id)];
|
|
12607
|
-
if (startDate) conditions.push(
|
|
12608
|
-
else if (cutoffDate) conditions.push(
|
|
12609
|
-
if (endDate) conditions.push(
|
|
12610
|
-
if (query) conditions.push(
|
|
12611
|
-
if (page) conditions.push(
|
|
12716
|
+
if (startDate) conditions.push(sql7`${gscSearchData.date} >= ${startDate}`);
|
|
12717
|
+
else if (cutoffDate) conditions.push(sql7`${gscSearchData.date} >= ${cutoffDate}`);
|
|
12718
|
+
if (endDate) conditions.push(sql7`${gscSearchData.date} <= ${endDate}`);
|
|
12719
|
+
if (query) conditions.push(sql7`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
|
|
12720
|
+
if (page) conditions.push(sql7`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
|
|
12612
12721
|
const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
|
|
12613
12722
|
const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
|
|
12614
12723
|
const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
|
|
@@ -13853,7 +13962,7 @@ async function cdpRoutes(app, opts) {
|
|
|
13853
13962
|
|
|
13854
13963
|
// ../api-routes/src/ga.ts
|
|
13855
13964
|
import crypto16 from "crypto";
|
|
13856
|
-
import { eq as eq21, desc as desc10, and as and12, sql as
|
|
13965
|
+
import { eq as eq21, desc as desc10, and as and12, sql as sql8 } from "drizzle-orm";
|
|
13857
13966
|
function gaLog(level, action, ctx) {
|
|
13858
13967
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
|
|
13859
13968
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
@@ -14150,8 +14259,8 @@ async function ga4Routes(app, opts) {
|
|
|
14150
14259
|
tx.delete(gaTrafficSnapshots).where(
|
|
14151
14260
|
and12(
|
|
14152
14261
|
eq21(gaTrafficSnapshots.projectId, project.id),
|
|
14153
|
-
|
|
14154
|
-
|
|
14262
|
+
sql8`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
|
|
14263
|
+
sql8`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
|
|
14155
14264
|
)
|
|
14156
14265
|
).run();
|
|
14157
14266
|
for (const row of rows) {
|
|
@@ -14174,8 +14283,8 @@ async function ga4Routes(app, opts) {
|
|
|
14174
14283
|
tx.delete(gaAiReferrals).where(
|
|
14175
14284
|
and12(
|
|
14176
14285
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14177
|
-
|
|
14178
|
-
|
|
14286
|
+
sql8`${gaAiReferrals.date} >= ${summary.periodStart}`,
|
|
14287
|
+
sql8`${gaAiReferrals.date} <= ${summary.periodEnd}`
|
|
14179
14288
|
)
|
|
14180
14289
|
).run();
|
|
14181
14290
|
for (const row of aiReferrals) {
|
|
@@ -14200,8 +14309,8 @@ async function ga4Routes(app, opts) {
|
|
|
14200
14309
|
tx.delete(gaSocialReferrals).where(
|
|
14201
14310
|
and12(
|
|
14202
14311
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14203
|
-
|
|
14204
|
-
|
|
14312
|
+
sql8`${gaSocialReferrals.date} >= ${summary.periodStart}`,
|
|
14313
|
+
sql8`${gaSocialReferrals.date} <= ${summary.periodEnd}`
|
|
14205
14314
|
)
|
|
14206
14315
|
).run();
|
|
14207
14316
|
for (const row of socialReferrals) {
|
|
@@ -14291,11 +14400,11 @@ async function ga4Routes(app, opts) {
|
|
|
14291
14400
|
const cutoff = windowCutoff(window);
|
|
14292
14401
|
const cutoffDate = cutoff?.slice(0, 10) ?? null;
|
|
14293
14402
|
const snapshotConditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14294
|
-
if (cutoffDate) snapshotConditions.push(
|
|
14403
|
+
if (cutoffDate) snapshotConditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14295
14404
|
const aiConditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14296
|
-
if (cutoffDate) aiConditions.push(
|
|
14405
|
+
if (cutoffDate) aiConditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14297
14406
|
const socialConditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14298
|
-
if (cutoffDate) socialConditions.push(
|
|
14407
|
+
if (cutoffDate) socialConditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14299
14408
|
const windowSummaryRow = cutoffDate ? app.db.select({
|
|
14300
14409
|
totalSessions: gaTrafficWindowSummaries.totalSessions,
|
|
14301
14410
|
totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
|
|
@@ -14308,9 +14417,9 @@ async function ga4Routes(app, opts) {
|
|
|
14308
14417
|
)
|
|
14309
14418
|
).get() : null;
|
|
14310
14419
|
const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
|
|
14311
|
-
totalSessions:
|
|
14312
|
-
totalOrganicSessions:
|
|
14313
|
-
totalUsers:
|
|
14420
|
+
totalSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
|
|
14421
|
+
totalOrganicSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
|
|
14422
|
+
totalUsers: sql8`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
|
|
14314
14423
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
|
|
14315
14424
|
const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
|
|
14316
14425
|
totalSessions: gaTrafficSummaries.totalSessions,
|
|
@@ -14318,38 +14427,38 @@ async function ga4Routes(app, opts) {
|
|
|
14318
14427
|
totalUsers: gaTrafficSummaries.totalUsers
|
|
14319
14428
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14320
14429
|
const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
|
|
14321
|
-
totalDirectSessions:
|
|
14430
|
+
totalDirectSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
|
|
14322
14431
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
|
|
14323
14432
|
const summaryMeta = app.db.select({
|
|
14324
14433
|
periodStart: gaTrafficSummaries.periodStart,
|
|
14325
14434
|
periodEnd: gaTrafficSummaries.periodEnd
|
|
14326
14435
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14327
14436
|
const rows = app.db.select({
|
|
14328
|
-
landingPage:
|
|
14329
|
-
sessions:
|
|
14330
|
-
organicSessions:
|
|
14331
|
-
directSessions:
|
|
14332
|
-
users:
|
|
14333
|
-
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(
|
|
14437
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14438
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14439
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14440
|
+
directSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
|
|
14441
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14442
|
+
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
|
|
14334
14443
|
const aiReferralRows = app.db.select({
|
|
14335
14444
|
source: gaAiReferrals.source,
|
|
14336
14445
|
medium: gaAiReferrals.medium,
|
|
14337
14446
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14338
|
-
sessions:
|
|
14339
|
-
users:
|
|
14447
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14448
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14340
14449
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
|
|
14341
14450
|
const aiReferralLandingPageRows = app.db.select({
|
|
14342
14451
|
source: gaAiReferrals.source,
|
|
14343
14452
|
medium: gaAiReferrals.medium,
|
|
14344
14453
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14345
|
-
landingPage:
|
|
14346
|
-
sessions:
|
|
14347
|
-
users:
|
|
14454
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14455
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14456
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14348
14457
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
|
|
14349
14458
|
gaAiReferrals.source,
|
|
14350
14459
|
gaAiReferrals.medium,
|
|
14351
14460
|
gaAiReferrals.sourceDimension,
|
|
14352
|
-
|
|
14461
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14353
14462
|
).all();
|
|
14354
14463
|
const aiReferrals = pickWinningDimension(
|
|
14355
14464
|
aiReferralRows,
|
|
@@ -14360,10 +14469,10 @@ async function ga4Routes(app, opts) {
|
|
|
14360
14469
|
(r) => `${r.source}\0${r.medium}\0${r.landingPage}`
|
|
14361
14470
|
);
|
|
14362
14471
|
const aiDeduped = app.db.select({
|
|
14363
|
-
sessions:
|
|
14364
|
-
users:
|
|
14472
|
+
sessions: sql8`COALESCE(SUM(max_sessions), 0)`,
|
|
14473
|
+
users: sql8`COALESCE(SUM(max_users), 0)`
|
|
14365
14474
|
}).from(
|
|
14366
|
-
|
|
14475
|
+
sql8`(
|
|
14367
14476
|
SELECT date, source, medium,
|
|
14368
14477
|
MAX(dimension_sessions) AS max_sessions,
|
|
14369
14478
|
MAX(dimension_users) AS max_users
|
|
@@ -14372,7 +14481,7 @@ async function ga4Routes(app, opts) {
|
|
|
14372
14481
|
SUM(sessions) AS dimension_sessions,
|
|
14373
14482
|
SUM(users) AS dimension_users
|
|
14374
14483
|
FROM ga_ai_referrals
|
|
14375
|
-
WHERE project_id = ${project.id}${cutoffDate ?
|
|
14484
|
+
WHERE project_id = ${project.id}${cutoffDate ? sql8` AND date >= ${cutoffDate}` : sql8``}
|
|
14376
14485
|
GROUP BY date, source, medium, source_dimension
|
|
14377
14486
|
)
|
|
14378
14487
|
GROUP BY date, source, medium
|
|
@@ -14380,8 +14489,8 @@ async function ga4Routes(app, opts) {
|
|
|
14380
14489
|
).get();
|
|
14381
14490
|
const aiBySessionRows = app.db.select({
|
|
14382
14491
|
channelGroup: gaAiReferrals.channelGroup,
|
|
14383
|
-
sessions:
|
|
14384
|
-
users:
|
|
14492
|
+
sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
14493
|
+
users: sql8`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
14385
14494
|
}).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
|
|
14386
14495
|
const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
|
|
14387
14496
|
let aiBySessionUsers = 0;
|
|
@@ -14394,12 +14503,12 @@ async function ga4Routes(app, opts) {
|
|
|
14394
14503
|
source: gaSocialReferrals.source,
|
|
14395
14504
|
medium: gaSocialReferrals.medium,
|
|
14396
14505
|
channelGroup: gaSocialReferrals.channelGroup,
|
|
14397
|
-
sessions:
|
|
14398
|
-
users:
|
|
14399
|
-
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(
|
|
14506
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14507
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14508
|
+
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
|
|
14400
14509
|
const socialTotals = app.db.select({
|
|
14401
|
-
sessions:
|
|
14402
|
-
users:
|
|
14510
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14511
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14403
14512
|
}).from(gaSocialReferrals).where(and12(...socialConditions)).get();
|
|
14404
14513
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
14405
14514
|
const total = summaryRow?.totalSessions ?? 0;
|
|
@@ -14482,21 +14591,21 @@ async function ga4Routes(app, opts) {
|
|
|
14482
14591
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14483
14592
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14484
14593
|
const conditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14485
|
-
if (cutoffDate) conditions.push(
|
|
14594
|
+
if (cutoffDate) conditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14486
14595
|
const rows = app.db.select({
|
|
14487
14596
|
date: gaAiReferrals.date,
|
|
14488
14597
|
source: gaAiReferrals.source,
|
|
14489
14598
|
medium: gaAiReferrals.medium,
|
|
14490
|
-
landingPage:
|
|
14599
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14491
14600
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14492
|
-
sessions:
|
|
14493
|
-
users:
|
|
14601
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14602
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14494
14603
|
}).from(gaAiReferrals).where(and12(...conditions)).groupBy(
|
|
14495
14604
|
gaAiReferrals.date,
|
|
14496
14605
|
gaAiReferrals.source,
|
|
14497
14606
|
gaAiReferrals.medium,
|
|
14498
14607
|
gaAiReferrals.sourceDimension,
|
|
14499
|
-
|
|
14608
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14500
14609
|
).orderBy(gaAiReferrals.date).all();
|
|
14501
14610
|
return rows;
|
|
14502
14611
|
});
|
|
@@ -14505,7 +14614,7 @@ async function ga4Routes(app, opts) {
|
|
|
14505
14614
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14506
14615
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14507
14616
|
const conditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14508
|
-
if (cutoffDate) conditions.push(
|
|
14617
|
+
if (cutoffDate) conditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14509
14618
|
const rows = app.db.select({
|
|
14510
14619
|
date: gaSocialReferrals.date,
|
|
14511
14620
|
source: gaSocialReferrals.source,
|
|
@@ -14526,10 +14635,10 @@ async function ga4Routes(app, opts) {
|
|
|
14526
14635
|
d.setDate(d.getDate() - n);
|
|
14527
14636
|
return fmt(d);
|
|
14528
14637
|
};
|
|
14529
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14638
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
|
|
14530
14639
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14531
|
-
|
|
14532
|
-
|
|
14640
|
+
sql8`${gaSocialReferrals.date} >= ${from}`,
|
|
14641
|
+
sql8`${gaSocialReferrals.date} < ${to}`
|
|
14533
14642
|
)).get();
|
|
14534
14643
|
const current7d = sumSocial(daysAgo2(7), fmt(today));
|
|
14535
14644
|
const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
|
|
@@ -14538,19 +14647,19 @@ async function ga4Routes(app, opts) {
|
|
|
14538
14647
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
14539
14648
|
const sourceCurrent = app.db.select({
|
|
14540
14649
|
source: gaSocialReferrals.source,
|
|
14541
|
-
sessions:
|
|
14650
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14542
14651
|
}).from(gaSocialReferrals).where(and12(
|
|
14543
14652
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14544
|
-
|
|
14545
|
-
|
|
14653
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
|
|
14654
|
+
sql8`${gaSocialReferrals.date} < ${fmt(today)}`
|
|
14546
14655
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14547
14656
|
const sourcePrev = app.db.select({
|
|
14548
14657
|
source: gaSocialReferrals.source,
|
|
14549
|
-
sessions:
|
|
14658
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14550
14659
|
}).from(gaSocialReferrals).where(and12(
|
|
14551
14660
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14552
|
-
|
|
14553
|
-
|
|
14661
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
|
|
14662
|
+
sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`
|
|
14554
14663
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14555
14664
|
const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
|
|
14556
14665
|
let biggestMover = null;
|
|
@@ -14589,16 +14698,16 @@ async function ga4Routes(app, opts) {
|
|
|
14589
14698
|
return fmt(d);
|
|
14590
14699
|
};
|
|
14591
14700
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
14592
|
-
const sumTotal = (from, to) => app.db.select({ sessions:
|
|
14593
|
-
const sumOrganic = (from, to) => app.db.select({ sessions:
|
|
14594
|
-
const sumDirect = (from, to) => app.db.select({ sessions:
|
|
14595
|
-
const sumAi = (from, to) => app.db.select({ sessions:
|
|
14701
|
+
const sumTotal = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14702
|
+
const sumOrganic = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14703
|
+
const sumDirect = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14704
|
+
const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14596
14705
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14597
|
-
|
|
14598
|
-
|
|
14706
|
+
sql8`${gaAiReferrals.date} >= ${from}`,
|
|
14707
|
+
sql8`${gaAiReferrals.date} < ${to}`,
|
|
14599
14708
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14600
14709
|
)).get();
|
|
14601
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14710
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${from}`, sql8`${gaSocialReferrals.date} < ${to}`)).get();
|
|
14602
14711
|
const todayStr = fmt(today);
|
|
14603
14712
|
const buildTrend = (sum) => {
|
|
14604
14713
|
const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
|
|
@@ -14607,16 +14716,16 @@ async function ga4Routes(app, opts) {
|
|
|
14607
14716
|
const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
|
|
14608
14717
|
return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
|
|
14609
14718
|
};
|
|
14610
|
-
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14719
|
+
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14611
14720
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14612
|
-
|
|
14613
|
-
|
|
14721
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
|
|
14722
|
+
sql8`${gaAiReferrals.date} < ${todayStr}`,
|
|
14614
14723
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14615
14724
|
)).groupBy(gaAiReferrals.source).all();
|
|
14616
|
-
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14725
|
+
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14617
14726
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14618
|
-
|
|
14619
|
-
|
|
14727
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
|
|
14728
|
+
sql8`${gaAiReferrals.date} < ${daysAgo2(7)}`,
|
|
14620
14729
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14621
14730
|
)).groupBy(gaAiReferrals.source).all();
|
|
14622
14731
|
const findBiggestMover = (current, prev) => {
|
|
@@ -14633,8 +14742,8 @@ async function ga4Routes(app, opts) {
|
|
|
14633
14742
|
}
|
|
14634
14743
|
return mover;
|
|
14635
14744
|
};
|
|
14636
|
-
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14637
|
-
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14745
|
+
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql8`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
|
|
14746
|
+
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
|
|
14638
14747
|
return {
|
|
14639
14748
|
total: buildTrend(sumTotal),
|
|
14640
14749
|
organic: buildTrend(sumOrganic),
|
|
@@ -14650,12 +14759,12 @@ async function ga4Routes(app, opts) {
|
|
|
14650
14759
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14651
14760
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14652
14761
|
const conditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14653
|
-
if (cutoffDate) conditions.push(
|
|
14762
|
+
if (cutoffDate) conditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14654
14763
|
const rows = app.db.select({
|
|
14655
14764
|
date: gaTrafficSnapshots.date,
|
|
14656
|
-
sessions:
|
|
14657
|
-
organicSessions:
|
|
14658
|
-
users:
|
|
14765
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14766
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14767
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14659
14768
|
}).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
|
|
14660
14769
|
return rows.map((r) => ({
|
|
14661
14770
|
date: r.date,
|
|
@@ -14668,11 +14777,11 @@ async function ga4Routes(app, opts) {
|
|
|
14668
14777
|
const project = resolveProject(app.db, request.params.name);
|
|
14669
14778
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14670
14779
|
const trafficPages = app.db.select({
|
|
14671
|
-
landingPage:
|
|
14672
|
-
sessions:
|
|
14673
|
-
organicSessions:
|
|
14674
|
-
users:
|
|
14675
|
-
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(
|
|
14780
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14781
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14782
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14783
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14784
|
+
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
|
|
14676
14785
|
return {
|
|
14677
14786
|
pages: trafficPages.map((r) => ({
|
|
14678
14787
|
landingPage: r.landingPage,
|
|
@@ -14888,10 +14997,10 @@ function buildAuthErrorMessage(res, responseText) {
|
|
|
14888
14997
|
}
|
|
14889
14998
|
return "WordPress credentials are invalid or lack permission for this action";
|
|
14890
14999
|
}
|
|
14891
|
-
async function fetchJson(connection, siteUrl,
|
|
15000
|
+
async function fetchJson(connection, siteUrl, path16, init) {
|
|
14892
15001
|
if (siteUrl.startsWith("http:")) {
|
|
14893
15002
|
}
|
|
14894
|
-
const res = await fetch(`${normalizeSiteUrl(siteUrl)}${
|
|
15003
|
+
const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path16}`, {
|
|
14895
15004
|
...init,
|
|
14896
15005
|
headers: {
|
|
14897
15006
|
"Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
|
|
@@ -16309,7 +16418,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
16309
16418
|
|
|
16310
16419
|
// ../api-routes/src/backlinks.ts
|
|
16311
16420
|
import crypto18 from "crypto";
|
|
16312
|
-
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as
|
|
16421
|
+
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
|
|
16313
16422
|
|
|
16314
16423
|
// ../integration-commoncrawl/src/constants.ts
|
|
16315
16424
|
import os3 from "os";
|
|
@@ -16593,7 +16702,7 @@ async function queryBacklinks(opts) {
|
|
|
16593
16702
|
const reversed = opts.targets.map(reverseDomain);
|
|
16594
16703
|
const targetList = reversed.map(quote).join(", ");
|
|
16595
16704
|
const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
|
|
16596
|
-
const
|
|
16705
|
+
const sql16 = `
|
|
16597
16706
|
WITH vertices AS (
|
|
16598
16707
|
SELECT * FROM read_csv(
|
|
16599
16708
|
${quote(opts.vertexPath)},
|
|
@@ -16629,7 +16738,7 @@ async function queryBacklinks(opts) {
|
|
|
16629
16738
|
const conn = await instance.connect();
|
|
16630
16739
|
let rows;
|
|
16631
16740
|
try {
|
|
16632
|
-
const reader = await conn.runAndReadAll(
|
|
16741
|
+
const reader = await conn.runAndReadAll(sql16);
|
|
16633
16742
|
rows = reader.getRowObjects();
|
|
16634
16743
|
} finally {
|
|
16635
16744
|
conn.disconnectSync?.();
|
|
@@ -16805,12 +16914,12 @@ function computeFilteredSummary(db, base) {
|
|
|
16805
16914
|
);
|
|
16806
16915
|
const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
|
|
16807
16916
|
const unfilteredAgg = db.select({
|
|
16808
|
-
count:
|
|
16809
|
-
total:
|
|
16917
|
+
count: sql9`count(*)`,
|
|
16918
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16810
16919
|
}).from(backlinkDomains).where(baseDomainCondition).get();
|
|
16811
16920
|
const filteredAgg = db.select({
|
|
16812
|
-
count:
|
|
16813
|
-
total:
|
|
16921
|
+
count: sql9`count(*)`,
|
|
16922
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16814
16923
|
}).from(backlinkDomains).where(filteredCondition).get();
|
|
16815
16924
|
const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc11(backlinkDomains.numHosts)).limit(10).all();
|
|
16816
16925
|
const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
|
|
@@ -16984,7 +17093,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
16984
17093
|
eq22(backlinkDomains.release, targetRelease)
|
|
16985
17094
|
);
|
|
16986
17095
|
const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
|
|
16987
|
-
const totalRow = app.db.select({ count:
|
|
17096
|
+
const totalRow = app.db.select({ count: sql9`count(*)` }).from(backlinkDomains).where(domainCondition).get();
|
|
16988
17097
|
const rows = app.db.select({
|
|
16989
17098
|
linkingDomain: backlinkDomains.linkingDomain,
|
|
16990
17099
|
numHosts: backlinkDomains.numHosts
|
|
@@ -17019,7 +17128,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
17019
17128
|
|
|
17020
17129
|
// ../api-routes/src/traffic.ts
|
|
17021
17130
|
import crypto20 from "crypto";
|
|
17022
|
-
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as
|
|
17131
|
+
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
|
|
17023
17132
|
|
|
17024
17133
|
// ../integration-cloud-run/src/auth.ts
|
|
17025
17134
|
import crypto19 from "crypto";
|
|
@@ -17518,8 +17627,8 @@ var ASSET_PATH_PREFIXES = [
|
|
|
17518
17627
|
"/img/",
|
|
17519
17628
|
"/static/"
|
|
17520
17629
|
];
|
|
17521
|
-
function normalizeTrafficPathPattern(
|
|
17522
|
-
const cleanPath =
|
|
17630
|
+
function normalizeTrafficPathPattern(path16) {
|
|
17631
|
+
const cleanPath = path16.trim() || "/";
|
|
17523
17632
|
const pathOnly = cleanPath.split("?")[0] || "/";
|
|
17524
17633
|
const segments = pathOnly.split("/").map((segment) => {
|
|
17525
17634
|
if (!segment) return segment;
|
|
@@ -17568,8 +17677,8 @@ function resolveAiReferralLandingPath(event, evidenceType) {
|
|
|
17568
17677
|
}
|
|
17569
17678
|
return normalizeTrafficPathPattern(event.path);
|
|
17570
17679
|
}
|
|
17571
|
-
function isLikelySubresourcePath(
|
|
17572
|
-
const cleanPath =
|
|
17680
|
+
function isLikelySubresourcePath(path16) {
|
|
17681
|
+
const cleanPath = path16.split("?")[0] || "/";
|
|
17573
17682
|
return ASSET_PATH_PREFIXES.some((prefix) => cleanPath.startsWith(prefix)) || ASSET_EXTENSION_PATTERN.test(cleanPath);
|
|
17574
17683
|
}
|
|
17575
17684
|
function actorKey(event) {
|
|
@@ -17755,11 +17864,11 @@ function buildEventId2(event) {
|
|
|
17755
17864
|
function normalizeWordpressTrafficEvent(event) {
|
|
17756
17865
|
if (!event.observed_at) return null;
|
|
17757
17866
|
if (typeof event.id !== "number" || !Number.isFinite(event.id)) return null;
|
|
17758
|
-
const
|
|
17759
|
-
if (!
|
|
17867
|
+
const path16 = event.path?.trim();
|
|
17868
|
+
if (!path16) return null;
|
|
17760
17869
|
const queryString = trimOrNull(event.query_string);
|
|
17761
17870
|
const host = trimOrNull(event.host);
|
|
17762
|
-
const requestUrl = host ? `https://${host}${
|
|
17871
|
+
const requestUrl = host ? `https://${host}${path16}${queryString ? `?${queryString}` : ""}` : `${path16}${queryString ? `?${queryString}` : ""}`;
|
|
17763
17872
|
return {
|
|
17764
17873
|
sourceType: TrafficSourceTypes.wordpress,
|
|
17765
17874
|
evidenceKind: TrafficEvidenceKinds["raw-request"],
|
|
@@ -17769,7 +17878,7 @@ function normalizeWordpressTrafficEvent(event) {
|
|
|
17769
17878
|
method: trimOrNull(event.method),
|
|
17770
17879
|
requestUrl,
|
|
17771
17880
|
host,
|
|
17772
|
-
path:
|
|
17881
|
+
path: path16,
|
|
17773
17882
|
queryString,
|
|
17774
17883
|
status: typeof event.status === "number" && Number.isFinite(event.status) ? event.status : null,
|
|
17775
17884
|
userAgent: trimOrNull(event.user_agent),
|
|
@@ -17933,15 +18042,15 @@ function stringLabels(input) {
|
|
|
17933
18042
|
);
|
|
17934
18043
|
}
|
|
17935
18044
|
function normalizeVercelLogRow(row) {
|
|
17936
|
-
const
|
|
17937
|
-
if (!
|
|
18045
|
+
const path16 = row.requestPath;
|
|
18046
|
+
if (!path16) return null;
|
|
17938
18047
|
const observedAt = row.timestamp;
|
|
17939
18048
|
if (!observedAt) return null;
|
|
17940
18049
|
const requestId = row.requestId;
|
|
17941
18050
|
if (!requestId) return null;
|
|
17942
18051
|
const host = emptyToNull(row.domain);
|
|
17943
18052
|
const queryString = serializeSearchParams(row.requestSearchParams);
|
|
17944
|
-
const requestUrl = host ? `https://${host}${
|
|
18053
|
+
const requestUrl = host ? `https://${host}${path16}${queryString ? `?${queryString}` : ""}` : null;
|
|
17945
18054
|
return {
|
|
17946
18055
|
sourceType: TrafficSourceTypes.vercel,
|
|
17947
18056
|
evidenceKind: TrafficEvidenceKinds["raw-request"],
|
|
@@ -17951,7 +18060,7 @@ function normalizeVercelLogRow(row) {
|
|
|
17951
18060
|
method: row.requestMethod ?? null,
|
|
17952
18061
|
requestUrl,
|
|
17953
18062
|
host,
|
|
17954
|
-
path:
|
|
18063
|
+
path: path16,
|
|
17955
18064
|
queryString,
|
|
17956
18065
|
status: resolveStatus(row),
|
|
17957
18066
|
userAgent: emptyToNull(row.clientUserAgent),
|
|
@@ -18757,7 +18866,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18757
18866
|
crawlerEventsHourly.status
|
|
18758
18867
|
],
|
|
18759
18868
|
set: {
|
|
18760
|
-
hits:
|
|
18869
|
+
hits: sql10`${crawlerEventsHourly.hits} + ${bucket.hits}`,
|
|
18761
18870
|
sampledUserAgent: bucket.sampledUserAgent,
|
|
18762
18871
|
updatedAt: finishedAt
|
|
18763
18872
|
}
|
|
@@ -18792,7 +18901,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18792
18901
|
aiReferralEventsHourly.status
|
|
18793
18902
|
],
|
|
18794
18903
|
set: {
|
|
18795
|
-
sessionsOrHits:
|
|
18904
|
+
sessionsOrHits: sql10`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
|
|
18796
18905
|
updatedAt: finishedAt
|
|
18797
18906
|
}
|
|
18798
18907
|
}).run();
|
|
@@ -19044,19 +19153,19 @@ async function trafficRoutes(app, opts) {
|
|
|
19044
19153
|
return response;
|
|
19045
19154
|
});
|
|
19046
19155
|
function buildSourceDetail(projectId, row, since) {
|
|
19047
|
-
const crawlerTotals = app.db.select({ total:
|
|
19156
|
+
const crawlerTotals = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
19048
19157
|
and15(
|
|
19049
19158
|
eq23(crawlerEventsHourly.sourceId, row.id),
|
|
19050
19159
|
gte2(crawlerEventsHourly.tsHour, since)
|
|
19051
19160
|
)
|
|
19052
19161
|
).get();
|
|
19053
|
-
const aiTotals = app.db.select({ total:
|
|
19162
|
+
const aiTotals = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
19054
19163
|
and15(
|
|
19055
19164
|
eq23(aiReferralEventsHourly.sourceId, row.id),
|
|
19056
19165
|
gte2(aiReferralEventsHourly.tsHour, since)
|
|
19057
19166
|
)
|
|
19058
19167
|
).get();
|
|
19059
|
-
const sampleTotals = app.db.select({ total:
|
|
19168
|
+
const sampleTotals = app.db.select({ total: sql10`COUNT(*)` }).from(rawEventSamples).where(
|
|
19060
19169
|
and15(
|
|
19061
19170
|
eq23(rawEventSamples.sourceId, row.id),
|
|
19062
19171
|
gte2(rawEventSamples.ts, since)
|
|
@@ -19158,7 +19267,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19158
19267
|
];
|
|
19159
19268
|
if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
19160
19269
|
const crawlerWhere = and15(...crawlerFilters);
|
|
19161
|
-
const total = app.db.select({ total:
|
|
19270
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
|
|
19162
19271
|
crawlerTotal = Number(total?.total ?? 0);
|
|
19163
19272
|
const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
|
|
19164
19273
|
for (const r of rows) {
|
|
@@ -19183,7 +19292,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19183
19292
|
];
|
|
19184
19293
|
if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
19185
19294
|
const aiWhere = and15(...aiFilters);
|
|
19186
|
-
const total = app.db.select({ total:
|
|
19295
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
19187
19296
|
aiReferralTotal = Number(total?.total ?? 0);
|
|
19188
19297
|
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
19189
19298
|
for (const r of rows) {
|
|
@@ -19216,6 +19325,75 @@ async function trafficRoutes(app, opts) {
|
|
|
19216
19325
|
});
|
|
19217
19326
|
}
|
|
19218
19327
|
|
|
19328
|
+
// ../api-routes/src/doctor/checks/agent.ts
|
|
19329
|
+
import fs6 from "fs";
|
|
19330
|
+
import path7 from "path";
|
|
19331
|
+
var REQUIRED_SKILLS = ["canonry", "aero"];
|
|
19332
|
+
var skillsInstalledCheck = {
|
|
19333
|
+
id: "agent.skills.installed",
|
|
19334
|
+
category: CheckCategories.agent,
|
|
19335
|
+
scope: CheckScopes.global,
|
|
19336
|
+
title: "Agent skills installed (~/.claude/skills/)",
|
|
19337
|
+
run: () => {
|
|
19338
|
+
const home = process.env.HOME;
|
|
19339
|
+
if (!home) {
|
|
19340
|
+
return {
|
|
19341
|
+
status: CheckStatuses.skipped,
|
|
19342
|
+
code: "agent.skills.no-home",
|
|
19343
|
+
summary: "Cannot determine $HOME \u2014 skip skills filesystem check.",
|
|
19344
|
+
remediation: null
|
|
19345
|
+
};
|
|
19346
|
+
}
|
|
19347
|
+
const skillsBase = path7.join(home, ".claude", "skills");
|
|
19348
|
+
const installed = [];
|
|
19349
|
+
const missing = [];
|
|
19350
|
+
for (const name of REQUIRED_SKILLS) {
|
|
19351
|
+
const dir = path7.join(skillsBase, name);
|
|
19352
|
+
if (isInstalled(dir)) installed.push(name);
|
|
19353
|
+
else missing.push(name);
|
|
19354
|
+
}
|
|
19355
|
+
const details = {
|
|
19356
|
+
checkedPath: skillsBase,
|
|
19357
|
+
installed,
|
|
19358
|
+
missing
|
|
19359
|
+
};
|
|
19360
|
+
if (missing.length === 0) {
|
|
19361
|
+
return {
|
|
19362
|
+
status: CheckStatuses.ok,
|
|
19363
|
+
code: "agent.skills.installed",
|
|
19364
|
+
summary: `Both canonry and aero skills are installed in ${skillsBase}.`,
|
|
19365
|
+
remediation: null,
|
|
19366
|
+
details
|
|
19367
|
+
};
|
|
19368
|
+
}
|
|
19369
|
+
if (installed.length === 0) {
|
|
19370
|
+
return {
|
|
19371
|
+
status: CheckStatuses.warn,
|
|
19372
|
+
code: "agent.skills.not-installed",
|
|
19373
|
+
summary: "Agent skills are not installed for Claude Code on this machine. Claude sessions on this host will not auto-load canonry/aero reference docs.",
|
|
19374
|
+
remediation: "Run `canonry skills install --dir ~` (or `canonry skills install --user`) to install both skills to ~/.claude/skills/ and ~/.codex/skills/.",
|
|
19375
|
+
details
|
|
19376
|
+
};
|
|
19377
|
+
}
|
|
19378
|
+
return {
|
|
19379
|
+
status: CheckStatuses.warn,
|
|
19380
|
+
code: "agent.skills.partial",
|
|
19381
|
+
summary: `Only ${installed.length} of ${REQUIRED_SKILLS.length} agent skills are installed (${installed.join(", ")}); ${missing.join(", ")} missing.`,
|
|
19382
|
+
remediation: `Run \`canonry skills install ${missing.join(" ")} --dir ~\` to fill the gap.`,
|
|
19383
|
+
details
|
|
19384
|
+
};
|
|
19385
|
+
}
|
|
19386
|
+
};
|
|
19387
|
+
function isInstalled(dir) {
|
|
19388
|
+
try {
|
|
19389
|
+
if (!fs6.existsSync(dir)) return false;
|
|
19390
|
+
return fs6.existsSync(path7.join(dir, "SKILL.md"));
|
|
19391
|
+
} catch {
|
|
19392
|
+
return false;
|
|
19393
|
+
}
|
|
19394
|
+
}
|
|
19395
|
+
var AGENT_CHECKS = [skillsInstalledCheck];
|
|
19396
|
+
|
|
19219
19397
|
// ../api-routes/src/doctor/checks/bing-auth.ts
|
|
19220
19398
|
var BING_AUTH_CHECKS = [
|
|
19221
19399
|
{
|
|
@@ -19837,7 +20015,7 @@ var providersConfiguredCheck = {
|
|
|
19837
20015
|
var PROVIDERS_CHECKS = [providersConfiguredCheck];
|
|
19838
20016
|
|
|
19839
20017
|
// ../api-routes/src/doctor/checks/traffic-source.ts
|
|
19840
|
-
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as
|
|
20018
|
+
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql11 } from "drizzle-orm";
|
|
19841
20019
|
var RECENT_DATA_WARN_DAYS = 7;
|
|
19842
20020
|
var RECENT_DATA_FAIL_DAYS = 30;
|
|
19843
20021
|
function skippedNoProject2() {
|
|
@@ -19931,7 +20109,7 @@ var recentDataCheck = {
|
|
|
19931
20109
|
const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
|
|
19932
20110
|
const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
|
|
19933
20111
|
const recentCrawlers = Number(
|
|
19934
|
-
ctx.db.select({ total:
|
|
20112
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
19935
20113
|
and16(
|
|
19936
20114
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
19937
20115
|
gte3(crawlerEventsHourly.tsHour, warnCutoff)
|
|
@@ -19939,7 +20117,7 @@ var recentDataCheck = {
|
|
|
19939
20117
|
).get()?.total ?? 0
|
|
19940
20118
|
);
|
|
19941
20119
|
const recentReferrals = Number(
|
|
19942
|
-
ctx.db.select({ total:
|
|
20120
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
19943
20121
|
and16(
|
|
19944
20122
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
19945
20123
|
gte3(aiReferralEventsHourly.tsHour, warnCutoff)
|
|
@@ -19955,7 +20133,7 @@ var recentDataCheck = {
|
|
|
19955
20133
|
};
|
|
19956
20134
|
}
|
|
19957
20135
|
const olderCrawlers = Number(
|
|
19958
|
-
ctx.db.select({ total:
|
|
20136
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
19959
20137
|
and16(
|
|
19960
20138
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
19961
20139
|
gte3(crawlerEventsHourly.tsHour, failCutoff)
|
|
@@ -19963,7 +20141,7 @@ var recentDataCheck = {
|
|
|
19963
20141
|
).get()?.total ?? 0
|
|
19964
20142
|
);
|
|
19965
20143
|
const olderReferrals = Number(
|
|
19966
|
-
ctx.db.select({ total:
|
|
20144
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
19967
20145
|
and16(
|
|
19968
20146
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
19969
20147
|
gte3(aiReferralEventsHourly.tsHour, failCutoff)
|
|
@@ -20145,7 +20323,8 @@ var ALL_CHECKS = [
|
|
|
20145
20323
|
...BING_AUTH_CHECKS,
|
|
20146
20324
|
...GA_AUTH_CHECKS,
|
|
20147
20325
|
...PROVIDERS_CHECKS,
|
|
20148
|
-
...TRAFFIC_SOURCE_CHECKS
|
|
20326
|
+
...TRAFFIC_SOURCE_CHECKS,
|
|
20327
|
+
...AGENT_CHECKS
|
|
20149
20328
|
];
|
|
20150
20329
|
var CHECK_BY_ID = Object.fromEntries(
|
|
20151
20330
|
ALL_CHECKS.map((check) => [check.id, check])
|
|
@@ -20262,7 +20441,7 @@ async function doctorRoutes(app, opts) {
|
|
|
20262
20441
|
|
|
20263
20442
|
// ../api-routes/src/discovery/routes.ts
|
|
20264
20443
|
import crypto21 from "crypto";
|
|
20265
|
-
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as
|
|
20444
|
+
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
|
|
20266
20445
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
20267
20446
|
async function discoveryRoutes(app, opts) {
|
|
20268
20447
|
app.post("/projects/:name/discover/run", async (request, reply) => {
|
|
@@ -20297,7 +20476,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
20297
20476
|
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and17(
|
|
20298
20477
|
eq25(discoverySessions.projectId, project.id),
|
|
20299
20478
|
eq25(discoverySessions.icpDescription, icpDescription),
|
|
20300
|
-
|
|
20479
|
+
inArray9(discoverySessions.status, [
|
|
20301
20480
|
DiscoverySessionStatuses.queued,
|
|
20302
20481
|
DiscoverySessionStatuses.seeding,
|
|
20303
20482
|
DiscoverySessionStatuses.probing
|
|
@@ -22450,7 +22629,7 @@ var localAdapter = {
|
|
|
22450
22629
|
};
|
|
22451
22630
|
|
|
22452
22631
|
// ../provider-cdp/src/adapter.ts
|
|
22453
|
-
import
|
|
22632
|
+
import path9 from "path";
|
|
22454
22633
|
import os4 from "os";
|
|
22455
22634
|
|
|
22456
22635
|
// ../provider-cdp/src/connection.ts
|
|
@@ -22815,12 +22994,12 @@ function sleep2(ms) {
|
|
|
22815
22994
|
}
|
|
22816
22995
|
|
|
22817
22996
|
// ../provider-cdp/src/screenshot.ts
|
|
22818
|
-
import
|
|
22819
|
-
import
|
|
22997
|
+
import fs7 from "fs";
|
|
22998
|
+
import path8 from "path";
|
|
22820
22999
|
async function captureElementScreenshot(client, selector, outputPath) {
|
|
22821
|
-
const dir =
|
|
22822
|
-
if (!
|
|
22823
|
-
|
|
23000
|
+
const dir = path8.dirname(outputPath);
|
|
23001
|
+
if (!fs7.existsSync(dir)) {
|
|
23002
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
22824
23003
|
}
|
|
22825
23004
|
let clip;
|
|
22826
23005
|
try {
|
|
@@ -22854,7 +23033,7 @@ async function captureElementScreenshot(client, selector, outputPath) {
|
|
|
22854
23033
|
}
|
|
22855
23034
|
const { data } = await client.Page.captureScreenshot(screenshotParams);
|
|
22856
23035
|
const buffer = Buffer.from(data, "base64");
|
|
22857
|
-
|
|
23036
|
+
fs7.writeFileSync(outputPath, buffer);
|
|
22858
23037
|
return outputPath;
|
|
22859
23038
|
}
|
|
22860
23039
|
|
|
@@ -22915,7 +23094,7 @@ function getConnection(config) {
|
|
|
22915
23094
|
return conn;
|
|
22916
23095
|
}
|
|
22917
23096
|
function getScreenshotDir2() {
|
|
22918
|
-
return
|
|
23097
|
+
return path9.join(os4.homedir(), ".canonry", "screenshots");
|
|
22919
23098
|
}
|
|
22920
23099
|
var cdpChatgptAdapter = {
|
|
22921
23100
|
name: "cdp:chatgpt",
|
|
@@ -22979,7 +23158,7 @@ var cdpChatgptAdapter = {
|
|
|
22979
23158
|
const answerText = await target.extractAnswer(client);
|
|
22980
23159
|
const groundingSources = await target.extractCitations(client);
|
|
22981
23160
|
const screenshotId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
22982
|
-
const screenshotPath =
|
|
23161
|
+
const screenshotPath = path9.join(getScreenshotDir2(), `${screenshotId}.png`);
|
|
22983
23162
|
let capturedScreenshotPath;
|
|
22984
23163
|
try {
|
|
22985
23164
|
capturedScreenshotPath = await captureElementScreenshot(
|
|
@@ -23612,10 +23791,10 @@ function removeWordpressConnection(config, projectName) {
|
|
|
23612
23791
|
|
|
23613
23792
|
// src/job-runner.ts
|
|
23614
23793
|
import crypto24 from "crypto";
|
|
23615
|
-
import
|
|
23616
|
-
import
|
|
23794
|
+
import fs8 from "fs";
|
|
23795
|
+
import path10 from "path";
|
|
23617
23796
|
import os5 from "os";
|
|
23618
|
-
import { and as and18, eq as eq27, inArray as
|
|
23797
|
+
import { and as and18, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
|
|
23619
23798
|
|
|
23620
23799
|
// src/run-telemetry.ts
|
|
23621
23800
|
import crypto23 from "crypto";
|
|
@@ -23960,7 +24139,7 @@ var JobRunner = class {
|
|
|
23960
24139
|
this.registry = registry;
|
|
23961
24140
|
}
|
|
23962
24141
|
recoverStaleRuns() {
|
|
23963
|
-
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(
|
|
24142
|
+
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(inArray10(runs.status, ["running", "queued"])).all();
|
|
23964
24143
|
if (stale.length === 0) return;
|
|
23965
24144
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23966
24145
|
for (const run of stale) {
|
|
@@ -24023,7 +24202,7 @@ var JobRunner = class {
|
|
|
24023
24202
|
}
|
|
24024
24203
|
log.info("run.dispatch", { runId, providerCount: activeProviders.length, providers: activeProviders.map((p) => p.adapter.name) });
|
|
24025
24204
|
const scopedQueryNames = parseJsonColumn(existingRun.queries, null);
|
|
24026
|
-
projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId),
|
|
24205
|
+
projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId), inArray10(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
|
|
24027
24206
|
const projectCompetitors = this.db.select().from(competitors).where(eq27(competitors.projectId, projectId)).all();
|
|
24028
24207
|
const competitorDomains = projectCompetitors.map((c) => c.domain);
|
|
24029
24208
|
const allDomains = effectiveDomains({
|
|
@@ -24106,12 +24285,12 @@ var JobRunner = class {
|
|
|
24106
24285
|
allBrandNames
|
|
24107
24286
|
);
|
|
24108
24287
|
let screenshotRelPath = null;
|
|
24109
|
-
if (raw.screenshotPath &&
|
|
24288
|
+
if (raw.screenshotPath && fs8.existsSync(raw.screenshotPath)) {
|
|
24110
24289
|
const snapshotId = crypto24.randomUUID();
|
|
24111
|
-
const screenshotDir =
|
|
24112
|
-
if (!
|
|
24113
|
-
const destPath =
|
|
24114
|
-
|
|
24290
|
+
const screenshotDir = path10.join(os5.homedir(), ".canonry", "screenshots", runId);
|
|
24291
|
+
if (!fs8.existsSync(screenshotDir)) fs8.mkdirSync(screenshotDir, { recursive: true });
|
|
24292
|
+
const destPath = path10.join(screenshotDir, `${snapshotId}.png`);
|
|
24293
|
+
fs8.renameSync(raw.screenshotPath, destPath);
|
|
24115
24294
|
screenshotRelPath = `${runId}/${snapshotId}.png`;
|
|
24116
24295
|
this.db.insert(querySnapshots).values({
|
|
24117
24296
|
id: snapshotId,
|
|
@@ -24296,7 +24475,7 @@ var JobRunner = class {
|
|
|
24296
24475
|
updatedAt: now
|
|
24297
24476
|
}).onConflictDoUpdate({
|
|
24298
24477
|
target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
|
|
24299
|
-
set: { count:
|
|
24478
|
+
set: { count: sql12`${usageCounters.count} + ${count}`, updatedAt: now }
|
|
24300
24479
|
}).run();
|
|
24301
24480
|
}
|
|
24302
24481
|
flushProviderUsage(projectId, providerDispatchCounts) {
|
|
@@ -24366,7 +24545,7 @@ function buildPhases(input) {
|
|
|
24366
24545
|
|
|
24367
24546
|
// src/gsc-sync.ts
|
|
24368
24547
|
import crypto25 from "crypto";
|
|
24369
|
-
import { eq as eq28, and as and19, sql as
|
|
24548
|
+
import { eq as eq28, and as and19, sql as sql13 } from "drizzle-orm";
|
|
24370
24549
|
var log2 = createLogger("GscSync");
|
|
24371
24550
|
function formatDate3(d) {
|
|
24372
24551
|
return d.toISOString().split("T")[0];
|
|
@@ -24420,8 +24599,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
24420
24599
|
db.delete(gscSearchData).where(
|
|
24421
24600
|
and19(
|
|
24422
24601
|
eq28(gscSearchData.projectId, projectId),
|
|
24423
|
-
|
|
24424
|
-
|
|
24602
|
+
sql13`${gscSearchData.date} >= ${startDate}`,
|
|
24603
|
+
sql13`${gscSearchData.date} <= ${endDate}`
|
|
24425
24604
|
)
|
|
24426
24605
|
).run();
|
|
24427
24606
|
const batchSize = 500;
|
|
@@ -24956,8 +25135,8 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
24956
25135
|
|
|
24957
25136
|
// src/commoncrawl-sync.ts
|
|
24958
25137
|
import crypto28 from "crypto";
|
|
24959
|
-
import
|
|
24960
|
-
import { and as and21, eq as eq31, sql as
|
|
25138
|
+
import path11 from "path";
|
|
25139
|
+
import { and as and21, eq as eq31, sql as sql14 } from "drizzle-orm";
|
|
24961
25140
|
var log6 = createLogger("CommonCrawlSync");
|
|
24962
25141
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
24963
25142
|
function defaultDeps() {
|
|
@@ -24985,9 +25164,9 @@ async function executeReleaseSync(db, syncId, opts) {
|
|
|
24985
25164
|
error: null
|
|
24986
25165
|
}).where(eq31(ccReleaseSyncs.id, syncId)).run();
|
|
24987
25166
|
const paths = ccReleasePaths(release);
|
|
24988
|
-
const releaseCacheDir =
|
|
24989
|
-
const vertexPath =
|
|
24990
|
-
const edgesPath =
|
|
25167
|
+
const releaseCacheDir = path11.join(deps.cacheDir, release);
|
|
25168
|
+
const vertexPath = path11.join(releaseCacheDir, paths.vertexFilename);
|
|
25169
|
+
const edgesPath = path11.join(releaseCacheDir, paths.edgesFilename);
|
|
24991
25170
|
const [vertex, edges] = await Promise.all([
|
|
24992
25171
|
deps.downloadFile({ url: paths.vertexUrl, destPath: vertexPath }),
|
|
24993
25172
|
deps.downloadFile({ url: paths.edgesUrl, destPath: edgesPath })
|
|
@@ -25147,7 +25326,7 @@ function computeSummary(rows) {
|
|
|
25147
25326
|
|
|
25148
25327
|
// src/backlink-extract.ts
|
|
25149
25328
|
import crypto29 from "crypto";
|
|
25150
|
-
import
|
|
25329
|
+
import fs9 from "fs";
|
|
25151
25330
|
import { and as and22, desc as desc15, eq as eq32 } from "drizzle-orm";
|
|
25152
25331
|
var log7 = createLogger("BacklinkExtract");
|
|
25153
25332
|
function defaultDeps2() {
|
|
@@ -25176,7 +25355,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
25176
25355
|
if (!sync.vertexPath || !sync.edgesPath) {
|
|
25177
25356
|
throw new Error(`Release ${sync.release} is missing cached file paths`);
|
|
25178
25357
|
}
|
|
25179
|
-
if (!
|
|
25358
|
+
if (!fs9.existsSync(sync.vertexPath) || !fs9.existsSync(sync.edgesPath)) {
|
|
25180
25359
|
throw new Error(
|
|
25181
25360
|
`Cache for release ${sync.release} is missing from disk (expected at ${sync.vertexPath}). The sync record exists in the database, but the ~16 GB dump was deleted or never present on this machine. Re-sync this release from the Backlinks admin page to restore the cache.`
|
|
25182
25361
|
);
|
|
@@ -25569,23 +25748,25 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
25569
25748
|
}
|
|
25570
25749
|
|
|
25571
25750
|
// src/commands/backfill.ts
|
|
25572
|
-
import { and as and24, eq as eq34, inArray as
|
|
25751
|
+
import { and as and24, eq as eq34, inArray as inArray11 } from "drizzle-orm";
|
|
25573
25752
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
25574
25753
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
25575
25754
|
const config = loadConfig();
|
|
25576
25755
|
const db = createClient(config.database);
|
|
25577
25756
|
migrate(db);
|
|
25578
25757
|
const projectFilter = opts?.project?.trim();
|
|
25758
|
+
const isDryRun = opts?.dryRun === true;
|
|
25579
25759
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
25580
25760
|
let examined = 0;
|
|
25581
25761
|
let updated = 0;
|
|
25762
|
+
let wouldUpdate = 0;
|
|
25582
25763
|
let mentioned = 0;
|
|
25583
25764
|
let reparsed = 0;
|
|
25584
25765
|
let providerErrors = 0;
|
|
25585
25766
|
if (scopedProjects.length > 0) {
|
|
25586
25767
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and24(
|
|
25587
25768
|
eq34(runs.kind, RunKinds["answer-visibility"]),
|
|
25588
|
-
|
|
25769
|
+
inArray11(runs.projectId, scopedProjects.map((project) => project.id))
|
|
25589
25770
|
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq34(runs.kind, RunKinds["answer-visibility"])).all();
|
|
25590
25771
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
25591
25772
|
for (const run of runRows) {
|
|
@@ -25617,7 +25798,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25617
25798
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25618
25799
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
25619
25800
|
rawResponse: querySnapshots.rawResponse
|
|
25620
|
-
}).from(querySnapshots).where(
|
|
25801
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
25621
25802
|
const pendingUpdates = [];
|
|
25622
25803
|
for (const snapshot of snapshotRows) {
|
|
25623
25804
|
examined++;
|
|
@@ -25681,12 +25862,16 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25681
25862
|
}
|
|
25682
25863
|
}
|
|
25683
25864
|
if (pendingUpdates.length > 0) {
|
|
25684
|
-
|
|
25685
|
-
|
|
25686
|
-
|
|
25687
|
-
|
|
25688
|
-
|
|
25689
|
-
|
|
25865
|
+
if (isDryRun) {
|
|
25866
|
+
wouldUpdate += pendingUpdates.length;
|
|
25867
|
+
} else {
|
|
25868
|
+
db.transaction((tx) => {
|
|
25869
|
+
for (const update of pendingUpdates) {
|
|
25870
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
25871
|
+
}
|
|
25872
|
+
});
|
|
25873
|
+
updated += pendingUpdates.length;
|
|
25874
|
+
}
|
|
25690
25875
|
}
|
|
25691
25876
|
}
|
|
25692
25877
|
}
|
|
@@ -25700,20 +25885,33 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25700
25885
|
reparsed,
|
|
25701
25886
|
providerErrors
|
|
25702
25887
|
};
|
|
25888
|
+
if (isDryRun) {
|
|
25889
|
+
result.dryRun = true;
|
|
25890
|
+
result.wouldUpdate = wouldUpdate;
|
|
25891
|
+
}
|
|
25703
25892
|
if (opts?.format === "json") {
|
|
25704
25893
|
console.log(JSON.stringify(result, null, 2));
|
|
25705
25894
|
return;
|
|
25706
25895
|
}
|
|
25707
|
-
console.log(
|
|
25896
|
+
console.log(`Answer visibility backfill ${isDryRun ? "preview" : "complete"}.
|
|
25897
|
+
`);
|
|
25708
25898
|
if (projectFilter) {
|
|
25709
25899
|
console.log(` Project: ${projectFilter}`);
|
|
25710
25900
|
}
|
|
25711
25901
|
console.log(` Projects: ${scopedProjects.length}`);
|
|
25712
|
-
console.log(` Examined:
|
|
25713
|
-
|
|
25714
|
-
|
|
25715
|
-
|
|
25716
|
-
|
|
25902
|
+
console.log(` Examined: ${examined}`);
|
|
25903
|
+
if (isDryRun) {
|
|
25904
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
25905
|
+
} else {
|
|
25906
|
+
console.log(` Updated: ${updated}`);
|
|
25907
|
+
}
|
|
25908
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
25909
|
+
console.log(` Reparsed: ${reparsed}`);
|
|
25910
|
+
console.log(` Errors: ${providerErrors}`);
|
|
25911
|
+
if (isDryRun) {
|
|
25912
|
+
console.log(`
|
|
25913
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
25914
|
+
}
|
|
25717
25915
|
}
|
|
25718
25916
|
function backfillNormalizedPaths(db, opts) {
|
|
25719
25917
|
const baseConditions = [];
|
|
@@ -25859,7 +26057,8 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
25859
26057
|
console.log(` Updated: ${updated}`);
|
|
25860
26058
|
console.log(` Unchanged: ${unchanged}`);
|
|
25861
26059
|
}
|
|
25862
|
-
function backfillProjectAnswerMentions(db, projectId) {
|
|
26060
|
+
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
26061
|
+
const isDryRun = opts?.dryRun === true;
|
|
25863
26062
|
const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
|
|
25864
26063
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
25865
26064
|
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
@@ -25867,8 +26066,11 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25867
26066
|
const runIds = runRows.map((r) => r.id);
|
|
25868
26067
|
let examined = 0;
|
|
25869
26068
|
let updated = 0;
|
|
26069
|
+
let wouldUpdate = 0;
|
|
25870
26070
|
let mentioned = 0;
|
|
25871
|
-
if (runIds.length === 0)
|
|
26071
|
+
if (runIds.length === 0) {
|
|
26072
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
26073
|
+
}
|
|
25872
26074
|
const projectDomains = effectiveDomains({
|
|
25873
26075
|
canonicalDomain: project.canonicalDomain,
|
|
25874
26076
|
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
@@ -25888,7 +26090,7 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25888
26090
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25889
26091
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
25890
26092
|
rawResponse: querySnapshots.rawResponse
|
|
25891
|
-
}).from(querySnapshots).where(
|
|
26093
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
25892
26094
|
const pendingUpdates = [];
|
|
25893
26095
|
for (const snapshot of snapshotRows) {
|
|
25894
26096
|
examined++;
|
|
@@ -25931,29 +26133,36 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25931
26133
|
}
|
|
25932
26134
|
}
|
|
25933
26135
|
if (pendingUpdates.length > 0) {
|
|
25934
|
-
|
|
25935
|
-
|
|
25936
|
-
|
|
25937
|
-
|
|
25938
|
-
|
|
25939
|
-
|
|
26136
|
+
if (isDryRun) {
|
|
26137
|
+
wouldUpdate += pendingUpdates.length;
|
|
26138
|
+
} else {
|
|
26139
|
+
db.transaction((tx) => {
|
|
26140
|
+
for (const update of pendingUpdates) {
|
|
26141
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
26142
|
+
}
|
|
26143
|
+
});
|
|
26144
|
+
updated += pendingUpdates.length;
|
|
26145
|
+
}
|
|
25940
26146
|
}
|
|
25941
26147
|
}
|
|
25942
|
-
return { examined, updated, mentioned };
|
|
26148
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
25943
26149
|
}
|
|
25944
26150
|
async function backfillAnswerMentionsCommand(opts) {
|
|
25945
26151
|
const config = loadConfig();
|
|
25946
26152
|
const db = createClient(config.database);
|
|
25947
26153
|
migrate(db);
|
|
25948
26154
|
const projectFilter = opts?.project?.trim();
|
|
26155
|
+
const isDryRun = opts?.dryRun === true;
|
|
25949
26156
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
25950
26157
|
let examined = 0;
|
|
25951
26158
|
let updated = 0;
|
|
26159
|
+
let wouldUpdate = 0;
|
|
25952
26160
|
let mentioned = 0;
|
|
25953
26161
|
for (const project of scopedProjects) {
|
|
25954
|
-
const result2 = backfillProjectAnswerMentions(db, project.id);
|
|
26162
|
+
const result2 = backfillProjectAnswerMentions(db, project.id, { dryRun: isDryRun });
|
|
25955
26163
|
examined += result2.examined;
|
|
25956
26164
|
updated += result2.updated;
|
|
26165
|
+
wouldUpdate += result2.wouldUpdate ?? 0;
|
|
25957
26166
|
mentioned += result2.mentioned;
|
|
25958
26167
|
}
|
|
25959
26168
|
const result = {
|
|
@@ -25963,16 +26172,29 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
25963
26172
|
updated,
|
|
25964
26173
|
mentioned
|
|
25965
26174
|
};
|
|
26175
|
+
if (isDryRun) {
|
|
26176
|
+
result.dryRun = true;
|
|
26177
|
+
result.wouldUpdate = wouldUpdate;
|
|
26178
|
+
}
|
|
25966
26179
|
if (opts?.format === "json") {
|
|
25967
26180
|
console.log(JSON.stringify(result, null, 2));
|
|
25968
26181
|
return;
|
|
25969
26182
|
}
|
|
25970
|
-
console.log(
|
|
25971
|
-
|
|
25972
|
-
console.log(`
|
|
25973
|
-
console.log(`
|
|
25974
|
-
console.log(`
|
|
25975
|
-
|
|
26183
|
+
console.log(`Answer mentions backfill ${isDryRun ? "preview" : "complete"}.
|
|
26184
|
+
`);
|
|
26185
|
+
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
26186
|
+
console.log(` Projects: ${scopedProjects.length}`);
|
|
26187
|
+
console.log(` Examined: ${examined}`);
|
|
26188
|
+
if (isDryRun) {
|
|
26189
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
26190
|
+
} else {
|
|
26191
|
+
console.log(` Updated: ${updated}`);
|
|
26192
|
+
}
|
|
26193
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
26194
|
+
if (isDryRun) {
|
|
26195
|
+
console.log(`
|
|
26196
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
26197
|
+
}
|
|
25976
26198
|
}
|
|
25977
26199
|
function readStoredGroundingSources(rawResponse) {
|
|
25978
26200
|
const envelope = parseJsonColumn(rawResponse, {});
|
|
@@ -25991,19 +26213,24 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
25991
26213
|
return result;
|
|
25992
26214
|
}
|
|
25993
26215
|
async function backfillInsightsCommand(project, opts) {
|
|
25994
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
26216
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-CIGYPPMR.js");
|
|
25995
26217
|
const config = loadConfig();
|
|
25996
26218
|
const db = createClient(config.database);
|
|
25997
26219
|
migrate(db);
|
|
25998
26220
|
const service = new IntelligenceService2(db);
|
|
25999
26221
|
const isJson = opts?.format === "json";
|
|
26222
|
+
const isDryRun = opts?.dryRun === true;
|
|
26000
26223
|
if (!isJson) {
|
|
26001
|
-
|
|
26224
|
+
const scope = opts?.since ? ` (since ${opts.since})` : "";
|
|
26225
|
+
const mode = isDryRun ? " [DRY RUN \u2014 no writes]" : "";
|
|
26226
|
+
process.stderr.write(`Backfilling insights for "${project}"${scope}${mode}...
|
|
26002
26227
|
`);
|
|
26003
26228
|
}
|
|
26004
26229
|
const result = service.backfill(project, {
|
|
26005
26230
|
fromRunId: opts?.fromRun,
|
|
26006
|
-
toRunId: opts?.toRun
|
|
26231
|
+
toRunId: opts?.toRun,
|
|
26232
|
+
since: opts?.since,
|
|
26233
|
+
dryRun: isDryRun
|
|
26007
26234
|
}, (info) => {
|
|
26008
26235
|
if (!isJson) {
|
|
26009
26236
|
process.stderr.write(` [${info.index}/${info.total}] ${info.runId} \u2014 ${info.insights} insights
|
|
@@ -26016,15 +26243,23 @@ async function backfillInsightsCommand(project, opts) {
|
|
|
26016
26243
|
skipped: result.skipped,
|
|
26017
26244
|
totalInsights: result.totalInsights
|
|
26018
26245
|
};
|
|
26246
|
+
if (result.dryRun) {
|
|
26247
|
+
output.dryRun = true;
|
|
26248
|
+
output.delta = result.delta;
|
|
26249
|
+
}
|
|
26019
26250
|
if (isJson) {
|
|
26020
26251
|
console.log(JSON.stringify(output, null, 2));
|
|
26021
26252
|
return;
|
|
26022
26253
|
}
|
|
26023
26254
|
console.log(`
|
|
26024
|
-
Backfill complete.`);
|
|
26255
|
+
Backfill ${isDryRun ? "preview" : "complete"}.`);
|
|
26025
26256
|
console.log(` Processed: ${result.processed}`);
|
|
26026
26257
|
console.log(` Skipped: ${result.skipped}`);
|
|
26027
26258
|
console.log(` Insights: ${result.totalInsights}`);
|
|
26259
|
+
if (result.delta) {
|
|
26260
|
+
console.log(` Delta: -${result.delta.wouldDelete} existing +${result.delta.wouldCreate} new (net ${result.delta.netChange >= 0 ? "+" : ""}${result.delta.netChange})`);
|
|
26261
|
+
console.log(` No DB writes performed. Re-run without --dry-run to apply.`);
|
|
26262
|
+
}
|
|
26028
26263
|
}
|
|
26029
26264
|
function reparseProviderSnapshot(provider, rawResponse) {
|
|
26030
26265
|
const envelope = parseJsonColumn(rawResponse, {});
|
|
@@ -26296,7 +26531,7 @@ var Scheduler = class {
|
|
|
26296
26531
|
};
|
|
26297
26532
|
|
|
26298
26533
|
// src/notifier.ts
|
|
26299
|
-
import { eq as eq36, desc as desc16, and as and26, inArray as
|
|
26534
|
+
import { eq as eq36, desc as desc16, and as and26, inArray as inArray12, or as or5 } from "drizzle-orm";
|
|
26300
26535
|
import crypto31 from "crypto";
|
|
26301
26536
|
var log10 = createLogger("Notifier");
|
|
26302
26537
|
var Notifier = class {
|
|
@@ -26449,13 +26684,13 @@ var Notifier = class {
|
|
|
26449
26684
|
provider: querySnapshots.provider,
|
|
26450
26685
|
location: querySnapshots.location,
|
|
26451
26686
|
citationState: querySnapshots.citationState
|
|
26452
|
-
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(
|
|
26687
|
+
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
|
|
26453
26688
|
const previousSnapshots = this.db.select({
|
|
26454
26689
|
queryId: querySnapshots.queryId,
|
|
26455
26690
|
provider: querySnapshots.provider,
|
|
26456
26691
|
location: querySnapshots.location,
|
|
26457
26692
|
citationState: querySnapshots.citationState
|
|
26458
|
-
}).from(querySnapshots).where(
|
|
26693
|
+
}).from(querySnapshots).where(inArray12(querySnapshots.runId, previousRunIds)).all();
|
|
26459
26694
|
const prevMap = /* @__PURE__ */ new Map();
|
|
26460
26695
|
for (const s of previousSnapshots) {
|
|
26461
26696
|
if (s.queryId == null) continue;
|
|
@@ -26621,8 +26856,8 @@ import crypto33 from "crypto";
|
|
|
26621
26856
|
import { eq as eq39 } from "drizzle-orm";
|
|
26622
26857
|
|
|
26623
26858
|
// src/agent/session.ts
|
|
26624
|
-
import
|
|
26625
|
-
import
|
|
26859
|
+
import fs12 from "fs";
|
|
26860
|
+
import path14 from "path";
|
|
26626
26861
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
26627
26862
|
import { registerBuiltInApiProviders } from "@mariozechner/pi-ai";
|
|
26628
26863
|
|
|
@@ -26723,26 +26958,26 @@ function buildAgentProvidersResponse(config) {
|
|
|
26723
26958
|
}
|
|
26724
26959
|
|
|
26725
26960
|
// src/agent/skill-paths.ts
|
|
26726
|
-
import
|
|
26727
|
-
import
|
|
26961
|
+
import fs10 from "fs";
|
|
26962
|
+
import path12 from "path";
|
|
26728
26963
|
import { fileURLToPath } from "url";
|
|
26729
26964
|
function resolveAeroSkillDir(pkgDir) {
|
|
26730
|
-
const here = pkgDir ??
|
|
26965
|
+
const here = pkgDir ?? path12.dirname(fileURLToPath(import.meta.url));
|
|
26731
26966
|
const candidates = [
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26967
|
+
path12.join(here, "../assets/agent-workspace/skills/aero"),
|
|
26968
|
+
path12.join(here, "../../assets/agent-workspace/skills/aero"),
|
|
26969
|
+
path12.join(here, "../../../../skills/aero")
|
|
26735
26970
|
];
|
|
26736
26971
|
for (const candidate of candidates) {
|
|
26737
|
-
if (
|
|
26972
|
+
if (fs10.existsSync(path12.join(candidate, "SKILL.md"))) return candidate;
|
|
26738
26973
|
}
|
|
26739
26974
|
throw new Error(`Aero skill not found. Searched:
|
|
26740
26975
|
${candidates.join("\n ")}`);
|
|
26741
26976
|
}
|
|
26742
26977
|
|
|
26743
26978
|
// src/agent/skill-tools.ts
|
|
26744
|
-
import
|
|
26745
|
-
import
|
|
26979
|
+
import fs11 from "fs";
|
|
26980
|
+
import path13 from "path";
|
|
26746
26981
|
import { Type } from "@sinclair/typebox";
|
|
26747
26982
|
var MAX_DOC_CHARS = 2e4;
|
|
26748
26983
|
function textResult(details) {
|
|
@@ -26763,13 +26998,13 @@ function parseDescription(body) {
|
|
|
26763
26998
|
return "(no description)";
|
|
26764
26999
|
}
|
|
26765
27000
|
function scanSkillDocs(skillDir) {
|
|
26766
|
-
const refsDir =
|
|
26767
|
-
if (!
|
|
27001
|
+
const refsDir = path13.join(skillDir ?? resolveAeroSkillDir(), "references");
|
|
27002
|
+
if (!fs11.existsSync(refsDir)) return [];
|
|
26768
27003
|
const entries = [];
|
|
26769
|
-
for (const file of
|
|
27004
|
+
for (const file of fs11.readdirSync(refsDir)) {
|
|
26770
27005
|
if (!file.endsWith(".md")) continue;
|
|
26771
|
-
const filePath =
|
|
26772
|
-
const body =
|
|
27006
|
+
const filePath = path13.join(refsDir, file);
|
|
27007
|
+
const body = fs11.readFileSync(filePath, "utf-8");
|
|
26773
27008
|
entries.push({
|
|
26774
27009
|
slug: file.replace(/\.md$/, ""),
|
|
26775
27010
|
description: parseDescription(body),
|
|
@@ -26812,8 +27047,8 @@ function buildReadSkillDocTool() {
|
|
|
26812
27047
|
availableSlugs: docs.map((d) => d.slug)
|
|
26813
27048
|
});
|
|
26814
27049
|
}
|
|
26815
|
-
const filePath =
|
|
26816
|
-
const content =
|
|
27050
|
+
const filePath = path13.join(skillDir, "references", `${match.slug}.md`);
|
|
27051
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
26817
27052
|
if (content.length > MAX_DOC_CHARS) {
|
|
26818
27053
|
return textResult({
|
|
26819
27054
|
slug: match.slug,
|
|
@@ -26907,10 +27142,10 @@ function ensureBuiltinsRegistered() {
|
|
|
26907
27142
|
}
|
|
26908
27143
|
function loadAeroSystemPrompt(pkgDir) {
|
|
26909
27144
|
const skillDir = resolveAeroSkillDir(pkgDir);
|
|
26910
|
-
const skillBody =
|
|
26911
|
-
const soulPath =
|
|
26912
|
-
if (!
|
|
26913
|
-
const soulBody =
|
|
27145
|
+
const skillBody = fs12.readFileSync(path14.join(skillDir, "SKILL.md"), "utf-8");
|
|
27146
|
+
const soulPath = path14.join(skillDir, "soul.md");
|
|
27147
|
+
if (!fs12.existsSync(soulPath)) return skillBody;
|
|
27148
|
+
const soulBody = fs12.readFileSync(soulPath, "utf-8");
|
|
26914
27149
|
return `${soulBody.trimEnd()}
|
|
26915
27150
|
|
|
26916
27151
|
---
|
|
@@ -26968,7 +27203,7 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
26968
27203
|
|
|
26969
27204
|
// src/agent/memory-store.ts
|
|
26970
27205
|
import crypto32 from "crypto";
|
|
26971
|
-
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as
|
|
27206
|
+
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
|
|
26972
27207
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
26973
27208
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
26974
27209
|
function rowToDto2(row) {
|
|
@@ -27054,7 +27289,7 @@ function writeCompactionNote(db, args) {
|
|
|
27054
27289
|
).orderBy(desc17(agentMemory.updatedAt)).all();
|
|
27055
27290
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
27056
27291
|
if (stale.length > 0) {
|
|
27057
|
-
tx.delete(agentMemory).where(
|
|
27292
|
+
tx.delete(agentMemory).where(sql15`${agentMemory.id} IN (${sql15.join(stale.map((s) => sql15`${s}`), sql15`, `)})`).run();
|
|
27058
27293
|
}
|
|
27059
27294
|
const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
|
|
27060
27295
|
if (row) inserted = rowToDto2(row);
|
|
@@ -27812,13 +28047,13 @@ function extractHostname(domain) {
|
|
|
27812
28047
|
function fetchWithPinnedAddress(target) {
|
|
27813
28048
|
return new Promise((resolve) => {
|
|
27814
28049
|
const port = target.url.port ? Number(target.url.port) : 443;
|
|
27815
|
-
const
|
|
28050
|
+
const path16 = target.url.pathname + target.url.search;
|
|
27816
28051
|
const req = https2.request(
|
|
27817
28052
|
{
|
|
27818
28053
|
hostname: target.address,
|
|
27819
28054
|
family: target.family,
|
|
27820
28055
|
port,
|
|
27821
|
-
path:
|
|
28056
|
+
path: path16,
|
|
27822
28057
|
method: "GET",
|
|
27823
28058
|
timeout: FETCH_TIMEOUT_MS2,
|
|
27824
28059
|
servername: target.url.hostname,
|
|
@@ -28723,8 +28958,8 @@ async function createServer(opts) {
|
|
|
28723
28958
|
);
|
|
28724
28959
|
jobRunner.onRunCompleted = (runId, projectId) => runCoordinator.onRunCompleted(runId, projectId);
|
|
28725
28960
|
const snapshotService = new SnapshotService(registry);
|
|
28726
|
-
const orphanedOpenClawDir =
|
|
28727
|
-
if (
|
|
28961
|
+
const orphanedOpenClawDir = path15.join(os6.homedir(), ".openclaw-aero");
|
|
28962
|
+
if (fs13.existsSync(orphanedOpenClawDir)) {
|
|
28728
28963
|
app.log.warn(
|
|
28729
28964
|
{ path: orphanedOpenClawDir },
|
|
28730
28965
|
"OpenClaw gateway is no longer used. Remove ~/.openclaw-aero/ manually to reclaim the directory."
|
|
@@ -29439,10 +29674,10 @@ async function createServer(opts) {
|
|
|
29439
29674
|
return snapshotService.createReport(input);
|
|
29440
29675
|
}
|
|
29441
29676
|
});
|
|
29442
|
-
const dirname =
|
|
29443
|
-
const assetsDir =
|
|
29444
|
-
if (
|
|
29445
|
-
const indexPath =
|
|
29677
|
+
const dirname = path15.dirname(fileURLToPath2(import.meta.url));
|
|
29678
|
+
const assetsDir = path15.join(dirname, "..", "assets");
|
|
29679
|
+
if (fs13.existsSync(assetsDir)) {
|
|
29680
|
+
const indexPath = path15.join(assetsDir, "index.html");
|
|
29446
29681
|
const injectConfig = (html) => {
|
|
29447
29682
|
const clientConfig = {};
|
|
29448
29683
|
if (basePath) clientConfig.basePath = basePath;
|
|
@@ -29460,8 +29695,8 @@ async function createServer(opts) {
|
|
|
29460
29695
|
index: false
|
|
29461
29696
|
});
|
|
29462
29697
|
const serveIndex = (_request, reply) => {
|
|
29463
|
-
if (
|
|
29464
|
-
const html =
|
|
29698
|
+
if (fs13.existsSync(indexPath)) {
|
|
29699
|
+
const html = fs13.readFileSync(indexPath, "utf-8");
|
|
29465
29700
|
return reply.type("text/html").send(injectConfig(html));
|
|
29466
29701
|
}
|
|
29467
29702
|
return reply.status(404).send({ error: "Dashboard not built" });
|
|
@@ -29481,8 +29716,8 @@ async function createServer(opts) {
|
|
|
29481
29716
|
if (basePath && !url.startsWith(basePath)) {
|
|
29482
29717
|
return reply.status(404).send({ error: "Not found", path: request.url });
|
|
29483
29718
|
}
|
|
29484
|
-
if (
|
|
29485
|
-
const html =
|
|
29719
|
+
if (fs13.existsSync(indexPath)) {
|
|
29720
|
+
const html = fs13.readFileSync(indexPath, "utf-8");
|
|
29486
29721
|
return reply.type("text/html").send(injectConfig(html));
|
|
29487
29722
|
}
|
|
29488
29723
|
return reply.status(404).send({ error: "Not found" });
|