@ainyc/canonry 4.15.0 → 4.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -223
- package/assets/assets/{index-D0EPNRDs.css → index-C1WW21tz.css} +1 -1
- package/assets/assets/index-Qq_oMI-C.js +302 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-C32VL5BB.js → chunk-7SRKUAZO.js} +130 -2
- package/dist/{chunk-DLSQXNUN.js → chunk-IVNWS2YU.js} +289 -38
- package/dist/{chunk-7HBZCGRL.js → chunk-MI33SQL6.js} +27 -2
- package/dist/{chunk-6QTH5NS5.js → chunk-ONI3TX2A.js} +62 -0
- package/dist/cli.js +200 -11
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-BCKXIKIL.js → intelligence-service-JYV3CO4H.js} +2 -2
- package/dist/mcp.js +9 -3
- package/package.json +7 -7
- package/assets/assets/index-B6Mi9Fd1.js +0 -302
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadConfig,
|
|
6
6
|
loadConfigRaw,
|
|
7
7
|
saveConfigPatch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-7SRKUAZO.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
11
11
|
IntelligenceService,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
schedules,
|
|
67
67
|
trafficSources,
|
|
68
68
|
usageCounters
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-MI33SQL6.js";
|
|
70
70
|
import {
|
|
71
71
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
72
72
|
AGENT_PROVIDER_IDS,
|
|
@@ -82,6 +82,7 @@ import {
|
|
|
82
82
|
RunStatuses,
|
|
83
83
|
RunTriggers,
|
|
84
84
|
TrafficEventConfidences,
|
|
85
|
+
TrafficEventKinds,
|
|
85
86
|
TrafficEvidenceKinds,
|
|
86
87
|
TrafficSourceAuthModes,
|
|
87
88
|
TrafficSourceStatuses,
|
|
@@ -152,7 +153,7 @@ import {
|
|
|
152
153
|
visibilityStateFromAnswerMentioned,
|
|
153
154
|
windowCutoff,
|
|
154
155
|
wordpressEnvSchema
|
|
155
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-ONI3TX2A.js";
|
|
156
157
|
|
|
157
158
|
// src/telemetry.ts
|
|
158
159
|
import crypto from "crypto";
|
|
@@ -9661,8 +9662,66 @@ var routeCatalog = [
|
|
|
9661
9662
|
},
|
|
9662
9663
|
responses: {
|
|
9663
9664
|
200: { description: "Sync summary returned." },
|
|
9664
|
-
400: { description: "Invalid sync request
|
|
9665
|
-
404: { description: "Project or traffic source not found." }
|
|
9665
|
+
400: { description: "Invalid sync request or missing credentials." },
|
|
9666
|
+
404: { description: "Project or traffic source not found." },
|
|
9667
|
+
502: { description: "Upstream Cloud Run pull or auth-token resolution failed." }
|
|
9668
|
+
}
|
|
9669
|
+
},
|
|
9670
|
+
{
|
|
9671
|
+
method: "get",
|
|
9672
|
+
path: "/api/v1/projects/{name}/traffic/sources",
|
|
9673
|
+
summary: "List non-archived traffic sources for a project",
|
|
9674
|
+
tags: ["traffic"],
|
|
9675
|
+
parameters: [nameParameter],
|
|
9676
|
+
responses: {
|
|
9677
|
+
200: { description: "Source list returned." },
|
|
9678
|
+
404: { description: "Project not found." }
|
|
9679
|
+
}
|
|
9680
|
+
},
|
|
9681
|
+
{
|
|
9682
|
+
method: "get",
|
|
9683
|
+
path: "/api/v1/projects/{name}/traffic/status",
|
|
9684
|
+
summary: "List non-archived traffic sources with last-24h totals and the latest sync run for each",
|
|
9685
|
+
description: "Single-call composite for the `canonry traffic status` view: same shape as `GET /traffic/sources/{id}` but returned as `{ sources: TrafficSourceDetailDto[] }` for every non-archived source. Lets agents and the dashboard avoid an N+1 fan-out.",
|
|
9686
|
+
tags: ["traffic"],
|
|
9687
|
+
parameters: [nameParameter],
|
|
9688
|
+
responses: {
|
|
9689
|
+
200: { description: "Status returned." },
|
|
9690
|
+
404: { description: "Project not found." }
|
|
9691
|
+
}
|
|
9692
|
+
},
|
|
9693
|
+
{
|
|
9694
|
+
method: "get",
|
|
9695
|
+
path: "/api/v1/projects/{name}/traffic/sources/{id}",
|
|
9696
|
+
summary: "Get a single traffic source with last-24h totals and the latest sync run",
|
|
9697
|
+
tags: ["traffic"],
|
|
9698
|
+
parameters: [
|
|
9699
|
+
nameParameter,
|
|
9700
|
+
{ name: "id", in: "path", required: true, description: "Traffic source ID.", schema: stringSchema }
|
|
9701
|
+
],
|
|
9702
|
+
responses: {
|
|
9703
|
+
200: { description: "Source detail returned." },
|
|
9704
|
+
404: { description: "Project or source not found." }
|
|
9705
|
+
}
|
|
9706
|
+
},
|
|
9707
|
+
{
|
|
9708
|
+
method: "get",
|
|
9709
|
+
path: "/api/v1/projects/{name}/traffic/events",
|
|
9710
|
+
summary: "List rolled-up crawler and AI-referral hits within a window",
|
|
9711
|
+
description: "Returns hourly rollup rows from `crawler_events_hourly` and `ai_referral_events_hourly`. Defaults to the last 24h. Totals reflect the full window; the `events` array is capped by `limit` (default 500, max 5000).",
|
|
9712
|
+
tags: ["traffic"],
|
|
9713
|
+
parameters: [
|
|
9714
|
+
nameParameter,
|
|
9715
|
+
{ name: "since", in: "query", description: "ISO-8601 window start (defaults to 24h ago).", schema: stringSchema },
|
|
9716
|
+
{ name: "until", in: "query", description: "ISO-8601 window end (defaults to now).", schema: stringSchema },
|
|
9717
|
+
{ name: "kind", in: "query", description: 'Filter to "crawler", "ai-referral", or "all" (default).', schema: stringSchema },
|
|
9718
|
+
{ name: "limit", in: "query", description: "Max rows per kind in the events array (default 500, max 5000).", schema: stringSchema },
|
|
9719
|
+
{ name: "sourceId", in: "query", description: "Restrict to a single traffic source.", schema: stringSchema }
|
|
9720
|
+
],
|
|
9721
|
+
responses: {
|
|
9722
|
+
200: { description: "Events returned with windowed totals." },
|
|
9723
|
+
400: { description: "Invalid query parameters." },
|
|
9724
|
+
404: { description: "Project not found." }
|
|
9666
9725
|
}
|
|
9667
9726
|
}
|
|
9668
9727
|
];
|
|
@@ -15893,7 +15952,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
15893
15952
|
|
|
15894
15953
|
// ../api-routes/src/traffic.ts
|
|
15895
15954
|
import crypto20 from "crypto";
|
|
15896
|
-
import { eq as eq23, sql as sql7 } from "drizzle-orm";
|
|
15955
|
+
import { and as and12, desc as desc12, eq as eq23, gte, lte, sql as sql7 } from "drizzle-orm";
|
|
15897
15956
|
|
|
15898
15957
|
// ../integration-cloud-run/src/auth.ts
|
|
15899
15958
|
import crypto19 from "crypto";
|
|
@@ -16500,10 +16559,11 @@ function incrementBucket(map, key, fields) {
|
|
|
16500
16559
|
}
|
|
16501
16560
|
|
|
16502
16561
|
// ../api-routes/src/traffic.ts
|
|
16503
|
-
var DEFAULT_SYNC_WINDOW_MINUTES =
|
|
16562
|
+
var DEFAULT_SYNC_WINDOW_MINUTES = 43200;
|
|
16504
16563
|
var DEFAULT_PAGE_SIZE2 = 1e3;
|
|
16505
16564
|
var DEFAULT_MAX_PAGES2 = 5;
|
|
16506
16565
|
var DEFAULT_SAMPLE_LIMIT2 = 100;
|
|
16566
|
+
var MAX_TRACKED_EVENT_IDS = 1e3;
|
|
16507
16567
|
function parseSourceConfig(row) {
|
|
16508
16568
|
return parseJsonColumn(row.configJson, {});
|
|
16509
16569
|
}
|
|
@@ -16664,17 +16724,24 @@ async function trafficRoutes(app, opts) {
|
|
|
16664
16724
|
kind: RunKinds["traffic-sync"],
|
|
16665
16725
|
status: RunStatuses.running,
|
|
16666
16726
|
trigger: RunTriggers.manual,
|
|
16727
|
+
sourceId: sourceRow.id,
|
|
16667
16728
|
startedAt,
|
|
16668
16729
|
createdAt: startedAt
|
|
16669
16730
|
}).run();
|
|
16731
|
+
const markFailed = (msg) => {
|
|
16732
|
+
const failedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
16733
|
+
app.db.transaction((tx) => {
|
|
16734
|
+
tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq23(runs.id, runId)).run();
|
|
16735
|
+
tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq23(trafficSources.id, sourceRow.id)).run();
|
|
16736
|
+
});
|
|
16737
|
+
};
|
|
16670
16738
|
let accessToken;
|
|
16671
16739
|
try {
|
|
16672
16740
|
accessToken = await resolveAccessToken2(credential);
|
|
16673
16741
|
} catch (e) {
|
|
16674
16742
|
const msg = e instanceof Error ? e.message : String(e);
|
|
16675
|
-
|
|
16676
|
-
|
|
16677
|
-
throw validationError(`Failed to resolve Cloud Run access token: ${msg}`);
|
|
16743
|
+
markFailed(msg);
|
|
16744
|
+
throw providerError(`Failed to resolve Cloud Run access token: ${msg}`);
|
|
16678
16745
|
}
|
|
16679
16746
|
let allEvents = [];
|
|
16680
16747
|
try {
|
|
@@ -16690,11 +16757,23 @@ async function trafficRoutes(app, opts) {
|
|
|
16690
16757
|
allEvents = page.events;
|
|
16691
16758
|
} catch (e) {
|
|
16692
16759
|
const msg = e instanceof Error ? e.message : String(e);
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16697
|
-
const
|
|
16760
|
+
markFailed(msg);
|
|
16761
|
+
throw providerError(`Cloud Run pull failed: ${msg}`);
|
|
16762
|
+
}
|
|
16763
|
+
const seenEventIds = new Set(parseJsonColumn(sourceRow.lastEventIds, []));
|
|
16764
|
+
const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
|
|
16765
|
+
const newSorted = dedupedEvents.slice().sort((a, b) => a.observedAt < b.observedAt ? 1 : a.observedAt > b.observedAt ? -1 : 0).map((e) => e.eventId);
|
|
16766
|
+
const previousIds = parseJsonColumn(sourceRow.lastEventIds, []);
|
|
16767
|
+
const merged = [];
|
|
16768
|
+
const mergedSet = /* @__PURE__ */ new Set();
|
|
16769
|
+
for (const id of [...newSorted, ...previousIds]) {
|
|
16770
|
+
if (mergedSet.has(id)) continue;
|
|
16771
|
+
mergedSet.add(id);
|
|
16772
|
+
merged.push(id);
|
|
16773
|
+
if (merged.length >= MAX_TRACKED_EVENT_IDS) break;
|
|
16774
|
+
}
|
|
16775
|
+
const nextEventIds = merged;
|
|
16776
|
+
const report = buildTrafficProbeReport(dedupedEvents, { sampleLimit });
|
|
16698
16777
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
16699
16778
|
let crawlerBucketRows = 0;
|
|
16700
16779
|
let aiReferralBucketRows = 0;
|
|
@@ -16800,6 +16879,7 @@ async function trafficRoutes(app, opts) {
|
|
|
16800
16879
|
status: TrafficSourceStatuses.connected,
|
|
16801
16880
|
lastSyncedAt: finishedAt,
|
|
16802
16881
|
lastError: null,
|
|
16882
|
+
lastEventIds: JSON.stringify(nextEventIds),
|
|
16803
16883
|
updatedAt: finishedAt
|
|
16804
16884
|
}).where(eq23(trafficSources.id, sourceRow.id)).run();
|
|
16805
16885
|
tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq23(runs.id, runId)).run();
|
|
@@ -16827,6 +16907,177 @@ async function trafficRoutes(app, opts) {
|
|
|
16827
16907
|
};
|
|
16828
16908
|
return response;
|
|
16829
16909
|
});
|
|
16910
|
+
function buildSourceDetail(projectId, row, since) {
|
|
16911
|
+
const crawlerTotals = app.db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
16912
|
+
and12(
|
|
16913
|
+
eq23(crawlerEventsHourly.sourceId, row.id),
|
|
16914
|
+
gte(crawlerEventsHourly.tsHour, since)
|
|
16915
|
+
)
|
|
16916
|
+
).get();
|
|
16917
|
+
const aiTotals = app.db.select({ total: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
16918
|
+
and12(
|
|
16919
|
+
eq23(aiReferralEventsHourly.sourceId, row.id),
|
|
16920
|
+
gte(aiReferralEventsHourly.tsHour, since)
|
|
16921
|
+
)
|
|
16922
|
+
).get();
|
|
16923
|
+
const sampleTotals = app.db.select({ total: sql7`COUNT(*)` }).from(rawEventSamples).where(
|
|
16924
|
+
and12(
|
|
16925
|
+
eq23(rawEventSamples.sourceId, row.id),
|
|
16926
|
+
gte(rawEventSamples.ts, since)
|
|
16927
|
+
)
|
|
16928
|
+
).get();
|
|
16929
|
+
const latestRun = app.db.select().from(runs).where(
|
|
16930
|
+
and12(
|
|
16931
|
+
eq23(runs.projectId, projectId),
|
|
16932
|
+
eq23(runs.kind, RunKinds["traffic-sync"]),
|
|
16933
|
+
eq23(runs.sourceId, row.id)
|
|
16934
|
+
)
|
|
16935
|
+
).orderBy(desc12(runs.startedAt)).limit(1).get();
|
|
16936
|
+
return {
|
|
16937
|
+
...rowToDto(row),
|
|
16938
|
+
totals24h: {
|
|
16939
|
+
crawlerHits: Number(crawlerTotals?.total ?? 0),
|
|
16940
|
+
aiReferralHits: Number(aiTotals?.total ?? 0),
|
|
16941
|
+
sampleCount: Number(sampleTotals?.total ?? 0)
|
|
16942
|
+
},
|
|
16943
|
+
latestRun: latestRun ? {
|
|
16944
|
+
runId: latestRun.id,
|
|
16945
|
+
status: latestRun.status,
|
|
16946
|
+
startedAt: latestRun.startedAt,
|
|
16947
|
+
finishedAt: latestRun.finishedAt ?? null,
|
|
16948
|
+
error: latestRun.error ?? null
|
|
16949
|
+
} : null
|
|
16950
|
+
};
|
|
16951
|
+
}
|
|
16952
|
+
app.get("/projects/:name/traffic/sources", async (request) => {
|
|
16953
|
+
const project = resolveProject(app.db, request.params.name);
|
|
16954
|
+
const rows = app.db.select().from(trafficSources).where(eq23(trafficSources.projectId, project.id)).orderBy(desc12(trafficSources.createdAt)).all();
|
|
16955
|
+
const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
|
|
16956
|
+
const response = { sources };
|
|
16957
|
+
return response;
|
|
16958
|
+
});
|
|
16959
|
+
app.get("/projects/:name/traffic/status", async (request) => {
|
|
16960
|
+
const project = resolveProject(app.db, request.params.name);
|
|
16961
|
+
const rows = app.db.select().from(trafficSources).where(eq23(trafficSources.projectId, project.id)).orderBy(desc12(trafficSources.createdAt)).all();
|
|
16962
|
+
const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
|
|
16963
|
+
const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
|
|
16964
|
+
const response = { sources };
|
|
16965
|
+
return response;
|
|
16966
|
+
});
|
|
16967
|
+
app.get(
|
|
16968
|
+
"/projects/:name/traffic/sources/:id",
|
|
16969
|
+
async (request) => {
|
|
16970
|
+
const project = resolveProject(app.db, request.params.name);
|
|
16971
|
+
const row = app.db.select().from(trafficSources).where(eq23(trafficSources.id, request.params.id)).get();
|
|
16972
|
+
if (!row || row.projectId !== project.id) {
|
|
16973
|
+
throw notFound("Traffic source", request.params.id);
|
|
16974
|
+
}
|
|
16975
|
+
const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
|
|
16976
|
+
return buildSourceDetail(project.id, row, since);
|
|
16977
|
+
}
|
|
16978
|
+
);
|
|
16979
|
+
app.get("/projects/:name/traffic/events", async (request) => {
|
|
16980
|
+
const project = resolveProject(app.db, request.params.name);
|
|
16981
|
+
const now = /* @__PURE__ */ new Date();
|
|
16982
|
+
const defaultSince = new Date(now.getTime() - 24 * 60 * 6e4);
|
|
16983
|
+
const sinceParam = request.query?.since;
|
|
16984
|
+
const untilParam = request.query?.until;
|
|
16985
|
+
const since = sinceParam ? new Date(sinceParam) : defaultSince;
|
|
16986
|
+
const until = untilParam ? new Date(untilParam) : now;
|
|
16987
|
+
if (Number.isNaN(since.getTime())) {
|
|
16988
|
+
throw validationError('"since" must be an ISO-8601 timestamp');
|
|
16989
|
+
}
|
|
16990
|
+
if (Number.isNaN(until.getTime())) {
|
|
16991
|
+
throw validationError('"until" must be an ISO-8601 timestamp');
|
|
16992
|
+
}
|
|
16993
|
+
if (since.getTime() > until.getTime()) {
|
|
16994
|
+
throw validationError('"since" must be \u2264 "until"');
|
|
16995
|
+
}
|
|
16996
|
+
const kindParam = request.query?.kind;
|
|
16997
|
+
let kind = "all";
|
|
16998
|
+
if (kindParam !== void 0) {
|
|
16999
|
+
if (kindParam === "all" || kindParam === TrafficEventKinds.crawler || kindParam === TrafficEventKinds["ai-referral"]) {
|
|
17000
|
+
kind = kindParam;
|
|
17001
|
+
} else {
|
|
17002
|
+
throw validationError(`"kind" must be one of: all, ${TrafficEventKinds.crawler}, ${TrafficEventKinds["ai-referral"]}`);
|
|
17003
|
+
}
|
|
17004
|
+
}
|
|
17005
|
+
const limitParam = request.query?.limit;
|
|
17006
|
+
const requestedLimit = limitParam ? parseInt(limitParam, 10) : 500;
|
|
17007
|
+
if (!Number.isFinite(requestedLimit) || requestedLimit <= 0) {
|
|
17008
|
+
throw validationError('"limit" must be a positive integer');
|
|
17009
|
+
}
|
|
17010
|
+
const limit = Math.min(requestedLimit, 5e3);
|
|
17011
|
+
const sourceIdParam = request.query?.sourceId;
|
|
17012
|
+
const sinceIso = since.toISOString();
|
|
17013
|
+
const untilIso = until.toISOString();
|
|
17014
|
+
const events = [];
|
|
17015
|
+
let crawlerTotal = 0;
|
|
17016
|
+
let aiReferralTotal = 0;
|
|
17017
|
+
if (kind === "all" || kind === TrafficEventKinds.crawler) {
|
|
17018
|
+
const crawlerFilters = [
|
|
17019
|
+
eq23(crawlerEventsHourly.projectId, project.id),
|
|
17020
|
+
gte(crawlerEventsHourly.tsHour, sinceIso),
|
|
17021
|
+
lte(crawlerEventsHourly.tsHour, untilIso)
|
|
17022
|
+
];
|
|
17023
|
+
if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
17024
|
+
const crawlerWhere = and12(...crawlerFilters);
|
|
17025
|
+
const total = app.db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
|
|
17026
|
+
crawlerTotal = Number(total?.total ?? 0);
|
|
17027
|
+
const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
|
|
17028
|
+
for (const r of rows) {
|
|
17029
|
+
events.push({
|
|
17030
|
+
kind: TrafficEventKinds.crawler,
|
|
17031
|
+
sourceId: r.sourceId,
|
|
17032
|
+
tsHour: r.tsHour,
|
|
17033
|
+
botId: r.botId,
|
|
17034
|
+
operator: r.operator,
|
|
17035
|
+
verificationStatus: r.verificationStatus,
|
|
17036
|
+
pathNormalized: r.pathNormalized,
|
|
17037
|
+
status: r.status,
|
|
17038
|
+
hits: r.hits
|
|
17039
|
+
});
|
|
17040
|
+
}
|
|
17041
|
+
}
|
|
17042
|
+
if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
|
|
17043
|
+
const aiFilters = [
|
|
17044
|
+
eq23(aiReferralEventsHourly.projectId, project.id),
|
|
17045
|
+
gte(aiReferralEventsHourly.tsHour, sinceIso),
|
|
17046
|
+
lte(aiReferralEventsHourly.tsHour, untilIso)
|
|
17047
|
+
];
|
|
17048
|
+
if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
17049
|
+
const aiWhere = and12(...aiFilters);
|
|
17050
|
+
const total = app.db.select({ total: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
17051
|
+
aiReferralTotal = Number(total?.total ?? 0);
|
|
17052
|
+
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
17053
|
+
for (const r of rows) {
|
|
17054
|
+
events.push({
|
|
17055
|
+
kind: TrafficEventKinds["ai-referral"],
|
|
17056
|
+
sourceId: r.sourceId,
|
|
17057
|
+
tsHour: r.tsHour,
|
|
17058
|
+
product: r.product,
|
|
17059
|
+
operator: r.operator,
|
|
17060
|
+
sourceDomain: r.sourceDomain,
|
|
17061
|
+
evidenceType: r.evidenceType,
|
|
17062
|
+
landingPathNormalized: r.landingPathNormalized,
|
|
17063
|
+
status: r.status,
|
|
17064
|
+
hits: r.sessionsOrHits
|
|
17065
|
+
});
|
|
17066
|
+
}
|
|
17067
|
+
}
|
|
17068
|
+
events.sort((a, b) => a.tsHour < b.tsHour ? 1 : a.tsHour > b.tsHour ? -1 : 0);
|
|
17069
|
+
const trimmed = events.slice(0, limit);
|
|
17070
|
+
const response = {
|
|
17071
|
+
windowStart: sinceIso,
|
|
17072
|
+
windowEnd: untilIso,
|
|
17073
|
+
totals: {
|
|
17074
|
+
crawlerHits: crawlerTotal,
|
|
17075
|
+
aiReferralHits: aiReferralTotal
|
|
17076
|
+
},
|
|
17077
|
+
events: trimmed
|
|
17078
|
+
};
|
|
17079
|
+
return response;
|
|
17080
|
+
});
|
|
16830
17081
|
}
|
|
16831
17082
|
|
|
16832
17083
|
// ../api-routes/src/doctor/checks/bing-auth.ts
|
|
@@ -20198,7 +20449,7 @@ import crypto22 from "crypto";
|
|
|
20198
20449
|
import fs7 from "fs";
|
|
20199
20450
|
import path9 from "path";
|
|
20200
20451
|
import os5 from "os";
|
|
20201
|
-
import { and as
|
|
20452
|
+
import { and as and13, eq as eq24, inArray as inArray7, sql as sql8 } from "drizzle-orm";
|
|
20202
20453
|
|
|
20203
20454
|
// src/run-telemetry.ts
|
|
20204
20455
|
import crypto21 from "crypto";
|
|
@@ -20577,7 +20828,7 @@ var JobRunner = class {
|
|
|
20577
20828
|
throw new Error(`Run ${runId} is not executable from status '${existingRun.status}'`);
|
|
20578
20829
|
}
|
|
20579
20830
|
if (existingRun.status === "queued") {
|
|
20580
|
-
this.db.update(runs).set({ status: "running", startedAt: now }).where(
|
|
20831
|
+
this.db.update(runs).set({ status: "running", startedAt: now }).where(and13(eq24(runs.id, runId), eq24(runs.status, "queued"))).run();
|
|
20581
20832
|
}
|
|
20582
20833
|
this.throwIfRunCancelled(runId);
|
|
20583
20834
|
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
@@ -20936,7 +21187,7 @@ function buildPhases(input) {
|
|
|
20936
21187
|
|
|
20937
21188
|
// src/gsc-sync.ts
|
|
20938
21189
|
import crypto23 from "crypto";
|
|
20939
|
-
import { eq as eq25, and as
|
|
21190
|
+
import { eq as eq25, and as and14, sql as sql9 } from "drizzle-orm";
|
|
20940
21191
|
var log2 = createLogger("GscSync");
|
|
20941
21192
|
function formatDate3(d) {
|
|
20942
21193
|
return d.toISOString().split("T")[0];
|
|
@@ -20988,7 +21239,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
20988
21239
|
});
|
|
20989
21240
|
log2.info("fetch.complete", { runId, projectId, rowCount: rows.length });
|
|
20990
21241
|
db.delete(gscSearchData).where(
|
|
20991
|
-
|
|
21242
|
+
and14(
|
|
20992
21243
|
eq25(gscSearchData.projectId, projectId),
|
|
20993
21244
|
sql9`${gscSearchData.date} >= ${startDate}`,
|
|
20994
21245
|
sql9`${gscSearchData.date} <= ${endDate}`
|
|
@@ -21077,7 +21328,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
21077
21328
|
}
|
|
21078
21329
|
}
|
|
21079
21330
|
const snapshotDate = formatDate3(/* @__PURE__ */ new Date());
|
|
21080
|
-
db.delete(gscCoverageSnapshots).where(
|
|
21331
|
+
db.delete(gscCoverageSnapshots).where(and14(eq25(gscCoverageSnapshots.projectId, projectId), eq25(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
21081
21332
|
db.insert(gscCoverageSnapshots).values({
|
|
21082
21333
|
id: crypto23.randomUUID(),
|
|
21083
21334
|
projectId,
|
|
@@ -21100,7 +21351,7 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
21100
21351
|
|
|
21101
21352
|
// src/gsc-inspect-sitemap.ts
|
|
21102
21353
|
import crypto24 from "crypto";
|
|
21103
|
-
import { eq as eq26, and as
|
|
21354
|
+
import { eq as eq26, and as and15 } from "drizzle-orm";
|
|
21104
21355
|
|
|
21105
21356
|
// src/sitemap-parser.ts
|
|
21106
21357
|
var log3 = createLogger("SitemapParser");
|
|
@@ -21316,7 +21567,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
21316
21567
|
}
|
|
21317
21568
|
}
|
|
21318
21569
|
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
21319
|
-
db.delete(gscCoverageSnapshots).where(
|
|
21570
|
+
db.delete(gscCoverageSnapshots).where(and15(eq26(gscCoverageSnapshots.projectId, projectId), eq26(gscCoverageSnapshots.date, snapshotDate))).run();
|
|
21320
21571
|
db.insert(gscCoverageSnapshots).values({
|
|
21321
21572
|
id: crypto24.randomUUID(),
|
|
21322
21573
|
projectId,
|
|
@@ -21340,7 +21591,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
|
|
|
21340
21591
|
|
|
21341
21592
|
// src/bing-inspect-sitemap.ts
|
|
21342
21593
|
import crypto25 from "crypto";
|
|
21343
|
-
import { eq as eq27, desc as
|
|
21594
|
+
import { eq as eq27, desc as desc13 } from "drizzle-orm";
|
|
21344
21595
|
var log5 = createLogger("BingInspectSitemap");
|
|
21345
21596
|
function parseBingDate2(value) {
|
|
21346
21597
|
if (!value) return null;
|
|
@@ -21461,7 +21712,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
21461
21712
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
21462
21713
|
}
|
|
21463
21714
|
}
|
|
21464
|
-
const allInspections = db.select().from(bingUrlInspections).where(eq27(bingUrlInspections.projectId, projectId)).orderBy(
|
|
21715
|
+
const allInspections = db.select().from(bingUrlInspections).where(eq27(bingUrlInspections.projectId, projectId)).orderBy(desc13(bingUrlInspections.inspectedAt)).all();
|
|
21465
21716
|
const latestByUrl = /* @__PURE__ */ new Map();
|
|
21466
21717
|
const definitiveByUrl = /* @__PURE__ */ new Map();
|
|
21467
21718
|
for (const row of allInspections) {
|
|
@@ -21527,7 +21778,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
21527
21778
|
// src/commoncrawl-sync.ts
|
|
21528
21779
|
import crypto26 from "crypto";
|
|
21529
21780
|
import path10 from "path";
|
|
21530
|
-
import { and as
|
|
21781
|
+
import { and as and16, eq as eq28, sql as sql10 } from "drizzle-orm";
|
|
21531
21782
|
var log6 = createLogger("CommonCrawlSync");
|
|
21532
21783
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
21533
21784
|
function defaultDeps() {
|
|
@@ -21718,7 +21969,7 @@ function computeSummary(rows) {
|
|
|
21718
21969
|
// src/backlink-extract.ts
|
|
21719
21970
|
import crypto27 from "crypto";
|
|
21720
21971
|
import fs8 from "fs";
|
|
21721
|
-
import { and as
|
|
21972
|
+
import { and as and17, desc as desc14, eq as eq29 } from "drizzle-orm";
|
|
21722
21973
|
var log7 = createLogger("BacklinkExtract");
|
|
21723
21974
|
function defaultDeps2() {
|
|
21724
21975
|
return {
|
|
@@ -21736,7 +21987,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
21736
21987
|
if (!project) {
|
|
21737
21988
|
throw new Error(`Project not found: ${projectId}`);
|
|
21738
21989
|
}
|
|
21739
|
-
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(
|
|
21990
|
+
const sync = opts.release ? db.select().from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.release, opts.release)).get() : db.select().from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).orderBy(desc14(ccReleaseSyncs.createdAt)).limit(1).get();
|
|
21740
21991
|
if (!sync) {
|
|
21741
21992
|
throw new Error("No ready release sync available \u2014 run `canonry backlinks sync` first");
|
|
21742
21993
|
}
|
|
@@ -21764,7 +22015,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
|
|
|
21764
22015
|
const targetDomain = project.canonicalDomain;
|
|
21765
22016
|
db.transaction((tx) => {
|
|
21766
22017
|
tx.delete(backlinkDomains).where(
|
|
21767
|
-
|
|
22018
|
+
and17(eq29(backlinkDomains.projectId, projectId), eq29(backlinkDomains.release, release))
|
|
21768
22019
|
).run();
|
|
21769
22020
|
if (rows.length > 0) {
|
|
21770
22021
|
const values = rows.map((r) => ({
|
|
@@ -22020,7 +22271,7 @@ var Scheduler = class {
|
|
|
22020
22271
|
};
|
|
22021
22272
|
|
|
22022
22273
|
// src/notifier.ts
|
|
22023
|
-
import { eq as eq31, desc as
|
|
22274
|
+
import { eq as eq31, desc as desc15, and as and18, or as or4 } from "drizzle-orm";
|
|
22024
22275
|
import crypto28 from "crypto";
|
|
22025
22276
|
var log9 = createLogger("Notifier");
|
|
22026
22277
|
var Notifier = class {
|
|
@@ -22126,11 +22377,11 @@ var Notifier = class {
|
|
|
22126
22377
|
}
|
|
22127
22378
|
computeTransitions(runId, projectId) {
|
|
22128
22379
|
const recentRuns = this.db.select().from(runs).where(
|
|
22129
|
-
|
|
22380
|
+
and18(
|
|
22130
22381
|
eq31(runs.projectId, projectId),
|
|
22131
22382
|
or4(eq31(runs.status, "completed"), eq31(runs.status, "partial"))
|
|
22132
22383
|
)
|
|
22133
|
-
).orderBy(
|
|
22384
|
+
).orderBy(desc15(runs.createdAt)).limit(2).all();
|
|
22134
22385
|
if (recentRuns.length < 2) return [];
|
|
22135
22386
|
const currentRunId = recentRuns[0].id;
|
|
22136
22387
|
const previousRunId = recentRuns[1].id;
|
|
@@ -22612,7 +22863,7 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
22612
22863
|
|
|
22613
22864
|
// src/agent/memory-store.ts
|
|
22614
22865
|
import crypto29 from "crypto";
|
|
22615
|
-
import { and as
|
|
22866
|
+
import { and as and19, desc as desc16, eq as eq32, like as like2, sql as sql11 } from "drizzle-orm";
|
|
22616
22867
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
22617
22868
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
22618
22869
|
function rowToDto2(row) {
|
|
@@ -22626,7 +22877,7 @@ function rowToDto2(row) {
|
|
|
22626
22877
|
};
|
|
22627
22878
|
}
|
|
22628
22879
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
22629
|
-
const query = db.select().from(agentMemory).where(eq32(agentMemory.projectId, projectId)).orderBy(
|
|
22880
|
+
const query = db.select().from(agentMemory).where(eq32(agentMemory.projectId, projectId)).orderBy(desc16(agentMemory.updatedAt));
|
|
22630
22881
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
22631
22882
|
return rows.map(rowToDto2);
|
|
22632
22883
|
}
|
|
@@ -22657,12 +22908,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
22657
22908
|
updatedAt: now
|
|
22658
22909
|
}
|
|
22659
22910
|
}).run();
|
|
22660
|
-
const row = db.select().from(agentMemory).where(
|
|
22911
|
+
const row = db.select().from(agentMemory).where(and19(eq32(agentMemory.projectId, args.projectId), eq32(agentMemory.key, args.key))).get();
|
|
22661
22912
|
if (!row) throw new Error("memory upsert produced no row");
|
|
22662
22913
|
return rowToDto2(row);
|
|
22663
22914
|
}
|
|
22664
22915
|
function deleteMemoryEntry(db, projectId, key) {
|
|
22665
|
-
const result = db.delete(agentMemory).where(
|
|
22916
|
+
const result = db.delete(agentMemory).where(and19(eq32(agentMemory.projectId, projectId), eq32(agentMemory.key, key))).run();
|
|
22666
22917
|
const changes = result.changes ?? 0;
|
|
22667
22918
|
return changes > 0;
|
|
22668
22919
|
}
|
|
@@ -22691,16 +22942,16 @@ function writeCompactionNote(db, args) {
|
|
|
22691
22942
|
}).run();
|
|
22692
22943
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
22693
22944
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
22694
|
-
|
|
22945
|
+
and19(
|
|
22695
22946
|
eq32(agentMemory.projectId, args.projectId),
|
|
22696
22947
|
like2(agentMemory.key, `${sessionPrefix}%`)
|
|
22697
22948
|
)
|
|
22698
|
-
).orderBy(
|
|
22949
|
+
).orderBy(desc16(agentMemory.updatedAt)).all();
|
|
22699
22950
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
22700
22951
|
if (stale.length > 0) {
|
|
22701
22952
|
tx.delete(agentMemory).where(sql11`${agentMemory.id} IN (${sql11.join(stale.map((s) => sql11`${s}`), sql11`, `)})`).run();
|
|
22702
22953
|
}
|
|
22703
|
-
const row = tx.select().from(agentMemory).where(
|
|
22954
|
+
const row = tx.select().from(agentMemory).where(and19(eq32(agentMemory.projectId, args.projectId), eq32(agentMemory.key, key))).get();
|
|
22704
22955
|
if (row) inserted = rowToDto2(row);
|
|
22705
22956
|
});
|
|
22706
22957
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
categoryLabel,
|
|
9
9
|
determineAnswerMentioned,
|
|
10
10
|
normalizeProjectDomain
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ONI3TX2A.js";
|
|
12
12
|
|
|
13
13
|
// src/intelligence-service.ts
|
|
14
14
|
import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
|
|
@@ -104,13 +104,15 @@ var runs = sqliteTable("runs", {
|
|
|
104
104
|
status: text("status").notNull().default("queued"),
|
|
105
105
|
trigger: text("trigger").notNull().default("manual"),
|
|
106
106
|
location: text("location"),
|
|
107
|
+
sourceId: text("source_id"),
|
|
107
108
|
startedAt: text("started_at"),
|
|
108
109
|
finishedAt: text("finished_at"),
|
|
109
110
|
error: text("error"),
|
|
110
111
|
createdAt: text("created_at").notNull()
|
|
111
112
|
}, (table) => [
|
|
112
113
|
index("idx_runs_project").on(table.projectId),
|
|
113
|
-
index("idx_runs_status").on(table.status)
|
|
114
|
+
index("idx_runs_status").on(table.status),
|
|
115
|
+
index("idx_runs_source").on(table.sourceId)
|
|
114
116
|
]);
|
|
115
117
|
var querySnapshots = sqliteTable("query_snapshots", {
|
|
116
118
|
id: text("id").primaryKey(),
|
|
@@ -555,6 +557,11 @@ var trafficSources = sqliteTable("traffic_sources", {
|
|
|
555
557
|
lastSyncedAt: text("last_synced_at"),
|
|
556
558
|
lastCursor: text("last_cursor"),
|
|
557
559
|
lastError: text("last_error"),
|
|
560
|
+
// JSON-encoded array of normalized event IDs (e.g. `cloud-run:<ts>:<insertId>`)
|
|
561
|
+
// observed in the most recent successful sync. Bounded ring buffer used to
|
|
562
|
+
// dedupe across sync runs at the boundary timestamp where lastSyncedAt
|
|
563
|
+
// clamping alone leaves a small overlap window.
|
|
564
|
+
lastEventIds: text("last_event_ids"),
|
|
558
565
|
archivedAt: text("archived_at"),
|
|
559
566
|
configJson: text("config_json").notNull().default("{}"),
|
|
560
567
|
createdAt: text("created_at").notNull(),
|
|
@@ -1586,6 +1593,24 @@ var MIGRATION_VERSIONS = [
|
|
|
1586
1593
|
tx.run(sql.raw(`CREATE UNIQUE INDEX IF NOT EXISTS idx_ga_ai_ref_unique_v4
|
|
1587
1594
|
ON ga_ai_referrals(project_id, date, source, medium, source_dimension, channel_group, landing_page)`));
|
|
1588
1595
|
}
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
version: 51,
|
|
1599
|
+
name: "runs-source-id",
|
|
1600
|
+
statements: [
|
|
1601
|
+
`ALTER TABLE runs ADD COLUMN source_id TEXT`,
|
|
1602
|
+
`CREATE INDEX IF NOT EXISTS idx_runs_source ON runs(source_id)`
|
|
1603
|
+
]
|
|
1604
|
+
},
|
|
1605
|
+
{
|
|
1606
|
+
version: 52,
|
|
1607
|
+
name: "traffic-sources-last-event-ids",
|
|
1608
|
+
statements: [
|
|
1609
|
+
// JSON-encoded array of normalized event IDs from the previous sync,
|
|
1610
|
+
// used for cross-sync boundary-window dedupe so a longer default
|
|
1611
|
+
// sync window (or any overlapping re-sync) cannot double-count.
|
|
1612
|
+
`ALTER TABLE traffic_sources ADD COLUMN last_event_ids TEXT`
|
|
1613
|
+
]
|
|
1589
1614
|
}
|
|
1590
1615
|
];
|
|
1591
1616
|
function isDuplicateColumnError(err) {
|