@canonry/canonry 4.155.0 → 4.156.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/agent-workspace/skills/aero/references/orchestration.md +1 -1
- package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +14 -2
- package/assets/assets/{AuditHistoryPanel-BlbMBxOY.js → AuditHistoryPanel-gmJMVlXX.js} +1 -1
- package/assets/assets/{BacklinksPage-DA9EaV9q.js → BacklinksPage-Bv2sTCyN.js} +1 -1
- package/assets/assets/{HistoryPage-BTrIHjMQ.js → HistoryPage-13q7GkiI.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-CN6o5xFg.js → MeasurementPropertyPage-BgBqXT1p.js} +1 -1
- package/assets/assets/ProjectPage-EsGwKhot.js +15 -0
- package/assets/assets/{RunRow-ch_OlCPs.js → RunRow-bPzapIGJ.js} +1 -1
- package/assets/assets/{RunsPage-T89Y_yS8.js → RunsPage-Dqfz18lH.js} +1 -1
- package/assets/assets/{SettingsPage-DnmEq3F9.js → SettingsPage-_wMecn-V.js} +1 -1
- package/assets/assets/{TrafficPage-CW1UhaJt.js → TrafficPage-RYBdsOLB.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-r4Ji3wJm.js → TrafficSourceDetailPage-DpeMMyKU.js} +1 -1
- package/assets/assets/{extract-error-message-a8EErBoZ.js → extract-error-message-DUarAlyD.js} +1 -1
- package/assets/assets/index-BxegEwic.css +1 -0
- package/assets/assets/{index-BKq6k5Of.js → index-DP-fT2uI.js} +29 -29
- package/assets/assets/react-sigma_core.esm.min-DnypJl5w.js +312 -0
- package/assets/assets/vendor-lucide-BtYdosYk.js +1 -0
- package/assets/assets/{vendor-markdown-CpDJch4G.js → vendor-markdown-D274_wsI.js} +1 -1
- package/assets/assets/{vendor-radix-DnzzbllM.js → vendor-radix-BxMXSwbC.js} +1 -1
- package/assets/assets/{vendor-recharts-33ACLxsD.js → vendor-recharts-C4ktw_aE.js} +1 -1
- package/assets/assets/{vendor-tanstack-BjS7iY3g.js → vendor-tanstack-pFxw0eGP.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{chunk-VOL2BSM5.js → chunk-ABNN2ZVR.js} +1332 -29
- package/dist/{chunk-VIKPFI6F.js → chunk-AMPT2EP7.js} +328 -1
- package/dist/{chunk-OSHRDV36.js → chunk-CPASKGUE.js} +247 -1
- package/dist/{chunk-YSESZNVT.js → chunk-ZWESOFA5.js} +711 -235
- package/dist/cli.js +427 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-G4NV3X36.js → intelligence-service-WHC7B32V.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +11 -9
- package/assets/assets/ProjectPage-CsDbgp_0.js +0 -12
- package/assets/assets/index-DyL54s8x.css +0 -1
- package/assets/assets/vendor-lucide-ByU7vBHt.js +0 -1
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CPASKGUE.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -128,12 +128,15 @@ import {
|
|
|
128
128
|
siteCrawlEdges,
|
|
129
129
|
siteCrawlEventReceipts,
|
|
130
130
|
siteCrawlFindings,
|
|
131
|
+
siteCrawlGraphEdges,
|
|
132
|
+
siteCrawlGraphLayouts,
|
|
133
|
+
siteCrawlGraphNodes,
|
|
131
134
|
siteCrawlPages,
|
|
132
135
|
siteCrawlRunRequests,
|
|
133
136
|
siteCrawlSnapshots,
|
|
134
137
|
toAlertView,
|
|
135
138
|
usageCounters
|
|
136
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-ABNN2ZVR.js";
|
|
137
140
|
import {
|
|
138
141
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
139
142
|
AGENT_PROVIDER_IDS,
|
|
@@ -162,6 +165,8 @@ import {
|
|
|
162
165
|
SITE_AUDIT_DEFAULT_PAGE_LIMIT,
|
|
163
166
|
SITE_AUDIT_MAX_EDGE_LIMIT,
|
|
164
167
|
SITE_AUDIT_MAX_PAGE_LIMIT,
|
|
168
|
+
SITE_CRAWL_GRAPH_MAX_EDGES,
|
|
169
|
+
SITE_CRAWL_GRAPH_MAX_NODES,
|
|
165
170
|
SKILL_MANIFEST_FILENAME,
|
|
166
171
|
SchedulableRunKinds,
|
|
167
172
|
SkillsClients,
|
|
@@ -231,7 +236,7 @@ import {
|
|
|
231
236
|
validationError,
|
|
232
237
|
winnabilityClassLabel,
|
|
233
238
|
withRetry
|
|
234
|
-
} from "./chunk-
|
|
239
|
+
} from "./chunk-AMPT2EP7.js";
|
|
235
240
|
|
|
236
241
|
// src/telemetry.ts
|
|
237
242
|
import crypto from "crypto";
|
|
@@ -561,12 +566,12 @@ function checkLatestVersionForServer(opts) {
|
|
|
561
566
|
}
|
|
562
567
|
|
|
563
568
|
// src/server.ts
|
|
564
|
-
import { createRequire as
|
|
565
|
-
import
|
|
569
|
+
import { createRequire as createRequire4 } from "module";
|
|
570
|
+
import crypto25 from "crypto";
|
|
566
571
|
import fs8 from "fs";
|
|
567
572
|
import path9 from "path";
|
|
568
573
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
569
|
-
import { and as
|
|
574
|
+
import { and as and17, eq as eq23 } from "drizzle-orm";
|
|
570
575
|
import Fastify from "fastify";
|
|
571
576
|
import os5 from "os";
|
|
572
577
|
|
|
@@ -4144,7 +4149,7 @@ var JobRunner = class {
|
|
|
4144
4149
|
}
|
|
4145
4150
|
}
|
|
4146
4151
|
}
|
|
4147
|
-
incrementUsage(scope, metric,
|
|
4152
|
+
incrementUsage(scope, metric, count2) {
|
|
4148
4153
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4149
4154
|
const period = now.slice(0, 10);
|
|
4150
4155
|
this.db.insert(usageCounters).values({
|
|
@@ -4152,11 +4157,11 @@ var JobRunner = class {
|
|
|
4152
4157
|
scope,
|
|
4153
4158
|
period,
|
|
4154
4159
|
metric,
|
|
4155
|
-
count,
|
|
4160
|
+
count: count2,
|
|
4156
4161
|
updatedAt: now
|
|
4157
4162
|
}).onConflictDoUpdate({
|
|
4158
4163
|
target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
|
|
4159
|
-
set: { count: sql2`${usageCounters.count} + ${
|
|
4164
|
+
set: { count: sql2`${usageCounters.count} + ${count2}`, updatedAt: now }
|
|
4160
4165
|
}).run();
|
|
4161
4166
|
}
|
|
4162
4167
|
flushProviderUsage(providerDispatchCounts, providerReservations) {
|
|
@@ -6930,9 +6935,421 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
6930
6935
|
}
|
|
6931
6936
|
|
|
6932
6937
|
// src/execute-site-audit.ts
|
|
6933
|
-
import
|
|
6934
|
-
import { and as
|
|
6938
|
+
import crypto17 from "crypto";
|
|
6939
|
+
import { and as and11, eq as eq14, sql as sql6 } from "drizzle-orm";
|
|
6935
6940
|
import { runSiteCrawl } from "@canonry/aeo-audit";
|
|
6941
|
+
|
|
6942
|
+
// src/site-crawl-graph-layout.ts
|
|
6943
|
+
import crypto16 from "crypto";
|
|
6944
|
+
import { createRequire as createRequire3 } from "module";
|
|
6945
|
+
import { Worker } from "worker_threads";
|
|
6946
|
+
import { and as and10, asc, count, desc as desc6, eq as eq13, isNull, ne as ne2, or, sql as sql5 } from "drizzle-orm";
|
|
6947
|
+
import { alias } from "drizzle-orm/sqlite-core";
|
|
6948
|
+
var SITE_CRAWL_GRAPH_LAYOUT_VERSION = "site-health-fa2-v1";
|
|
6949
|
+
var SITE_CRAWL_GRAPH_LAYOUT_TIMEOUT_MS = 15e3;
|
|
6950
|
+
var SITE_CRAWL_GRAPH_SEED_SCALE = 100;
|
|
6951
|
+
var graphSourcePage = alias(siteCrawlPages, "layout_graph_source_page");
|
|
6952
|
+
var graphTargetPage = alias(siteCrawlPages, "layout_graph_target_page");
|
|
6953
|
+
var FORCE_ATLAS_SETTINGS = Object.freeze({
|
|
6954
|
+
adjustSizes: false,
|
|
6955
|
+
barnesHutOptimize: true,
|
|
6956
|
+
barnesHutTheta: 0.5,
|
|
6957
|
+
edgeWeightInfluence: 0.5,
|
|
6958
|
+
gravity: 1,
|
|
6959
|
+
linLogMode: false,
|
|
6960
|
+
outboundAttractionDistribution: false,
|
|
6961
|
+
scalingRatio: 10,
|
|
6962
|
+
slowDown: 10,
|
|
6963
|
+
strongGravityMode: false
|
|
6964
|
+
});
|
|
6965
|
+
var WORKER_SOURCE = String.raw`
|
|
6966
|
+
const {parentPort, workerData} = require('node:worker_threads');
|
|
6967
|
+
try {
|
|
6968
|
+
const graphologyModule = require(workerData.graphologyPath);
|
|
6969
|
+
const forceAtlasModule = require(workerData.forceAtlasPath);
|
|
6970
|
+
const MultiDirectedGraph = graphologyModule.MultiDirectedGraph;
|
|
6971
|
+
const forceAtlas2 = forceAtlasModule.default || forceAtlasModule;
|
|
6972
|
+
const graph = new MultiDirectedGraph({allowSelfLoops: true});
|
|
6973
|
+
for (const node of workerData.input.nodes) {
|
|
6974
|
+
graph.addNode(node.nodeKey, {x: node.x, y: node.y});
|
|
6975
|
+
}
|
|
6976
|
+
for (const edge of workerData.input.edges) {
|
|
6977
|
+
graph.addDirectedEdgeWithKey(edge.edgeKey, edge.sourceNodeKey, edge.targetNodeKey, {
|
|
6978
|
+
weight: 1 + Math.log2(Math.max(1, edge.occurrences)),
|
|
6979
|
+
});
|
|
6980
|
+
}
|
|
6981
|
+
if (graph.order > 1 && graph.size > 0) {
|
|
6982
|
+
forceAtlas2.assign(graph, {
|
|
6983
|
+
iterations: workerData.input.iterations,
|
|
6984
|
+
settings: workerData.settings,
|
|
6985
|
+
});
|
|
6986
|
+
}
|
|
6987
|
+
const positions = [];
|
|
6988
|
+
graph.forEachNode((nodeKey, attributes) => {
|
|
6989
|
+
positions.push({nodeKey, x: attributes.x, y: attributes.y});
|
|
6990
|
+
});
|
|
6991
|
+
parentPort.postMessage({ok: true, positions});
|
|
6992
|
+
} catch (error) {
|
|
6993
|
+
parentPort.postMessage({
|
|
6994
|
+
ok: false,
|
|
6995
|
+
error: error && typeof error.message === 'string' ? error.message : String(error),
|
|
6996
|
+
});
|
|
6997
|
+
}
|
|
6998
|
+
`;
|
|
6999
|
+
var GraphLayoutTimeoutError = class extends Error {
|
|
7000
|
+
name = "GraphLayoutTimeoutError";
|
|
7001
|
+
};
|
|
7002
|
+
function hashUnit(value) {
|
|
7003
|
+
let hash = 2166136261;
|
|
7004
|
+
for (let i = 0; i < value.length; i++) {
|
|
7005
|
+
hash ^= value.charCodeAt(i);
|
|
7006
|
+
hash = Math.imul(hash, 16777619);
|
|
7007
|
+
}
|
|
7008
|
+
return (hash >>> 0) / 4294967296;
|
|
7009
|
+
}
|
|
7010
|
+
function pathSection(path10) {
|
|
7011
|
+
return path10.split("/").filter(Boolean)[0] ?? "__root__";
|
|
7012
|
+
}
|
|
7013
|
+
function seedSiteCrawlGraphNodes(nodes, rootNodeKey, priorPositions) {
|
|
7014
|
+
return [...nodes].sort((a, b) => a.sampleRank - b.sampleRank || a.nodeKey.localeCompare(b.nodeKey)).map((node) => {
|
|
7015
|
+
const prior = priorPositions?.get(node.nodeKey);
|
|
7016
|
+
if (prior && Number.isFinite(prior.x) && Number.isFinite(prior.y)) {
|
|
7017
|
+
return {
|
|
7018
|
+
...node,
|
|
7019
|
+
x: prior.x * SITE_CRAWL_GRAPH_SEED_SCALE,
|
|
7020
|
+
y: prior.y * SITE_CRAWL_GRAPH_SEED_SCALE
|
|
7021
|
+
};
|
|
7022
|
+
}
|
|
7023
|
+
if (node.nodeKey === rootNodeKey) return { ...node, x: 0, y: 0 };
|
|
7024
|
+
const section = pathSection(node.path);
|
|
7025
|
+
const sectionAngle = hashUnit(`section:${section}`) * Math.PI * 2;
|
|
7026
|
+
const sectionRadius = section === "__root__" ? 24 : 100;
|
|
7027
|
+
const clusterX = Math.cos(sectionAngle) * sectionRadius;
|
|
7028
|
+
const clusterY = Math.sin(sectionAngle) * sectionRadius;
|
|
7029
|
+
const localAngle = hashUnit(`node-angle:${node.nodeKey}`) * Math.PI * 2;
|
|
7030
|
+
const depth = Math.max(0, Math.min(12, node.depth ?? 4));
|
|
7031
|
+
const localRadius = 4 + depth * 2.5 + Math.sqrt(hashUnit(`node-radius:${node.nodeKey}`)) * 14;
|
|
7032
|
+
return {
|
|
7033
|
+
...node,
|
|
7034
|
+
x: clusterX + Math.cos(localAngle) * localRadius,
|
|
7035
|
+
y: clusterY + Math.sin(localAngle) * localRadius
|
|
7036
|
+
};
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
7039
|
+
function adaptiveForceAtlasIterations(nodeCount, edgeCount) {
|
|
7040
|
+
if (nodeCount <= 100 && edgeCount <= 500) return 100;
|
|
7041
|
+
if (nodeCount <= 500 && edgeCount <= 2500) return 70;
|
|
7042
|
+
if (nodeCount <= 2e3 && edgeCount <= 1e4) return 40;
|
|
7043
|
+
if (nodeCount <= 5e3 && edgeCount <= 2e4) return 24;
|
|
7044
|
+
if (nodeCount <= 1e4 && edgeCount <= 35e3) return 12;
|
|
7045
|
+
return 6;
|
|
7046
|
+
}
|
|
7047
|
+
async function computeForceAtlasPositions(input, options = {}) {
|
|
7048
|
+
if (input.nodes.length <= 1 || input.edges.length === 0) {
|
|
7049
|
+
return input.nodes.map(({ nodeKey, x, y }) => ({ nodeKey, x, y }));
|
|
7050
|
+
}
|
|
7051
|
+
const requireFromPackage = createRequire3(import.meta.url);
|
|
7052
|
+
const worker = new Worker(WORKER_SOURCE, {
|
|
7053
|
+
eval: true,
|
|
7054
|
+
workerData: {
|
|
7055
|
+
graphologyPath: requireFromPackage.resolve("graphology"),
|
|
7056
|
+
forceAtlasPath: requireFromPackage.resolve("graphology-layout-forceatlas2"),
|
|
7057
|
+
input,
|
|
7058
|
+
settings: FORCE_ATLAS_SETTINGS
|
|
7059
|
+
}
|
|
7060
|
+
});
|
|
7061
|
+
const timeoutMs = options.timeoutMs ?? SITE_CRAWL_GRAPH_LAYOUT_TIMEOUT_MS;
|
|
7062
|
+
return await new Promise((resolve, reject) => {
|
|
7063
|
+
let settled = false;
|
|
7064
|
+
const finish = (result) => {
|
|
7065
|
+
if (settled) return;
|
|
7066
|
+
settled = true;
|
|
7067
|
+
clearTimeout(timer);
|
|
7068
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
7069
|
+
void worker.terminate();
|
|
7070
|
+
if (result.error) reject(result.error instanceof Error ? result.error : new Error("Graph layout failed with a non-Error reason", { cause: result.error }));
|
|
7071
|
+
else resolve(result.positions ?? []);
|
|
7072
|
+
};
|
|
7073
|
+
const onAbort = () => finish({ error: options.signal?.reason ?? new DOMException("Layout aborted", "AbortError") });
|
|
7074
|
+
const timer = setTimeout(() => finish({ error: new GraphLayoutTimeoutError(`Graph layout exceeded ${timeoutMs}ms`) }), timeoutMs);
|
|
7075
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
7076
|
+
if (options.signal?.aborted) return onAbort();
|
|
7077
|
+
worker.once("message", (message) => {
|
|
7078
|
+
if (!message.ok) finish({ error: new Error(message.error ?? "Graph layout worker failed") });
|
|
7079
|
+
else finish({ positions: message.positions });
|
|
7080
|
+
});
|
|
7081
|
+
worker.once("error", (error) => finish({ error }));
|
|
7082
|
+
worker.once("exit", (code) => {
|
|
7083
|
+
if (code !== 0 && !settled) finish({ error: new Error(`Graph layout worker exited with code ${code}`) });
|
|
7084
|
+
});
|
|
7085
|
+
});
|
|
7086
|
+
}
|
|
7087
|
+
function normalizePositions(seededNodes, computed, rootNodeKey) {
|
|
7088
|
+
const byKey = new Map(computed.map((position) => [position.nodeKey, position]));
|
|
7089
|
+
if (byKey.size !== seededNodes.length) throw new Error("Graph layout returned an incomplete node set");
|
|
7090
|
+
const root = rootNodeKey ? byKey.get(rootNodeKey) : null;
|
|
7091
|
+
const centerX = root?.x ?? computed.reduce((sum, position) => sum + position.x, 0) / Math.max(1, computed.length);
|
|
7092
|
+
const centerY = root?.y ?? computed.reduce((sum, position) => sum + position.y, 0) / Math.max(1, computed.length);
|
|
7093
|
+
if (!Number.isFinite(centerX) || !Number.isFinite(centerY)) throw new Error("Graph layout returned a non-finite center");
|
|
7094
|
+
let scale = 0;
|
|
7095
|
+
for (const position of computed) {
|
|
7096
|
+
if (!Number.isFinite(position.x) || !Number.isFinite(position.y)) throw new Error("Graph layout returned non-finite coordinates");
|
|
7097
|
+
scale = Math.max(scale, Math.abs(position.x - centerX), Math.abs(position.y - centerY));
|
|
7098
|
+
}
|
|
7099
|
+
scale = scale > 0 ? scale : 1;
|
|
7100
|
+
return seededNodes.map((node) => {
|
|
7101
|
+
const position = byKey.get(node.nodeKey);
|
|
7102
|
+
if (!position) throw new Error(`Graph layout omitted ${node.nodeKey}`);
|
|
7103
|
+
return {
|
|
7104
|
+
nodeKey: node.nodeKey,
|
|
7105
|
+
sampleRank: node.sampleRank,
|
|
7106
|
+
x: Math.round((position.x - centerX) / scale * 1e6) / 1e6,
|
|
7107
|
+
y: Math.round((position.y - centerY) / scale * 1e6) / 1e6
|
|
7108
|
+
};
|
|
7109
|
+
});
|
|
7110
|
+
}
|
|
7111
|
+
async function layoutSiteCrawlGraphInput(input, options = {}) {
|
|
7112
|
+
options.signal?.throwIfAborted();
|
|
7113
|
+
if (input.nodes.length === 0) {
|
|
7114
|
+
return {
|
|
7115
|
+
state: "unavailable",
|
|
7116
|
+
failureCode: "empty-crawl",
|
|
7117
|
+
totalNodes: input.totalNodes,
|
|
7118
|
+
totalEdges: input.totalEdges,
|
|
7119
|
+
nodeCount: 0,
|
|
7120
|
+
edgeCount: 0,
|
|
7121
|
+
nodes: [],
|
|
7122
|
+
edges: []
|
|
7123
|
+
};
|
|
7124
|
+
}
|
|
7125
|
+
const seeded = seedSiteCrawlGraphNodes(input.nodes, input.rootNodeKey, input.priorPositions);
|
|
7126
|
+
try {
|
|
7127
|
+
const compute = options.computePositions ?? computeForceAtlasPositions;
|
|
7128
|
+
const positions = await compute({
|
|
7129
|
+
nodes: seeded,
|
|
7130
|
+
edges: input.edges,
|
|
7131
|
+
iterations: adaptiveForceAtlasIterations(input.nodes.length, input.edges.length)
|
|
7132
|
+
}, { signal: options.signal, timeoutMs: options.timeoutMs });
|
|
7133
|
+
options.signal?.throwIfAborted();
|
|
7134
|
+
return {
|
|
7135
|
+
state: "ready",
|
|
7136
|
+
layoutVersion: SITE_CRAWL_GRAPH_LAYOUT_VERSION,
|
|
7137
|
+
totalNodes: input.totalNodes,
|
|
7138
|
+
totalEdges: input.totalEdges,
|
|
7139
|
+
nodeCount: input.nodes.length,
|
|
7140
|
+
edgeCount: input.edges.length,
|
|
7141
|
+
nodes: normalizePositions(seeded, positions, input.rootNodeKey),
|
|
7142
|
+
edges: input.edges.map((edge, sampleRank) => ({ ...edge, sampleRank }))
|
|
7143
|
+
};
|
|
7144
|
+
} catch (error) {
|
|
7145
|
+
if (options.signal?.aborted) options.signal.throwIfAborted();
|
|
7146
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
7147
|
+
return {
|
|
7148
|
+
state: "unavailable",
|
|
7149
|
+
failureCode: error instanceof GraphLayoutTimeoutError ? "layout-timeout" : "layout-error",
|
|
7150
|
+
totalNodes: input.totalNodes,
|
|
7151
|
+
totalEdges: input.totalEdges,
|
|
7152
|
+
nodeCount: 0,
|
|
7153
|
+
edgeCount: 0,
|
|
7154
|
+
nodes: [],
|
|
7155
|
+
edges: []
|
|
7156
|
+
};
|
|
7157
|
+
}
|
|
7158
|
+
}
|
|
7159
|
+
function loadPriorSiteCrawlGraphPositions(db, scope) {
|
|
7160
|
+
const prior = db.select({
|
|
7161
|
+
runId: siteCrawlSnapshots.runId,
|
|
7162
|
+
attemptId: siteCrawlSnapshots.attemptId
|
|
7163
|
+
}).from(siteCrawlSnapshots).innerJoin(siteCrawlGraphLayouts, and10(
|
|
7164
|
+
eq13(siteCrawlGraphLayouts.projectId, siteCrawlSnapshots.projectId),
|
|
7165
|
+
eq13(siteCrawlGraphLayouts.runId, siteCrawlSnapshots.runId),
|
|
7166
|
+
eq13(siteCrawlGraphLayouts.attemptId, siteCrawlSnapshots.attemptId)
|
|
7167
|
+
)).where(and10(
|
|
7168
|
+
eq13(siteCrawlSnapshots.projectId, scope.projectId),
|
|
7169
|
+
ne2(siteCrawlSnapshots.runId, scope.runId),
|
|
7170
|
+
eq13(siteCrawlSnapshots.complete, true),
|
|
7171
|
+
eq13(siteCrawlGraphLayouts.state, "ready"),
|
|
7172
|
+
eq13(siteCrawlGraphLayouts.layoutVersion, SITE_CRAWL_GRAPH_LAYOUT_VERSION)
|
|
7173
|
+
)).orderBy(desc6(siteCrawlSnapshots.createdAt), desc6(siteCrawlSnapshots.id)).limit(1).get();
|
|
7174
|
+
if (!prior?.attemptId) return /* @__PURE__ */ new Map();
|
|
7175
|
+
return new Map(db.select({
|
|
7176
|
+
nodeKey: siteCrawlGraphNodes.nodeKey,
|
|
7177
|
+
x: siteCrawlGraphNodes.x,
|
|
7178
|
+
y: siteCrawlGraphNodes.y
|
|
7179
|
+
}).from(siteCrawlGraphNodes).where(and10(
|
|
7180
|
+
eq13(siteCrawlGraphNodes.projectId, scope.projectId),
|
|
7181
|
+
eq13(siteCrawlGraphNodes.runId, prior.runId),
|
|
7182
|
+
eq13(siteCrawlGraphNodes.attemptId, prior.attemptId)
|
|
7183
|
+
)).all().map((node) => [node.nodeKey, { x: node.x, y: node.y }]));
|
|
7184
|
+
}
|
|
7185
|
+
async function prepareSiteCrawlGraphLayout(db, scope, options = {}) {
|
|
7186
|
+
let totalNodes = 0;
|
|
7187
|
+
let totalEdges = 0;
|
|
7188
|
+
try {
|
|
7189
|
+
const assertNotAborted = () => options.signal?.throwIfAborted();
|
|
7190
|
+
assertNotAborted();
|
|
7191
|
+
const pageScope = [
|
|
7192
|
+
eq13(siteCrawlPages.projectId, scope.projectId),
|
|
7193
|
+
eq13(siteCrawlPages.runId, scope.runId),
|
|
7194
|
+
eq13(siteCrawlPages.attemptId, scope.attemptId)
|
|
7195
|
+
];
|
|
7196
|
+
const edgeScope = [
|
|
7197
|
+
eq13(siteCrawlEdges.projectId, scope.projectId),
|
|
7198
|
+
eq13(siteCrawlEdges.runId, scope.runId),
|
|
7199
|
+
eq13(siteCrawlEdges.attemptId, scope.attemptId),
|
|
7200
|
+
eq13(siteCrawlEdges.internal, true),
|
|
7201
|
+
eq13(siteCrawlEdges.relation, "anchor")
|
|
7202
|
+
];
|
|
7203
|
+
totalNodes = db.select({ value: count() }).from(siteCrawlPages).where(and10(...pageScope)).get()?.value ?? 0;
|
|
7204
|
+
assertNotAborted();
|
|
7205
|
+
totalEdges = db.select({ value: count() }).from(siteCrawlEdges).innerJoin(graphSourcePage, and10(
|
|
7206
|
+
eq13(graphSourcePage.projectId, scope.projectId),
|
|
7207
|
+
eq13(graphSourcePage.runId, scope.runId),
|
|
7208
|
+
eq13(graphSourcePage.attemptId, scope.attemptId),
|
|
7209
|
+
eq13(graphSourcePage.nodeKey, siteCrawlEdges.sourceNodeKey)
|
|
7210
|
+
)).innerJoin(graphTargetPage, and10(
|
|
7211
|
+
eq13(graphTargetPage.projectId, scope.projectId),
|
|
7212
|
+
eq13(graphTargetPage.runId, scope.runId),
|
|
7213
|
+
eq13(graphTargetPage.attemptId, scope.attemptId),
|
|
7214
|
+
eq13(graphTargetPage.nodeKey, siteCrawlEdges.targetNodeKey)
|
|
7215
|
+
)).where(and10(...edgeScope)).get()?.value ?? 0;
|
|
7216
|
+
assertNotAborted();
|
|
7217
|
+
const graphSeedColumns = {
|
|
7218
|
+
nodeKey: siteCrawlPages.nodeKey,
|
|
7219
|
+
path: siteCrawlPages.path,
|
|
7220
|
+
depth: siteCrawlPages.depth
|
|
7221
|
+
};
|
|
7222
|
+
const rootRows = db.select(graphSeedColumns).from(siteCrawlPages).where(and10(...pageScope, eq13(siteCrawlPages.url, scope.rootUrl))).orderBy(asc(siteCrawlPages.nodeKey)).limit(1).all();
|
|
7223
|
+
assertNotAborted();
|
|
7224
|
+
const retainedRows = [...rootRows];
|
|
7225
|
+
if (retainedRows.length < SITE_CRAWL_GRAPH_MAX_NODES) {
|
|
7226
|
+
retainedRows.push(...db.select(graphSeedColumns).from(siteCrawlPages).where(and10(...pageScope, eq13(siteCrawlPages.depth, 0), ne2(siteCrawlPages.url, scope.rootUrl))).orderBy(asc(siteCrawlPages.nodeKey)).limit(SITE_CRAWL_GRAPH_MAX_NODES - retainedRows.length).all());
|
|
7227
|
+
}
|
|
7228
|
+
assertNotAborted();
|
|
7229
|
+
if (retainedRows.length < SITE_CRAWL_GRAPH_MAX_NODES) {
|
|
7230
|
+
retainedRows.push(...db.select(graphSeedColumns).from(siteCrawlPages).where(and10(
|
|
7231
|
+
...pageScope,
|
|
7232
|
+
or(isNull(siteCrawlPages.depth), ne2(siteCrawlPages.depth, 0)),
|
|
7233
|
+
ne2(siteCrawlPages.url, scope.rootUrl)
|
|
7234
|
+
)).orderBy(desc6(siteCrawlPages.linkScoreNormalized), asc(siteCrawlPages.nodeKey)).limit(SITE_CRAWL_GRAPH_MAX_NODES - retainedRows.length).all());
|
|
7235
|
+
}
|
|
7236
|
+
assertNotAborted();
|
|
7237
|
+
const nodes = retainedRows.map((row, sampleRank) => ({
|
|
7238
|
+
nodeKey: row.nodeKey,
|
|
7239
|
+
path: row.path,
|
|
7240
|
+
depth: row.depth,
|
|
7241
|
+
sampleRank
|
|
7242
|
+
}));
|
|
7243
|
+
const rootNodeKey = rootRows[0]?.nodeKey ?? null;
|
|
7244
|
+
const priorPositions = loadPriorSiteCrawlGraphPositions(db, scope);
|
|
7245
|
+
assertNotAborted();
|
|
7246
|
+
const selectedKeys = JSON.stringify(nodes.map((node) => node.nodeKey));
|
|
7247
|
+
const edgeRows = nodes.length === 0 ? [] : db.all(sql5`
|
|
7248
|
+
WITH selected(node_key) AS (SELECT value FROM json_each(${selectedKeys}))
|
|
7249
|
+
SELECT
|
|
7250
|
+
edge.edge_key AS edgeKey,
|
|
7251
|
+
edge.source_node_key AS sourceNodeKey,
|
|
7252
|
+
edge.target_node_key AS targetNodeKey,
|
|
7253
|
+
edge.followable AS followable,
|
|
7254
|
+
edge.occurrences AS occurrences
|
|
7255
|
+
FROM site_crawl_edges AS edge
|
|
7256
|
+
INNER JOIN selected AS source ON source.node_key = edge.source_node_key
|
|
7257
|
+
INNER JOIN selected AS target ON target.node_key = edge.target_node_key
|
|
7258
|
+
WHERE edge.project_id = ${scope.projectId}
|
|
7259
|
+
AND edge.run_id = ${scope.runId}
|
|
7260
|
+
AND edge.attempt_id = ${scope.attemptId}
|
|
7261
|
+
AND edge.internal = 1
|
|
7262
|
+
AND edge.relation = 'anchor'
|
|
7263
|
+
ORDER BY edge.occurrences DESC, edge.edge_key ASC
|
|
7264
|
+
LIMIT ${SITE_CRAWL_GRAPH_MAX_EDGES}
|
|
7265
|
+
`);
|
|
7266
|
+
assertNotAborted();
|
|
7267
|
+
return await layoutSiteCrawlGraphInput({
|
|
7268
|
+
totalNodes,
|
|
7269
|
+
totalEdges,
|
|
7270
|
+
nodes,
|
|
7271
|
+
edges: edgeRows,
|
|
7272
|
+
rootNodeKey,
|
|
7273
|
+
priorPositions
|
|
7274
|
+
}, options);
|
|
7275
|
+
} catch (error) {
|
|
7276
|
+
if (options.signal?.aborted) options.signal.throwIfAborted();
|
|
7277
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
7278
|
+
return {
|
|
7279
|
+
state: "unavailable",
|
|
7280
|
+
failureCode: "layout-error",
|
|
7281
|
+
totalNodes,
|
|
7282
|
+
totalEdges,
|
|
7283
|
+
nodeCount: 0,
|
|
7284
|
+
edgeCount: 0,
|
|
7285
|
+
nodes: [],
|
|
7286
|
+
edges: []
|
|
7287
|
+
};
|
|
7288
|
+
}
|
|
7289
|
+
}
|
|
7290
|
+
function persistSiteCrawlGraphLayout(db, scope, layout, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
7291
|
+
db.transaction((tx) => {
|
|
7292
|
+
tx.delete(siteCrawlGraphLayouts).where(and10(
|
|
7293
|
+
eq13(siteCrawlGraphLayouts.projectId, scope.projectId),
|
|
7294
|
+
eq13(siteCrawlGraphLayouts.runId, scope.runId),
|
|
7295
|
+
eq13(siteCrawlGraphLayouts.attemptId, scope.attemptId)
|
|
7296
|
+
)).run();
|
|
7297
|
+
tx.insert(siteCrawlGraphLayouts).values({
|
|
7298
|
+
id: crypto16.randomUUID(),
|
|
7299
|
+
projectId: scope.projectId,
|
|
7300
|
+
runId: scope.runId,
|
|
7301
|
+
attemptId: scope.attemptId,
|
|
7302
|
+
state: layout.state,
|
|
7303
|
+
layoutVersion: layout.state === "ready" ? layout.layoutVersion : null,
|
|
7304
|
+
failureCode: layout.state === "unavailable" ? layout.failureCode : null,
|
|
7305
|
+
totalNodes: layout.totalNodes,
|
|
7306
|
+
totalEdges: layout.totalEdges,
|
|
7307
|
+
nodeCount: layout.nodeCount,
|
|
7308
|
+
edgeCount: layout.edgeCount,
|
|
7309
|
+
createdAt: now,
|
|
7310
|
+
updatedAt: now
|
|
7311
|
+
}).run();
|
|
7312
|
+
if (layout.state !== "ready") return;
|
|
7313
|
+
const batchSize = 250;
|
|
7314
|
+
for (let offset = 0; offset < layout.nodes.length; offset += batchSize) {
|
|
7315
|
+
tx.insert(siteCrawlGraphNodes).values(layout.nodes.slice(offset, offset + batchSize).map((node) => ({
|
|
7316
|
+
id: crypto16.randomUUID(),
|
|
7317
|
+
projectId: scope.projectId,
|
|
7318
|
+
runId: scope.runId,
|
|
7319
|
+
attemptId: scope.attemptId,
|
|
7320
|
+
nodeKey: node.nodeKey,
|
|
7321
|
+
sampleRank: node.sampleRank,
|
|
7322
|
+
x: node.x,
|
|
7323
|
+
y: node.y,
|
|
7324
|
+
createdAt: now
|
|
7325
|
+
}))).run();
|
|
7326
|
+
}
|
|
7327
|
+
for (let offset = 0; offset < layout.edges.length; offset += batchSize) {
|
|
7328
|
+
tx.insert(siteCrawlGraphEdges).values(layout.edges.slice(offset, offset + batchSize).map((edge) => ({
|
|
7329
|
+
id: crypto16.randomUUID(),
|
|
7330
|
+
projectId: scope.projectId,
|
|
7331
|
+
runId: scope.runId,
|
|
7332
|
+
attemptId: scope.attemptId,
|
|
7333
|
+
edgeKey: edge.edgeKey,
|
|
7334
|
+
sampleRank: edge.sampleRank,
|
|
7335
|
+
sourceNodeKey: edge.sourceNodeKey,
|
|
7336
|
+
targetNodeKey: edge.targetNodeKey,
|
|
7337
|
+
followable: edge.followable,
|
|
7338
|
+
occurrences: edge.occurrences,
|
|
7339
|
+
createdAt: now
|
|
7340
|
+
}))).run();
|
|
7341
|
+
}
|
|
7342
|
+
});
|
|
7343
|
+
}
|
|
7344
|
+
function deleteSiteCrawlGraphLayout(db, scope) {
|
|
7345
|
+
db.delete(siteCrawlGraphLayouts).where(and10(
|
|
7346
|
+
eq13(siteCrawlGraphLayouts.projectId, scope.projectId),
|
|
7347
|
+
eq13(siteCrawlGraphLayouts.runId, scope.runId),
|
|
7348
|
+
eq13(siteCrawlGraphLayouts.attemptId, scope.attemptId)
|
|
7349
|
+
)).run();
|
|
7350
|
+
}
|
|
7351
|
+
|
|
7352
|
+
// src/execute-site-audit.ts
|
|
6936
7353
|
var log12 = createLogger("SiteAudit");
|
|
6937
7354
|
var SiteAuditCancelledError = class extends Error {
|
|
6938
7355
|
name = "SiteAuditCancelledError";
|
|
@@ -6956,6 +7373,22 @@ function clampSiteAuditEdgeLimit(limit) {
|
|
|
6956
7373
|
function toPageFactor(factor) {
|
|
6957
7374
|
return { id: factor.id, name: factor.name, weight: factor.weight, score: factor.score };
|
|
6958
7375
|
}
|
|
7376
|
+
function toCrawlAuditFactor(factor) {
|
|
7377
|
+
return {
|
|
7378
|
+
...toPageFactor(factor),
|
|
7379
|
+
status: factorStatusFromScore(factor.score),
|
|
7380
|
+
applicable: factor.applicable ?? null,
|
|
7381
|
+
findings: factor.findings,
|
|
7382
|
+
recommendations: factor.recommendations
|
|
7383
|
+
};
|
|
7384
|
+
}
|
|
7385
|
+
function crawlAuditFields(audit) {
|
|
7386
|
+
return {
|
|
7387
|
+
schemaVersion: "1.0",
|
|
7388
|
+
factors: audit.factors.map(toCrawlAuditFactor),
|
|
7389
|
+
criticalDefects: audit.criticalDefects
|
|
7390
|
+
};
|
|
7391
|
+
}
|
|
6959
7392
|
function computeFactorAverages(pages) {
|
|
6960
7393
|
const byId = /* @__PURE__ */ new Map();
|
|
6961
7394
|
for (const page of pages) {
|
|
@@ -7063,10 +7496,10 @@ function deadLinkCheckedCount(edges, pages) {
|
|
|
7063
7496
|
}
|
|
7064
7497
|
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
7065
7498
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7066
|
-
const claim = db.update(runs).set({ status: "running", startedAt }).where(
|
|
7499
|
+
const claim = db.update(runs).set({ status: "running", startedAt }).where(and11(eq14(runs.id, runId), eq14(runs.projectId, projectId), eq14(runs.status, "queued"))).run();
|
|
7067
7500
|
if (claim.changes === 0) return;
|
|
7068
|
-
const attemptNumber = (db.select({ value:
|
|
7069
|
-
const attemptId =
|
|
7501
|
+
const attemptNumber = (db.select({ value: sql6`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq14(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
|
|
7502
|
+
const attemptId = crypto17.randomUUID();
|
|
7070
7503
|
db.insert(siteCrawlAttempts).values({
|
|
7071
7504
|
id: attemptId,
|
|
7072
7505
|
projectId,
|
|
@@ -7100,30 +7533,30 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7100
7533
|
const nodeKey = nodeKeyByUrl.get(url);
|
|
7101
7534
|
if (!nodeKey) continue;
|
|
7102
7535
|
const scope = [
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7536
|
+
eq14(siteCrawlEdges.projectId, projectId),
|
|
7537
|
+
eq14(siteCrawlEdges.runId, runId),
|
|
7538
|
+
eq14(siteCrawlEdges.attemptId, attemptId)
|
|
7106
7539
|
];
|
|
7107
|
-
tx.update(siteCrawlEdges).set({ sourceNodeKey: nodeKey, updatedAt: now }).where(
|
|
7540
|
+
tx.update(siteCrawlEdges).set({ sourceNodeKey: nodeKey, updatedAt: now }).where(and11(
|
|
7108
7541
|
...scope,
|
|
7109
|
-
|
|
7542
|
+
eq14(siteCrawlEdges.sourceUrl, url)
|
|
7110
7543
|
)).run();
|
|
7111
|
-
tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(
|
|
7544
|
+
tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(and11(
|
|
7112
7545
|
...scope,
|
|
7113
|
-
|
|
7546
|
+
eq14(siteCrawlEdges.targetUrl, url)
|
|
7114
7547
|
)).run();
|
|
7115
|
-
tx.update(siteCrawlPages).set({ canonicalNodeKey: nodeKey, updatedAt: now }).where(
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7548
|
+
tx.update(siteCrawlPages).set({ canonicalNodeKey: nodeKey, updatedAt: now }).where(and11(
|
|
7549
|
+
eq14(siteCrawlPages.projectId, projectId),
|
|
7550
|
+
eq14(siteCrawlPages.runId, runId),
|
|
7551
|
+
eq14(siteCrawlPages.attemptId, attemptId),
|
|
7552
|
+
eq14(siteCrawlPages.canonicalUrl, url)
|
|
7120
7553
|
)).run();
|
|
7121
7554
|
}
|
|
7122
7555
|
};
|
|
7123
7556
|
const persistPage = (tx, page, now) => {
|
|
7124
7557
|
const audit = page.audit;
|
|
7125
7558
|
tx.insert(siteCrawlPages).values({
|
|
7126
|
-
id:
|
|
7559
|
+
id: crypto17.randomUUID(),
|
|
7127
7560
|
projectId,
|
|
7128
7561
|
runId,
|
|
7129
7562
|
attemptId,
|
|
@@ -7151,7 +7584,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7151
7584
|
indexabilityReasons: page.indexability.reasons,
|
|
7152
7585
|
auditState: pageAuditState(page),
|
|
7153
7586
|
auditScore: audit?.overallScore ?? null,
|
|
7154
|
-
auditFields: audit ?
|
|
7587
|
+
auditFields: audit ? crawlAuditFields(audit) : {},
|
|
7155
7588
|
inventoryEligible: isInventoryEligible(page),
|
|
7156
7589
|
depth: page.depth,
|
|
7157
7590
|
inboundUniqueEdges: page.metrics.inbound.uniqueEdges,
|
|
@@ -7172,7 +7605,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7172
7605
|
discoveryProvenance: [{ discoveredFrom: page.provenance.discoveredFrom, sitemapSources: page.provenance.sitemapSources, root: page.provenance.root }],
|
|
7173
7606
|
sitemapMetadata: { sources: page.provenance.sitemapSources },
|
|
7174
7607
|
fetchState: page.state,
|
|
7175
|
-
fetchedAt: page.state === "discovered" || page.state === "robots-blocked" ? null :
|
|
7608
|
+
fetchedAt: page.state === "discovered" || page.state === "robots-blocked" ? null : sql6`coalesce(${siteCrawlPages.fetchedAt}, ${now})`,
|
|
7176
7609
|
httpStatus: page.statusCode,
|
|
7177
7610
|
contentType: page.contentType,
|
|
7178
7611
|
finalUrl: page.finalUrl,
|
|
@@ -7184,7 +7617,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7184
7617
|
indexabilityReasons: page.indexability.reasons,
|
|
7185
7618
|
auditState: pageAuditState(page),
|
|
7186
7619
|
auditScore: audit?.overallScore ?? null,
|
|
7187
|
-
auditFields: audit ?
|
|
7620
|
+
auditFields: audit ? crawlAuditFields(audit) : {},
|
|
7188
7621
|
inventoryEligible: isInventoryEligible(page),
|
|
7189
7622
|
depth: page.depth,
|
|
7190
7623
|
inboundUniqueEdges: page.metrics.inbound.uniqueEdges,
|
|
@@ -7206,7 +7639,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7206
7639
|
const persistEdge = (tx, edge, now) => {
|
|
7207
7640
|
observedEdges.set(edge.key, edge);
|
|
7208
7641
|
tx.insert(siteCrawlEdges).values({
|
|
7209
|
-
id:
|
|
7642
|
+
id: crypto17.randomUUID(),
|
|
7210
7643
|
projectId,
|
|
7211
7644
|
runId,
|
|
7212
7645
|
attemptId,
|
|
@@ -7252,20 +7685,20 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7252
7685
|
linkScoreRaw: metrics.linkScoreRaw,
|
|
7253
7686
|
linkScoreNormalized: metrics.linkScore,
|
|
7254
7687
|
updatedAt: now
|
|
7255
|
-
}).where(
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7688
|
+
}).where(and11(
|
|
7689
|
+
eq14(siteCrawlPages.projectId, projectId),
|
|
7690
|
+
eq14(siteCrawlPages.runId, runId),
|
|
7691
|
+
eq14(siteCrawlPages.attemptId, attemptId),
|
|
7692
|
+
eq14(siteCrawlPages.nodeKey, key)
|
|
7260
7693
|
)).run();
|
|
7261
7694
|
};
|
|
7262
7695
|
const persistEvent = async (event) => {
|
|
7263
7696
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7264
7697
|
db.transaction((tx) => {
|
|
7265
|
-
const receipt = tx.select({ checksum: siteCrawlEventReceipts.checksum }).from(siteCrawlEventReceipts).where(
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7698
|
+
const receipt = tx.select({ checksum: siteCrawlEventReceipts.checksum }).from(siteCrawlEventReceipts).where(and11(
|
|
7699
|
+
eq14(siteCrawlEventReceipts.attemptId, attemptId),
|
|
7700
|
+
eq14(siteCrawlEventReceipts.sequence, event.sequence),
|
|
7701
|
+
eq14(siteCrawlEventReceipts.batchId, event.batchId)
|
|
7269
7702
|
)).get();
|
|
7270
7703
|
if (receipt) {
|
|
7271
7704
|
if (receipt.checksum !== event.checksum) {
|
|
@@ -7274,7 +7707,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7274
7707
|
return;
|
|
7275
7708
|
}
|
|
7276
7709
|
tx.insert(siteCrawlEventReceipts).values({
|
|
7277
|
-
id:
|
|
7710
|
+
id: crypto17.randomUUID(),
|
|
7278
7711
|
projectId,
|
|
7279
7712
|
runId,
|
|
7280
7713
|
attemptId,
|
|
@@ -7294,22 +7727,22 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7294
7727
|
lastEventChecksum: event.checksum,
|
|
7295
7728
|
pagesDiscovered: progress.pagesDiscovered,
|
|
7296
7729
|
pagesFetched: progress.pagesFetched,
|
|
7297
|
-
pagesEligible: event.type === "summary" ? observedPages.size === 0 ? 0 : [...observedPages.values()].filter(isInventoryEligible).length :
|
|
7298
|
-
pagesErrored: event.type === "summary" ? observedErrorCount(observedPages.values()) :
|
|
7730
|
+
pagesEligible: event.type === "summary" ? observedPages.size === 0 ? 0 : [...observedPages.values()].filter(isInventoryEligible).length : sql6`${siteCrawlAttempts.pagesEligible}`,
|
|
7731
|
+
pagesErrored: event.type === "summary" ? observedErrorCount(observedPages.values()) : sql6`${siteCrawlAttempts.pagesErrored}`,
|
|
7299
7732
|
edgesDiscovered: progress.edgesObserved,
|
|
7300
7733
|
updatedAt: now
|
|
7301
|
-
}).where(
|
|
7734
|
+
}).where(eq14(siteCrawlAttempts.id, attemptId)).run();
|
|
7302
7735
|
} else {
|
|
7303
7736
|
tx.update(siteCrawlAttempts).set({
|
|
7304
7737
|
lastEventSequence: event.sequence,
|
|
7305
7738
|
lastEventChecksum: event.checksum,
|
|
7306
7739
|
updatedAt: now
|
|
7307
|
-
}).where(
|
|
7740
|
+
}).where(eq14(siteCrawlAttempts.id, attemptId)).run();
|
|
7308
7741
|
}
|
|
7309
7742
|
});
|
|
7310
7743
|
};
|
|
7311
7744
|
try {
|
|
7312
|
-
const project = db.select().from(projects).where(
|
|
7745
|
+
const project = db.select().from(projects).where(eq14(projects.id, projectId)).get();
|
|
7313
7746
|
if (!project) throw new Error(`Project not found: ${projectId}`);
|
|
7314
7747
|
const homepageUrl = toHomepageUrl(project.canonicalDomain);
|
|
7315
7748
|
const maxPages = clampSiteAuditLimit(opts.maxPages ?? opts.limit);
|
|
@@ -7332,6 +7765,12 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7332
7765
|
if (crawlSummary.complete && !hasAuditedPages) {
|
|
7333
7766
|
throw emptyCompleteCrawlError(crawlSummary.rootUrl, crawlSummary.finalRootUrl, crawlSummary.pagesObserved);
|
|
7334
7767
|
}
|
|
7768
|
+
const graphLayout = await prepareSiteCrawlGraphLayout(db, {
|
|
7769
|
+
projectId,
|
|
7770
|
+
runId,
|
|
7771
|
+
attemptId,
|
|
7772
|
+
rootUrl: crawlSummary.finalRootUrl ?? crawlSummary.rootUrl
|
|
7773
|
+
}, { signal: opts.signal, timeoutMs: opts.graphLayoutTimeoutMs });
|
|
7335
7774
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7336
7775
|
const factors = computeFactorAverages([...observedPages.values()]);
|
|
7337
7776
|
const errorCount = observedErrorCount(observedPages.values());
|
|
@@ -7339,12 +7778,39 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7339
7778
|
const deadLinksChecked = opts.checkDeadLinks ? deadLinkCheckedCount(observedEdges.values(), observedPages.values()) : 0;
|
|
7340
7779
|
const deadLinksFound = report.deadLinks.findings.length;
|
|
7341
7780
|
const legacyIssues = factors.map((factor) => toLegacyIssue(factor, crawlSummary.auditRollup.auditedPages)).filter((issue) => issue !== null);
|
|
7781
|
+
try {
|
|
7782
|
+
persistSiteCrawlGraphLayout(db, { projectId, runId, attemptId }, graphLayout, finishedAt);
|
|
7783
|
+
} catch (error) {
|
|
7784
|
+
log12.warn("graph-layout.persist-failed", {
|
|
7785
|
+
runId,
|
|
7786
|
+
projectId,
|
|
7787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7788
|
+
});
|
|
7789
|
+
try {
|
|
7790
|
+
persistSiteCrawlGraphLayout(db, { projectId, runId, attemptId }, {
|
|
7791
|
+
state: "unavailable",
|
|
7792
|
+
failureCode: "layout-error",
|
|
7793
|
+
totalNodes: graphLayout.totalNodes,
|
|
7794
|
+
totalEdges: graphLayout.totalEdges,
|
|
7795
|
+
nodeCount: 0,
|
|
7796
|
+
edgeCount: 0,
|
|
7797
|
+
nodes: [],
|
|
7798
|
+
edges: []
|
|
7799
|
+
}, finishedAt);
|
|
7800
|
+
} catch (markerError) {
|
|
7801
|
+
log12.warn("graph-layout.marker-persist-failed", {
|
|
7802
|
+
runId,
|
|
7803
|
+
projectId,
|
|
7804
|
+
error: markerError instanceof Error ? markerError.message : String(markerError)
|
|
7805
|
+
});
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7342
7808
|
const published = db.transaction((tx) => {
|
|
7343
|
-
const claim2 = tx.update(runs).set({ status: terminalStatus, finishedAt }).where(
|
|
7809
|
+
const claim2 = tx.update(runs).set({ status: terminalStatus, finishedAt }).where(and11(eq14(runs.id, runId), eq14(runs.status, "running"))).run();
|
|
7344
7810
|
if (claim2.changes === 0) return false;
|
|
7345
7811
|
if (hasAuditedPages) {
|
|
7346
7812
|
tx.insert(siteAuditSnapshots).values({
|
|
7347
|
-
id:
|
|
7813
|
+
id: crypto17.randomUUID(),
|
|
7348
7814
|
projectId,
|
|
7349
7815
|
runId,
|
|
7350
7816
|
sitemapUrl: opts.sitemapUrl ?? `${homepageUrl}/sitemap.xml`,
|
|
@@ -7362,7 +7828,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7362
7828
|
for (const page of observedPages.values()) {
|
|
7363
7829
|
if (!isLegacyScorecardPage(page)) continue;
|
|
7364
7830
|
tx.insert(siteAuditPages).values({
|
|
7365
|
-
id:
|
|
7831
|
+
id: crypto17.randomUUID(),
|
|
7366
7832
|
projectId,
|
|
7367
7833
|
runId,
|
|
7368
7834
|
url: page.finalUrl ?? page.requestedUrl,
|
|
@@ -7375,11 +7841,12 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7375
7841
|
}
|
|
7376
7842
|
}
|
|
7377
7843
|
tx.insert(siteCrawlSnapshots).values({
|
|
7378
|
-
id:
|
|
7844
|
+
id: crypto17.randomUUID(),
|
|
7379
7845
|
projectId,
|
|
7380
7846
|
runId,
|
|
7381
7847
|
attemptId,
|
|
7382
|
-
|
|
7848
|
+
requestedRootUrl: crawlSummary.rootUrl,
|
|
7849
|
+
rootUrl: crawlSummary.finalRootUrl ?? crawlSummary.rootUrl,
|
|
7383
7850
|
crawlSchemaVersion: crawlSummary.crawlSchemaVersion,
|
|
7384
7851
|
engineVersion: crawlSummary.engineVersion,
|
|
7385
7852
|
normalizationVersion: crawlSummary.urlNormalizationVersion,
|
|
@@ -7415,7 +7882,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7415
7882
|
if (opts.checkDeadLinks) {
|
|
7416
7883
|
for (const finding of report.deadLinks.findings) {
|
|
7417
7884
|
tx.insert(siteCrawlFindings).values({
|
|
7418
|
-
id:
|
|
7885
|
+
id: crypto17.randomUUID(),
|
|
7419
7886
|
projectId,
|
|
7420
7887
|
runId,
|
|
7421
7888
|
attemptId,
|
|
@@ -7441,11 +7908,20 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7441
7908
|
edgesDiscovered: crawlSummary.edgesObserved,
|
|
7442
7909
|
finishedAt,
|
|
7443
7910
|
updatedAt: finishedAt
|
|
7444
|
-
}).where(
|
|
7911
|
+
}).where(eq14(siteCrawlAttempts.id, attemptId)).run();
|
|
7445
7912
|
return true;
|
|
7446
7913
|
});
|
|
7447
7914
|
if (!published) {
|
|
7448
|
-
|
|
7915
|
+
try {
|
|
7916
|
+
deleteSiteCrawlGraphLayout(db, { projectId, runId, attemptId });
|
|
7917
|
+
} catch (cleanupError) {
|
|
7918
|
+
log12.warn("graph-layout.cleanup-failed", {
|
|
7919
|
+
runId,
|
|
7920
|
+
projectId,
|
|
7921
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
|
|
7922
|
+
});
|
|
7923
|
+
}
|
|
7924
|
+
const current = db.select({ status: runs.status }).from(runs).where(eq14(runs.id, runId)).get();
|
|
7449
7925
|
if (current?.status === "cancelled") throw new SiteAuditCancelledError(`Site audit cancelled before publication: ${runId}`);
|
|
7450
7926
|
throw new Error(`Site audit run was no longer running before publication: ${runId}`);
|
|
7451
7927
|
}
|
|
@@ -7460,12 +7936,12 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7460
7936
|
error: message,
|
|
7461
7937
|
finishedAt,
|
|
7462
7938
|
updatedAt: finishedAt
|
|
7463
|
-
}).where(
|
|
7939
|
+
}).where(eq14(siteCrawlAttempts.id, attemptId)).run();
|
|
7464
7940
|
tx.update(runs).set({
|
|
7465
7941
|
status: cancelled ? "cancelled" : "failed",
|
|
7466
7942
|
error: message,
|
|
7467
7943
|
finishedAt
|
|
7468
|
-
}).where(
|
|
7944
|
+
}).where(and11(eq14(runs.id, runId), eq14(runs.status, "running"))).run();
|
|
7469
7945
|
});
|
|
7470
7946
|
log12.error("failed", { runId, projectId, cancelled, error: message });
|
|
7471
7947
|
throw error;
|
|
@@ -7473,7 +7949,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
7473
7949
|
}
|
|
7474
7950
|
|
|
7475
7951
|
// src/commands/backfill.ts
|
|
7476
|
-
import { and as
|
|
7952
|
+
import { and as and12, eq as eq15, inArray as inArray4, isNull as isNull2, sql as sql7 } from "drizzle-orm";
|
|
7477
7953
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
7478
7954
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
7479
7955
|
const config = loadConfig();
|
|
@@ -7481,7 +7957,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
7481
7957
|
migrate(db);
|
|
7482
7958
|
const projectFilter = opts?.project?.trim();
|
|
7483
7959
|
const isDryRun = opts?.dryRun === true;
|
|
7484
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
7960
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq15(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
7485
7961
|
let examined = 0;
|
|
7486
7962
|
let updated = 0;
|
|
7487
7963
|
let wouldUpdate = 0;
|
|
@@ -7489,10 +7965,10 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
7489
7965
|
let reparsed = 0;
|
|
7490
7966
|
let providerErrors = 0;
|
|
7491
7967
|
if (scopedProjects.length > 0) {
|
|
7492
|
-
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
7493
|
-
|
|
7968
|
+
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and12(
|
|
7969
|
+
eq15(runs.kind, RunKinds["answer-visibility"]),
|
|
7494
7970
|
inArray4(runs.projectId, scopedProjects.map((project) => project.id))
|
|
7495
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
7971
|
+
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq15(runs.kind, RunKinds["answer-visibility"])).all();
|
|
7496
7972
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
7497
7973
|
for (const run of runRows) {
|
|
7498
7974
|
const existing = runIdsByProject.get(run.projectId);
|
|
@@ -7500,7 +7976,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
7500
7976
|
else runIdsByProject.set(run.projectId, [run.id]);
|
|
7501
7977
|
}
|
|
7502
7978
|
for (const project of scopedProjects) {
|
|
7503
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
7979
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq15(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
7504
7980
|
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
7505
7981
|
if (runIds.length === 0) continue;
|
|
7506
7982
|
const projectDomains = effectiveDomains({
|
|
@@ -7593,7 +8069,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
7593
8069
|
} else {
|
|
7594
8070
|
db.transaction((tx) => {
|
|
7595
8071
|
for (const update of pendingUpdates) {
|
|
7596
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
8072
|
+
tx.update(querySnapshots).set(update.patch).where(eq15(querySnapshots.id, update.id)).run();
|
|
7597
8073
|
}
|
|
7598
8074
|
});
|
|
7599
8075
|
updated += pendingUpdates.length;
|
|
@@ -7642,13 +8118,13 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
7642
8118
|
function backfillNormalizedPaths(db, opts) {
|
|
7643
8119
|
const baseConditions = [];
|
|
7644
8120
|
if (opts?.projectId) {
|
|
7645
|
-
baseConditions.push(
|
|
8121
|
+
baseConditions.push(eq15(gaTrafficSnapshots.projectId, opts.projectId));
|
|
7646
8122
|
}
|
|
7647
8123
|
const rows = db.select({
|
|
7648
8124
|
id: gaTrafficSnapshots.id,
|
|
7649
8125
|
landingPage: gaTrafficSnapshots.landingPage,
|
|
7650
8126
|
landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
|
|
7651
|
-
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ?
|
|
8127
|
+
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and12(...baseConditions) : void 0).all();
|
|
7652
8128
|
let updated = 0;
|
|
7653
8129
|
let unchanged = 0;
|
|
7654
8130
|
if (rows.length > 0) {
|
|
@@ -7663,7 +8139,7 @@ function backfillNormalizedPaths(db, opts) {
|
|
|
7663
8139
|
unchanged++;
|
|
7664
8140
|
continue;
|
|
7665
8141
|
}
|
|
7666
|
-
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(
|
|
8142
|
+
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq15(gaTrafficSnapshots.id, row.id)).run();
|
|
7667
8143
|
updated++;
|
|
7668
8144
|
}
|
|
7669
8145
|
});
|
|
@@ -7677,7 +8153,7 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
7677
8153
|
const projectFilter = opts?.project?.trim();
|
|
7678
8154
|
let projectId;
|
|
7679
8155
|
if (projectFilter) {
|
|
7680
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
8156
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq15(projects.name, projectFilter)).get();
|
|
7681
8157
|
if (!project) {
|
|
7682
8158
|
const result2 = {
|
|
7683
8159
|
project: projectFilter,
|
|
@@ -7714,13 +8190,13 @@ async function backfillNormalizedPathsCommand(opts) {
|
|
|
7714
8190
|
function backfillAiReferralPaths(db, opts) {
|
|
7715
8191
|
const baseConditions = [];
|
|
7716
8192
|
if (opts?.projectId) {
|
|
7717
|
-
baseConditions.push(
|
|
8193
|
+
baseConditions.push(eq15(gaAiReferrals.projectId, opts.projectId));
|
|
7718
8194
|
}
|
|
7719
8195
|
const rows = db.select({
|
|
7720
8196
|
id: gaAiReferrals.id,
|
|
7721
8197
|
landingPage: gaAiReferrals.landingPage,
|
|
7722
8198
|
landingPageNormalized: gaAiReferrals.landingPageNormalized
|
|
7723
|
-
}).from(gaAiReferrals).where(baseConditions.length > 0 ?
|
|
8199
|
+
}).from(gaAiReferrals).where(baseConditions.length > 0 ? and12(...baseConditions) : void 0).all();
|
|
7724
8200
|
let updated = 0;
|
|
7725
8201
|
let unchanged = 0;
|
|
7726
8202
|
if (rows.length > 0) {
|
|
@@ -7735,7 +8211,7 @@ function backfillAiReferralPaths(db, opts) {
|
|
|
7735
8211
|
unchanged++;
|
|
7736
8212
|
continue;
|
|
7737
8213
|
}
|
|
7738
|
-
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(
|
|
8214
|
+
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq15(gaAiReferrals.id, row.id)).run();
|
|
7739
8215
|
updated++;
|
|
7740
8216
|
}
|
|
7741
8217
|
});
|
|
@@ -7749,7 +8225,7 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
7749
8225
|
const projectFilter = opts?.project?.trim();
|
|
7750
8226
|
let projectId;
|
|
7751
8227
|
if (projectFilter) {
|
|
7752
|
-
const project = db.select({ id: projects.id }).from(projects).where(
|
|
8228
|
+
const project = db.select({ id: projects.id }).from(projects).where(eq15(projects.name, projectFilter)).get();
|
|
7753
8229
|
if (!project) {
|
|
7754
8230
|
const result2 = {
|
|
7755
8231
|
project: projectFilter,
|
|
@@ -7785,10 +8261,10 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
7785
8261
|
}
|
|
7786
8262
|
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
7787
8263
|
const isDryRun = opts?.dryRun === true;
|
|
7788
|
-
const project = db.select().from(projects).where(
|
|
8264
|
+
const project = db.select().from(projects).where(eq15(projects.id, projectId)).get();
|
|
7789
8265
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
7790
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
7791
|
-
const runRows = db.select({ id: runs.id }).from(runs).where(
|
|
8266
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq15(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
8267
|
+
const runRows = db.select({ id: runs.id }).from(runs).where(and12(eq15(runs.kind, RunKinds["answer-visibility"]), eq15(runs.projectId, projectId))).all();
|
|
7792
8268
|
const runIds = runRows.map((r) => r.id);
|
|
7793
8269
|
let examined = 0;
|
|
7794
8270
|
let updated = 0;
|
|
@@ -7862,7 +8338,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
|
7862
8338
|
} else {
|
|
7863
8339
|
db.transaction((tx) => {
|
|
7864
8340
|
for (const update of pendingUpdates) {
|
|
7865
|
-
tx.update(querySnapshots).set(update.patch).where(
|
|
8341
|
+
tx.update(querySnapshots).set(update.patch).where(eq15(querySnapshots.id, update.id)).run();
|
|
7866
8342
|
}
|
|
7867
8343
|
});
|
|
7868
8344
|
updated += pendingUpdates.length;
|
|
@@ -7877,7 +8353,7 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
7877
8353
|
migrate(db);
|
|
7878
8354
|
const projectFilter = opts?.project?.trim();
|
|
7879
8355
|
const isDryRun = opts?.dryRun === true;
|
|
7880
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
8356
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq15(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
7881
8357
|
let examined = 0;
|
|
7882
8358
|
let updated = 0;
|
|
7883
8359
|
let wouldUpdate = 0;
|
|
@@ -7937,7 +8413,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
7937
8413
|
return result;
|
|
7938
8414
|
}
|
|
7939
8415
|
async function backfillInsightsCommand(project, opts) {
|
|
7940
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
8416
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-WHC7B32V.js");
|
|
7941
8417
|
const config = loadConfig();
|
|
7942
8418
|
const db = createClient(config.database);
|
|
7943
8419
|
migrate(db);
|
|
@@ -8096,7 +8572,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
8096
8572
|
const config = loadConfig();
|
|
8097
8573
|
const db = createClient(config.database);
|
|
8098
8574
|
migrate(db);
|
|
8099
|
-
const project = db.select().from(projects).where(
|
|
8575
|
+
const project = db.select().from(projects).where(eq15(projects.name, opts.project)).get();
|
|
8100
8576
|
if (!project) {
|
|
8101
8577
|
throw new Error(`Project "${opts.project}" not found`);
|
|
8102
8578
|
}
|
|
@@ -8107,8 +8583,8 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
8107
8583
|
process.stderr.write(`Recovering orphan snapshot attribution for "${project.name}"${mode}...
|
|
8108
8584
|
`);
|
|
8109
8585
|
}
|
|
8110
|
-
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(
|
|
8111
|
-
|
|
8586
|
+
const events = db.select({ createdAt: auditLog.createdAt, action: auditLog.action, diff: auditLog.diff }).from(auditLog).where(and12(
|
|
8587
|
+
eq15(auditLog.projectId, project.id),
|
|
8112
8588
|
inArray4(auditLog.action, ["keywords.appended", "keywords.deleted", "queries.appended", "queries.deleted", "queries.replaced"])
|
|
8113
8589
|
)).orderBy(auditLog.createdAt).all();
|
|
8114
8590
|
const history = replayQueryAuditLog(events);
|
|
@@ -8116,10 +8592,10 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
8116
8592
|
runId: runs.id,
|
|
8117
8593
|
createdAt: runs.createdAt,
|
|
8118
8594
|
location: runs.location
|
|
8119
|
-
}).from(runs).innerJoin(querySnapshots,
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8595
|
+
}).from(runs).innerJoin(querySnapshots, eq15(querySnapshots.runId, runs.id)).where(and12(
|
|
8596
|
+
eq15(runs.projectId, project.id),
|
|
8597
|
+
isNull2(querySnapshots.queryId),
|
|
8598
|
+
isNull2(querySnapshots.queryText)
|
|
8123
8599
|
)).groupBy(runs.id).orderBy(runs.createdAt).all();
|
|
8124
8600
|
const result = {
|
|
8125
8601
|
project: project.name,
|
|
@@ -8139,10 +8615,10 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
8139
8615
|
provider: querySnapshots.provider,
|
|
8140
8616
|
createdAt: querySnapshots.createdAt,
|
|
8141
8617
|
answerText: querySnapshots.answerText
|
|
8142
|
-
}).from(querySnapshots).where(
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8618
|
+
}).from(querySnapshots).where(and12(
|
|
8619
|
+
eq15(querySnapshots.runId, run.runId),
|
|
8620
|
+
isNull2(querySnapshots.queryId),
|
|
8621
|
+
isNull2(querySnapshots.queryText)
|
|
8146
8622
|
)).orderBy(querySnapshots.provider, querySnapshots.createdAt).all();
|
|
8147
8623
|
const byProvider = /* @__PURE__ */ new Map();
|
|
8148
8624
|
for (const s of orphanSnaps) {
|
|
@@ -8206,7 +8682,7 @@ async function backfillSnapshotAttributionCommand(opts) {
|
|
|
8206
8682
|
if (!isDryRun && updates.length > 0) {
|
|
8207
8683
|
db.transaction((tx) => {
|
|
8208
8684
|
for (const u of updates) {
|
|
8209
|
-
tx.update(querySnapshots).set({ queryText: u.queryText }).where(
|
|
8685
|
+
tx.update(querySnapshots).set({ queryText: u.queryText }).where(eq15(querySnapshots.id, u.id)).run();
|
|
8210
8686
|
}
|
|
8211
8687
|
});
|
|
8212
8688
|
}
|
|
@@ -8280,7 +8756,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8280
8756
|
const projectFilter = opts?.project?.trim();
|
|
8281
8757
|
const isDryRun = opts?.dryRun === true;
|
|
8282
8758
|
const isJson = isMachineFormat(opts?.format);
|
|
8283
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(
|
|
8759
|
+
const scopedProjects = projectFilter ? db.select().from(projects).where(eq15(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
8284
8760
|
if (scopedProjects.length === 0) {
|
|
8285
8761
|
if (projectFilter && !isJson) {
|
|
8286
8762
|
process.stderr.write(`No project named "${projectFilter}".
|
|
@@ -8305,8 +8781,8 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8305
8781
|
dryRun: isDryRun,
|
|
8306
8782
|
byBot: {}
|
|
8307
8783
|
};
|
|
8308
|
-
const unknownCountRow = db.select({ n:
|
|
8309
|
-
|
|
8784
|
+
const unknownCountRow = db.select({ n: sql7`count(*)` }).from(rawEventSamples).where(and12(
|
|
8785
|
+
eq15(rawEventSamples.eventType, "unknown"),
|
|
8310
8786
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
8311
8787
|
)).get();
|
|
8312
8788
|
result.unknownBefore = Number(unknownCountRow?.n ?? 0);
|
|
@@ -8318,8 +8794,8 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8318
8794
|
userAgent: rawEventSamples.userAgent,
|
|
8319
8795
|
pathNormalized: rawEventSamples.pathNormalized,
|
|
8320
8796
|
status: rawEventSamples.status
|
|
8321
|
-
}).from(rawEventSamples).where(
|
|
8322
|
-
|
|
8797
|
+
}).from(rawEventSamples).where(and12(
|
|
8798
|
+
eq15(rawEventSamples.eventType, "unknown"),
|
|
8323
8799
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
8324
8800
|
)).all();
|
|
8325
8801
|
result.examined = unknownSamples.length;
|
|
@@ -8358,7 +8834,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8358
8834
|
result.reclassified++;
|
|
8359
8835
|
result.byBot[classified.botId] = (result.byBot[classified.botId] ?? 0) + 1;
|
|
8360
8836
|
if (isDryRun) continue;
|
|
8361
|
-
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(
|
|
8837
|
+
db.update(rawEventSamples).set({ eventType: userFetch ? TrafficEventKinds["ai-user-fetch"] : TrafficEventKinds.crawler }).where(eq15(rawEventSamples.id, snap.id)).run();
|
|
8362
8838
|
const tsHour = new Date(snap.ts);
|
|
8363
8839
|
tsHour.setUTCMinutes(0, 0, 0);
|
|
8364
8840
|
if (userFetch) {
|
|
@@ -8386,7 +8862,7 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8386
8862
|
aiUserFetchEventsHourly.status
|
|
8387
8863
|
],
|
|
8388
8864
|
set: {
|
|
8389
|
-
hits:
|
|
8865
|
+
hits: sql7`${aiUserFetchEventsHourly.hits} + 1`,
|
|
8390
8866
|
updatedAt: now
|
|
8391
8867
|
}
|
|
8392
8868
|
}).run();
|
|
@@ -8415,15 +8891,15 @@ async function backfillTrafficClassificationCommand(opts) {
|
|
|
8415
8891
|
crawlerEventsHourly.status
|
|
8416
8892
|
],
|
|
8417
8893
|
set: {
|
|
8418
|
-
hits:
|
|
8894
|
+
hits: sql7`${crawlerEventsHourly.hits} + 1`,
|
|
8419
8895
|
updatedAt: now
|
|
8420
8896
|
}
|
|
8421
8897
|
}).run();
|
|
8422
8898
|
}
|
|
8423
8899
|
}
|
|
8424
8900
|
if (!isDryRun) {
|
|
8425
|
-
const afterRow = db.select({ n:
|
|
8426
|
-
|
|
8901
|
+
const afterRow = db.select({ n: sql7`count(*)` }).from(rawEventSamples).where(and12(
|
|
8902
|
+
eq15(rawEventSamples.eventType, "unknown"),
|
|
8427
8903
|
inArray4(rawEventSamples.projectId, projectIds)
|
|
8428
8904
|
)).get();
|
|
8429
8905
|
result.unknownAfter = Number(afterRow?.n ?? 0);
|
|
@@ -8447,8 +8923,8 @@ Traffic classification ${result.dryRun ? "preview" : "recovery"} complete.`);
|
|
|
8447
8923
|
if (result.reclassified > 0) {
|
|
8448
8924
|
console.log(` By bot:`);
|
|
8449
8925
|
const sorted = Object.entries(result.byBot).sort(([, a], [, b]) => b - a);
|
|
8450
|
-
for (const [bot,
|
|
8451
|
-
console.log(` ${
|
|
8926
|
+
for (const [bot, count2] of sorted) {
|
|
8927
|
+
console.log(` ${count2.toString().padStart(5)} ${bot}`);
|
|
8452
8928
|
}
|
|
8453
8929
|
}
|
|
8454
8930
|
if (result.dryRun) {
|
|
@@ -8458,7 +8934,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
|
|
|
8458
8934
|
}
|
|
8459
8935
|
|
|
8460
8936
|
// src/commands/skills.ts
|
|
8461
|
-
import
|
|
8937
|
+
import crypto18 from "crypto";
|
|
8462
8938
|
import fs4 from "fs";
|
|
8463
8939
|
import os4 from "os";
|
|
8464
8940
|
import path5 from "path";
|
|
@@ -8513,7 +8989,7 @@ function walkRelative(dir, prefix = "") {
|
|
|
8513
8989
|
return out.sort();
|
|
8514
8990
|
}
|
|
8515
8991
|
function sha256File(filePath) {
|
|
8516
|
-
return
|
|
8992
|
+
return crypto18.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
|
|
8517
8993
|
}
|
|
8518
8994
|
function readSkillManifest(skillDir) {
|
|
8519
8995
|
try {
|
|
@@ -8855,9 +9331,9 @@ var ProviderRegistry = class {
|
|
|
8855
9331
|
};
|
|
8856
9332
|
|
|
8857
9333
|
// src/scheduler.ts
|
|
8858
|
-
import
|
|
9334
|
+
import crypto19 from "crypto";
|
|
8859
9335
|
import cron from "node-cron";
|
|
8860
|
-
import { and as
|
|
9336
|
+
import { and as and13, eq as eq16, inArray as inArray5, notExists, sql as sql8 } from "drizzle-orm";
|
|
8861
9337
|
var log13 = createLogger("Scheduler");
|
|
8862
9338
|
var DEFAULT_HEALTH_CRON = "0 */6 * * *";
|
|
8863
9339
|
function taskKey(projectId, kind) {
|
|
@@ -8889,16 +9365,16 @@ var Scheduler = class {
|
|
|
8889
9365
|
*/
|
|
8890
9366
|
ensureHealthSchedules() {
|
|
8891
9367
|
const projectsWithoutHealth = this.db.select({ id: projects.id }).from(projects).where(notExists(
|
|
8892
|
-
this.db.select({ one:
|
|
8893
|
-
|
|
8894
|
-
|
|
9368
|
+
this.db.select({ one: sql8`1` }).from(schedules).where(and13(
|
|
9369
|
+
eq16(schedules.projectId, projects.id),
|
|
9370
|
+
eq16(schedules.kind, SchedulableRunKinds.doctor)
|
|
8895
9371
|
))
|
|
8896
9372
|
)).all();
|
|
8897
9373
|
if (projectsWithoutHealth.length === 0) return;
|
|
8898
9374
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8899
9375
|
for (const project of projectsWithoutHealth) {
|
|
8900
9376
|
this.db.insert(schedules).values({
|
|
8901
|
-
id:
|
|
9377
|
+
id: crypto19.randomUUID(),
|
|
8902
9378
|
projectId: project.id,
|
|
8903
9379
|
kind: SchedulableRunKinds.doctor,
|
|
8904
9380
|
cronExpr: DEFAULT_HEALTH_CRON,
|
|
@@ -8942,7 +9418,7 @@ var Scheduler = class {
|
|
|
8942
9418
|
start() {
|
|
8943
9419
|
this.ensureHealthSchedules();
|
|
8944
9420
|
this.ensureQueryBaskets();
|
|
8945
|
-
const allSchedules = this.db.select().from(schedules).where(
|
|
9421
|
+
const allSchedules = this.db.select().from(schedules).where(eq16(schedules.enabled, true)).all();
|
|
8946
9422
|
for (const schedule of allSchedules) {
|
|
8947
9423
|
const missedRunAt = schedule.nextRunAt;
|
|
8948
9424
|
this.registerCronTask(schedule);
|
|
@@ -8972,7 +9448,7 @@ var Scheduler = class {
|
|
|
8972
9448
|
this.stopTask(key, existing, "Stopped");
|
|
8973
9449
|
this.tasks.delete(key);
|
|
8974
9450
|
}
|
|
8975
|
-
const schedule = this.db.select().from(schedules).where(
|
|
9451
|
+
const schedule = this.db.select().from(schedules).where(and13(eq16(schedules.projectId, projectId), eq16(schedules.kind, kind))).get();
|
|
8976
9452
|
if (schedule && schedule.enabled) {
|
|
8977
9453
|
this.registerCronTask(schedule);
|
|
8978
9454
|
}
|
|
@@ -9013,21 +9489,21 @@ var Scheduler = class {
|
|
|
9013
9489
|
this.db.update(schedules).set({
|
|
9014
9490
|
nextRunAt: nextRunFromCron(cronExpr, timezone),
|
|
9015
9491
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9016
|
-
}).where(
|
|
9492
|
+
}).where(eq16(schedules.id, scheduleId)).run();
|
|
9017
9493
|
const label = schedule.preset ?? cronExpr;
|
|
9018
9494
|
log13.info("cron.registered", { projectId, kind, schedule: label, timezone });
|
|
9019
9495
|
}
|
|
9020
9496
|
triggerRun(scheduleId, projectId, kind) {
|
|
9021
9497
|
try {
|
|
9022
9498
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9023
|
-
const currentSchedule = this.db.select().from(schedules).where(
|
|
9499
|
+
const currentSchedule = this.db.select().from(schedules).where(eq16(schedules.id, scheduleId)).get();
|
|
9024
9500
|
if (!currentSchedule || !currentSchedule.enabled) {
|
|
9025
9501
|
log13.warn("schedule.stale", { scheduleId, projectId, kind, msg: "schedule no longer exists or is disabled" });
|
|
9026
9502
|
this.remove(projectId, kind);
|
|
9027
9503
|
return;
|
|
9028
9504
|
}
|
|
9029
9505
|
const nextRunAt = nextRunFromCron(currentSchedule.cronExpr, currentSchedule.timezone);
|
|
9030
|
-
const project = this.db.select().from(projects).where(
|
|
9506
|
+
const project = this.db.select().from(projects).where(eq16(projects.id, projectId)).get();
|
|
9031
9507
|
if (!project) {
|
|
9032
9508
|
log13.error("project.not-found", { projectId, kind, msg: "skipping scheduled run" });
|
|
9033
9509
|
this.remove(projectId, kind);
|
|
@@ -9047,7 +9523,7 @@ var Scheduler = class {
|
|
|
9047
9523
|
lastRunAt: now,
|
|
9048
9524
|
nextRunAt,
|
|
9049
9525
|
updatedAt: now
|
|
9050
|
-
}).where(
|
|
9526
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9051
9527
|
log13.info("traffic-sync.triggered", { projectName: project.name, sourceId });
|
|
9052
9528
|
this.callbacks.onTrafficSyncRequested(project.name, sourceId);
|
|
9053
9529
|
return;
|
|
@@ -9057,7 +9533,7 @@ var Scheduler = class {
|
|
|
9057
9533
|
log13.warn("gbp-sync.no-callback", { scheduleId, projectId, msg: "host did not register onGbpSyncRequested" });
|
|
9058
9534
|
return;
|
|
9059
9535
|
}
|
|
9060
|
-
const runId2 =
|
|
9536
|
+
const runId2 = crypto19.randomUUID();
|
|
9061
9537
|
this.db.insert(runs).values({
|
|
9062
9538
|
id: runId2,
|
|
9063
9539
|
projectId,
|
|
@@ -9070,7 +9546,7 @@ var Scheduler = class {
|
|
|
9070
9546
|
lastRunAt: now,
|
|
9071
9547
|
nextRunAt,
|
|
9072
9548
|
updatedAt: now
|
|
9073
|
-
}).where(
|
|
9549
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9074
9550
|
log13.info("gbp-sync.triggered", { runId: runId2, projectName: project.name });
|
|
9075
9551
|
this.callbacks.onGbpSyncRequested(runId2, projectId);
|
|
9076
9552
|
return;
|
|
@@ -9080,17 +9556,17 @@ var Scheduler = class {
|
|
|
9080
9556
|
log13.warn("ads-sync.no-callback", { scheduleId, projectId, msg: "host did not register onAdsSyncRequested" });
|
|
9081
9557
|
return;
|
|
9082
9558
|
}
|
|
9083
|
-
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(
|
|
9084
|
-
|
|
9085
|
-
|
|
9559
|
+
const activeAdsRun = this.db.select({ id: runs.id }).from(runs).where(and13(
|
|
9560
|
+
eq16(runs.projectId, projectId),
|
|
9561
|
+
eq16(runs.kind, RunKinds["ads-sync"]),
|
|
9086
9562
|
inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
9087
9563
|
)).get();
|
|
9088
9564
|
if (activeAdsRun) {
|
|
9089
9565
|
log13.info("ads-sync.skipped-active", { projectName: project.name, activeRunId: activeAdsRun.id });
|
|
9090
|
-
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(
|
|
9566
|
+
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9091
9567
|
return;
|
|
9092
9568
|
}
|
|
9093
|
-
const runId2 =
|
|
9569
|
+
const runId2 = crypto19.randomUUID();
|
|
9094
9570
|
this.db.insert(runs).values({
|
|
9095
9571
|
id: runId2,
|
|
9096
9572
|
projectId,
|
|
@@ -9103,7 +9579,7 @@ var Scheduler = class {
|
|
|
9103
9579
|
lastRunAt: now,
|
|
9104
9580
|
nextRunAt,
|
|
9105
9581
|
updatedAt: now
|
|
9106
|
-
}).where(
|
|
9582
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9107
9583
|
log13.info("ads-sync.triggered", { runId: runId2, projectName: project.name });
|
|
9108
9584
|
this.callbacks.onAdsSyncRequested(runId2, projectId);
|
|
9109
9585
|
return;
|
|
@@ -9117,7 +9593,7 @@ var Scheduler = class {
|
|
|
9117
9593
|
lastRunAt: now,
|
|
9118
9594
|
nextRunAt,
|
|
9119
9595
|
updatedAt: now
|
|
9120
|
-
}).where(
|
|
9596
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9121
9597
|
log13.info("data-refresh.triggered", { projectName: project.name });
|
|
9122
9598
|
this.callbacks.onDataRefreshRequested(project.name);
|
|
9123
9599
|
return;
|
|
@@ -9131,7 +9607,7 @@ var Scheduler = class {
|
|
|
9131
9607
|
lastRunAt: now,
|
|
9132
9608
|
nextRunAt,
|
|
9133
9609
|
updatedAt: now
|
|
9134
|
-
}).where(
|
|
9610
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9135
9611
|
log13.info("doctor.triggered", { projectName: project.name });
|
|
9136
9612
|
this.callbacks.onDoctorRequested(project.name);
|
|
9137
9613
|
return;
|
|
@@ -9145,7 +9621,7 @@ var Scheduler = class {
|
|
|
9145
9621
|
lastRunAt: now,
|
|
9146
9622
|
nextRunAt,
|
|
9147
9623
|
updatedAt: now
|
|
9148
|
-
}).where(
|
|
9624
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9149
9625
|
log13.info("backlinks-sync.triggered", { projectName: project.name });
|
|
9150
9626
|
this.callbacks.onBacklinksSyncRequested(project.name);
|
|
9151
9627
|
return;
|
|
@@ -9155,17 +9631,17 @@ var Scheduler = class {
|
|
|
9155
9631
|
log13.warn("site-audit.no-callback", { scheduleId, projectId, msg: "host did not register onSiteAuditRequested" });
|
|
9156
9632
|
return;
|
|
9157
9633
|
}
|
|
9158
|
-
const active = this.db.select({ id: runs.id }).from(runs).where(
|
|
9159
|
-
|
|
9160
|
-
|
|
9634
|
+
const active = this.db.select({ id: runs.id }).from(runs).where(and13(
|
|
9635
|
+
eq16(runs.projectId, projectId),
|
|
9636
|
+
eq16(runs.kind, RunKinds["site-audit"]),
|
|
9161
9637
|
inArray5(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
9162
9638
|
)).get();
|
|
9163
9639
|
if (active) {
|
|
9164
9640
|
log13.info("site-audit.skipped-active", { projectName: project.name, activeRunId: active.id });
|
|
9165
|
-
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(
|
|
9641
|
+
this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9166
9642
|
return;
|
|
9167
9643
|
}
|
|
9168
|
-
const runId2 =
|
|
9644
|
+
const runId2 = crypto19.randomUUID();
|
|
9169
9645
|
const effectiveRequest = normalizeSiteAuditRunRequest({});
|
|
9170
9646
|
this.db.transaction((tx) => {
|
|
9171
9647
|
tx.insert(runs).values({
|
|
@@ -9188,7 +9664,7 @@ var Scheduler = class {
|
|
|
9188
9664
|
lastRunAt: now,
|
|
9189
9665
|
nextRunAt,
|
|
9190
9666
|
updatedAt: now
|
|
9191
|
-
}).where(
|
|
9667
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9192
9668
|
log13.info("site-audit.triggered", { runId: runId2, projectName: project.name });
|
|
9193
9669
|
this.callbacks.onSiteAuditRequested(runId2, projectId);
|
|
9194
9670
|
return;
|
|
@@ -9221,7 +9697,7 @@ var Scheduler = class {
|
|
|
9221
9697
|
this.db.update(schedules).set({
|
|
9222
9698
|
nextRunAt,
|
|
9223
9699
|
updatedAt: now
|
|
9224
|
-
}).where(
|
|
9700
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9225
9701
|
return;
|
|
9226
9702
|
}
|
|
9227
9703
|
const runId = queueResult.runId;
|
|
@@ -9229,7 +9705,7 @@ var Scheduler = class {
|
|
|
9229
9705
|
lastRunAt: now,
|
|
9230
9706
|
nextRunAt,
|
|
9231
9707
|
updatedAt: now
|
|
9232
|
-
}).where(
|
|
9708
|
+
}).where(eq16(schedules.id, currentSchedule.id)).run();
|
|
9233
9709
|
log13.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
9234
9710
|
this.callbacks.onRunCreated(runId, projectId, providers, resolvedLocation);
|
|
9235
9711
|
} catch (err) {
|
|
@@ -9262,8 +9738,8 @@ async function refreshAllIntegrations(client, projectName) {
|
|
|
9262
9738
|
}
|
|
9263
9739
|
|
|
9264
9740
|
// src/notifier.ts
|
|
9265
|
-
import { eq as
|
|
9266
|
-
import
|
|
9741
|
+
import { eq as eq17, desc as desc7, and as and14, inArray as inArray6, or as or2 } from "drizzle-orm";
|
|
9742
|
+
import crypto20 from "crypto";
|
|
9267
9743
|
var log15 = createLogger("Notifier");
|
|
9268
9744
|
var Notifier = class {
|
|
9269
9745
|
db;
|
|
@@ -9275,18 +9751,18 @@ var Notifier = class {
|
|
|
9275
9751
|
/** Called after a run completes (success, partial, or failed). */
|
|
9276
9752
|
async onRunCompleted(runId, projectId) {
|
|
9277
9753
|
log15.info("run.completed", { runId, projectId });
|
|
9278
|
-
const notifs = this.db.select().from(notifications).where(
|
|
9754
|
+
const notifs = this.db.select().from(notifications).where(eq17(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
9279
9755
|
if (notifs.length === 0) {
|
|
9280
9756
|
log15.info("notifications.none-enabled", { projectId });
|
|
9281
9757
|
return;
|
|
9282
9758
|
}
|
|
9283
9759
|
log15.info("notifications.found", { projectId, count: notifs.length });
|
|
9284
|
-
const run = this.db.select().from(runs).where(
|
|
9760
|
+
const run = this.db.select().from(runs).where(eq17(runs.id, runId)).get();
|
|
9285
9761
|
if (!run) {
|
|
9286
9762
|
log15.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
9287
9763
|
return;
|
|
9288
9764
|
}
|
|
9289
|
-
const project = this.db.select().from(projects).where(
|
|
9765
|
+
const project = this.db.select().from(projects).where(eq17(projects.id, projectId)).get();
|
|
9290
9766
|
if (!project) {
|
|
9291
9767
|
log15.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
9292
9768
|
return;
|
|
@@ -9339,7 +9815,7 @@ var Notifier = class {
|
|
|
9339
9815
|
* inferred from the absence of a webhook.
|
|
9340
9816
|
*/
|
|
9341
9817
|
async onHealthChecked(projectId, report) {
|
|
9342
|
-
const project = this.db.select().from(projects).where(
|
|
9818
|
+
const project = this.db.select().from(projects).where(eq17(projects.id, projectId)).get();
|
|
9343
9819
|
if (!project) {
|
|
9344
9820
|
log15.error("project.not-found", { projectId, msg: "skipping health notification" });
|
|
9345
9821
|
return null;
|
|
@@ -9370,7 +9846,7 @@ var Notifier = class {
|
|
|
9370
9846
|
const status = noSignal ? "warn" : worst ? worst.status : "ok";
|
|
9371
9847
|
const code = noSignal ? "health.no-signal" : worst?.code ?? "health.ok";
|
|
9372
9848
|
const summary = noSignal ? `No health check produced a result (${report.checks.length} skipped) \u2014 health is unknown, not confirmed.` : worst?.summary ?? `All ${graded.length} health check(s) passing.`;
|
|
9373
|
-
const previous = this.db.select().from(doctorHealthState).where(
|
|
9849
|
+
const previous = this.db.select().from(doctorHealthState).where(eq17(doctorHealthState.projectId, projectId)).get();
|
|
9374
9850
|
const previousStatus = previous?.status ?? null;
|
|
9375
9851
|
let event = null;
|
|
9376
9852
|
if (previous === void 0) {
|
|
@@ -9385,13 +9861,13 @@ var Notifier = class {
|
|
|
9385
9861
|
if (previous === void 0) {
|
|
9386
9862
|
this.db.insert(doctorHealthState).values({ ...observation, notifiedAt: null }).run();
|
|
9387
9863
|
} else {
|
|
9388
|
-
this.db.update(doctorHealthState).set(observation).where(
|
|
9864
|
+
this.db.update(doctorHealthState).set(observation).where(eq17(doctorHealthState.projectId, projectId)).run();
|
|
9389
9865
|
}
|
|
9390
9866
|
if (!event) {
|
|
9391
9867
|
log15.info("health.unchanged", { projectId, status, code });
|
|
9392
9868
|
return null;
|
|
9393
9869
|
}
|
|
9394
|
-
const notifs = this.db.select().from(notifications).where(
|
|
9870
|
+
const notifs = this.db.select().from(notifications).where(eq17(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
9395
9871
|
const payload = {
|
|
9396
9872
|
source: "canonry",
|
|
9397
9873
|
event,
|
|
@@ -9415,7 +9891,7 @@ var Notifier = class {
|
|
|
9415
9891
|
delivered += 1;
|
|
9416
9892
|
}
|
|
9417
9893
|
if (delivered > 0) {
|
|
9418
|
-
this.db.update(doctorHealthState).set({ notifiedAt: now }).where(
|
|
9894
|
+
this.db.update(doctorHealthState).set({ notifiedAt: now }).where(eq17(doctorHealthState.projectId, projectId)).run();
|
|
9419
9895
|
}
|
|
9420
9896
|
log15.info("health.notified", { projectId, event, status, code, subscribers: notifs.length, delivered });
|
|
9421
9897
|
return event;
|
|
@@ -9428,11 +9904,11 @@ var Notifier = class {
|
|
|
9428
9904
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
9429
9905
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
9430
9906
|
if (insightEvents.length === 0) return;
|
|
9431
|
-
const notifs = this.db.select().from(notifications).where(
|
|
9907
|
+
const notifs = this.db.select().from(notifications).where(eq17(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
9432
9908
|
if (notifs.length === 0) return;
|
|
9433
|
-
const run = this.db.select().from(runs).where(
|
|
9909
|
+
const run = this.db.select().from(runs).where(eq17(runs.id, runId)).get();
|
|
9434
9910
|
if (!run) return;
|
|
9435
|
-
const project = this.db.select().from(projects).where(
|
|
9911
|
+
const project = this.db.select().from(projects).where(eq17(projects.id, projectId)).get();
|
|
9436
9912
|
if (!project) return;
|
|
9437
9913
|
for (const notif of notifs) {
|
|
9438
9914
|
const config = notif.config;
|
|
@@ -9462,7 +9938,7 @@ var Notifier = class {
|
|
|
9462
9938
|
}
|
|
9463
9939
|
}
|
|
9464
9940
|
computeTransitions(runId, projectId) {
|
|
9465
|
-
const thisRun = this.db.select().from(runs).where(
|
|
9941
|
+
const thisRun = this.db.select().from(runs).where(eq17(runs.id, runId)).get();
|
|
9466
9942
|
if (!thisRun) return [];
|
|
9467
9943
|
const completeness = measurementRunCompleteness(this.db, runId);
|
|
9468
9944
|
if (completeness.planned && !completeness.complete) {
|
|
@@ -9473,10 +9949,10 @@ var Notifier = class {
|
|
|
9473
9949
|
});
|
|
9474
9950
|
return [];
|
|
9475
9951
|
}
|
|
9476
|
-
const groupSiblings = this.db.select().from(runs).where(
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9952
|
+
const groupSiblings = this.db.select().from(runs).where(and14(
|
|
9953
|
+
eq17(runs.projectId, projectId),
|
|
9954
|
+
eq17(runs.kind, thisRun.kind),
|
|
9955
|
+
eq17(runs.createdAt, thisRun.createdAt)
|
|
9480
9956
|
)).all();
|
|
9481
9957
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
9482
9958
|
if (stillPending) return [];
|
|
@@ -9492,19 +9968,19 @@ var Notifier = class {
|
|
|
9492
9968
|
return candidate.id > best.id ? candidate : best;
|
|
9493
9969
|
});
|
|
9494
9970
|
if (winner.id !== runId) return [];
|
|
9495
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
9971
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq17(projects.id, projectId)).get();
|
|
9496
9972
|
const locationCount = Math.max(
|
|
9497
9973
|
1,
|
|
9498
9974
|
(projectLocations?.locations ?? []).length
|
|
9499
9975
|
);
|
|
9500
9976
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
9501
9977
|
const recentRuns = this.db.select().from(runs).where(
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9978
|
+
and14(
|
|
9979
|
+
eq17(runs.projectId, projectId),
|
|
9980
|
+
eq17(runs.kind, thisRun.kind),
|
|
9981
|
+
or2(eq17(runs.status, "completed"), eq17(runs.status, "partial"))
|
|
9506
9982
|
)
|
|
9507
|
-
).orderBy(
|
|
9983
|
+
).orderBy(desc7(runs.createdAt), desc7(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
9508
9984
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
9509
9985
|
const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
|
|
9510
9986
|
if (currentGroupIdx < 0) return [];
|
|
@@ -9519,7 +9995,7 @@ var Notifier = class {
|
|
|
9519
9995
|
provider: querySnapshots.provider,
|
|
9520
9996
|
location: querySnapshots.location,
|
|
9521
9997
|
citationState: querySnapshots.citationState
|
|
9522
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
9998
|
+
}).from(querySnapshots).leftJoin(queries, eq17(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
|
|
9523
9999
|
const previousSnapshots = this.db.select({
|
|
9524
10000
|
queryId: querySnapshots.queryId,
|
|
9525
10001
|
provider: querySnapshots.provider,
|
|
@@ -9589,7 +10065,7 @@ var Notifier = class {
|
|
|
9589
10065
|
}
|
|
9590
10066
|
logDelivery(projectId, notificationId, event, status, error) {
|
|
9591
10067
|
this.db.insert(auditLog).values({
|
|
9592
|
-
id:
|
|
10068
|
+
id: crypto20.randomUUID(),
|
|
9593
10069
|
projectId,
|
|
9594
10070
|
actor: "scheduler",
|
|
9595
10071
|
action: `notification.${status}`,
|
|
@@ -9602,7 +10078,7 @@ var Notifier = class {
|
|
|
9602
10078
|
};
|
|
9603
10079
|
|
|
9604
10080
|
// src/run-coordinator.ts
|
|
9605
|
-
import { eq as
|
|
10081
|
+
import { eq as eq18 } from "drizzle-orm";
|
|
9606
10082
|
var log16 = createLogger("RunCoordinator");
|
|
9607
10083
|
var RunCoordinator = class {
|
|
9608
10084
|
constructor(db, notifier, intelligenceService, onInsightsGenerated, onAeroEvent) {
|
|
@@ -9618,7 +10094,7 @@ var RunCoordinator = class {
|
|
|
9618
10094
|
onInsightsGenerated;
|
|
9619
10095
|
onAeroEvent;
|
|
9620
10096
|
async onRunCompleted(runId, projectId) {
|
|
9621
|
-
const runRow = this.db.select().from(runs).where(
|
|
10097
|
+
const runRow = this.db.select().from(runs).where(eq18(runs.id, runId)).get();
|
|
9622
10098
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
9623
10099
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
9624
10100
|
log16.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
@@ -9709,7 +10185,7 @@ var RunCoordinator = class {
|
|
|
9709
10185
|
* so the Aero queue is never starved of a follow-up.
|
|
9710
10186
|
*/
|
|
9711
10187
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
9712
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
10188
|
+
const session = this.db.select().from(discoverySessions).where(eq18(discoverySessions.runId, runId)).get();
|
|
9713
10189
|
const competitorMap = session ? session.competitorMap : [];
|
|
9714
10190
|
return {
|
|
9715
10191
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -9751,8 +10227,8 @@ function analysisResultFromInsights(insights2) {
|
|
|
9751
10227
|
}
|
|
9752
10228
|
|
|
9753
10229
|
// src/agent/session-registry.ts
|
|
9754
|
-
import
|
|
9755
|
-
import { eq as
|
|
10230
|
+
import crypto24 from "crypto";
|
|
10231
|
+
import { eq as eq20 } from "drizzle-orm";
|
|
9756
10232
|
|
|
9757
10233
|
// src/agent/session.ts
|
|
9758
10234
|
import fs7 from "fs";
|
|
@@ -10495,7 +10971,7 @@ function buildAeroStateTools(ctx, opts = {}) {
|
|
|
10495
10971
|
}
|
|
10496
10972
|
|
|
10497
10973
|
// src/agent/llm-usage.ts
|
|
10498
|
-
import
|
|
10974
|
+
import crypto21 from "crypto";
|
|
10499
10975
|
var AeroLlmUsageFeatures = {
|
|
10500
10976
|
turn: "aero.turn"
|
|
10501
10977
|
};
|
|
@@ -10514,7 +10990,7 @@ function recordLlmUsageEvent(args) {
|
|
|
10514
10990
|
const usage = args.message.usage;
|
|
10515
10991
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10516
10992
|
args.db.insert(llmUsageEvents).values({
|
|
10517
|
-
id:
|
|
10993
|
+
id: crypto21.randomUUID(),
|
|
10518
10994
|
projectId: args.projectId,
|
|
10519
10995
|
runId: args.runId,
|
|
10520
10996
|
agentSessionId: args.agentSessionId,
|
|
@@ -10590,7 +11066,7 @@ function splitAeroAnthropicSystemCachePayload(payload, model) {
|
|
|
10590
11066
|
}
|
|
10591
11067
|
|
|
10592
11068
|
// src/agent/tool-usage.ts
|
|
10593
|
-
import
|
|
11069
|
+
import crypto22 from "crypto";
|
|
10594
11070
|
var AeroToolEventStatuses = {
|
|
10595
11071
|
success: "success",
|
|
10596
11072
|
error: "error"
|
|
@@ -10653,7 +11129,7 @@ function createAeroToolUsageHooks(args) {
|
|
|
10653
11129
|
function recordAgentToolEvent(args) {
|
|
10654
11130
|
try {
|
|
10655
11131
|
args.db.insert(agentToolEvents).values({
|
|
10656
|
-
id:
|
|
11132
|
+
id: crypto22.randomUUID(),
|
|
10657
11133
|
projectId: args.projectId,
|
|
10658
11134
|
agentSessionId: args.agentSessionId,
|
|
10659
11135
|
toolCallId: args.toolCallId,
|
|
@@ -10880,8 +11356,8 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
10880
11356
|
}
|
|
10881
11357
|
|
|
10882
11358
|
// src/agent/memory-store.ts
|
|
10883
|
-
import
|
|
10884
|
-
import { and as
|
|
11359
|
+
import crypto23 from "crypto";
|
|
11360
|
+
import { and as and15, desc as desc8, eq as eq19, like, sql as sql9 } from "drizzle-orm";
|
|
10885
11361
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
10886
11362
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
10887
11363
|
function rowToDto(row) {
|
|
@@ -10895,7 +11371,7 @@ function rowToDto(row) {
|
|
|
10895
11371
|
};
|
|
10896
11372
|
}
|
|
10897
11373
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
10898
|
-
const query = db.select().from(agentMemory).where(
|
|
11374
|
+
const query = db.select().from(agentMemory).where(eq19(agentMemory.projectId, projectId)).orderBy(desc8(agentMemory.updatedAt));
|
|
10899
11375
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
10900
11376
|
return rows.map(rowToDto);
|
|
10901
11377
|
}
|
|
@@ -10909,7 +11385,7 @@ function upsertMemoryEntry(db, args) {
|
|
|
10909
11385
|
throw new Error(`memory key prefix "${COMPACTION_KEY_PREFIX}" is reserved for compaction notes`);
|
|
10910
11386
|
}
|
|
10911
11387
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10912
|
-
const id =
|
|
11388
|
+
const id = crypto23.randomUUID();
|
|
10913
11389
|
db.insert(agentMemory).values({
|
|
10914
11390
|
id,
|
|
10915
11391
|
projectId: args.projectId,
|
|
@@ -10926,12 +11402,12 @@ function upsertMemoryEntry(db, args) {
|
|
|
10926
11402
|
updatedAt: now
|
|
10927
11403
|
}
|
|
10928
11404
|
}).run();
|
|
10929
|
-
const row = db.select().from(agentMemory).where(
|
|
11405
|
+
const row = db.select().from(agentMemory).where(and15(eq19(agentMemory.projectId, args.projectId), eq19(agentMemory.key, args.key))).get();
|
|
10930
11406
|
if (!row) throw new Error("memory upsert produced no row");
|
|
10931
11407
|
return rowToDto(row);
|
|
10932
11408
|
}
|
|
10933
11409
|
function deleteMemoryEntry(db, projectId, key) {
|
|
10934
|
-
const result = db.delete(agentMemory).where(
|
|
11410
|
+
const result = db.delete(agentMemory).where(and15(eq19(agentMemory.projectId, projectId), eq19(agentMemory.key, key))).run();
|
|
10935
11411
|
const changes = result.changes ?? 0;
|
|
10936
11412
|
return changes > 0;
|
|
10937
11413
|
}
|
|
@@ -10946,7 +11422,7 @@ function writeCompactionNote(db, args) {
|
|
|
10946
11422
|
}
|
|
10947
11423
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10948
11424
|
const key = `${COMPACTION_KEY_PREFIX}${args.sessionId}:${now}`;
|
|
10949
|
-
const id =
|
|
11425
|
+
const id = crypto23.randomUUID();
|
|
10950
11426
|
let inserted;
|
|
10951
11427
|
db.transaction((tx) => {
|
|
10952
11428
|
tx.insert(agentMemory).values({
|
|
@@ -10960,16 +11436,16 @@ function writeCompactionNote(db, args) {
|
|
|
10960
11436
|
}).run();
|
|
10961
11437
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
10962
11438
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
10963
|
-
|
|
10964
|
-
|
|
11439
|
+
and15(
|
|
11440
|
+
eq19(agentMemory.projectId, args.projectId),
|
|
10965
11441
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
10966
11442
|
)
|
|
10967
|
-
).orderBy(
|
|
11443
|
+
).orderBy(desc8(agentMemory.updatedAt)).all();
|
|
10968
11444
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
10969
11445
|
if (stale.length > 0) {
|
|
10970
|
-
tx.delete(agentMemory).where(
|
|
11446
|
+
tx.delete(agentMemory).where(sql9`${agentMemory.id} IN (${sql9.join(stale.map((s) => sql9`${s}`), sql9`, `)})`).run();
|
|
10971
11447
|
}
|
|
10972
|
-
const row = tx.select().from(agentMemory).where(
|
|
11448
|
+
const row = tx.select().from(agentMemory).where(and15(eq19(agentMemory.projectId, args.projectId), eq19(agentMemory.key, key))).get();
|
|
10973
11449
|
if (row) inserted = rowToDto(row);
|
|
10974
11450
|
});
|
|
10975
11451
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -11196,7 +11672,7 @@ var SessionRegistry = class {
|
|
|
11196
11672
|
modelProvider: effectiveProvider,
|
|
11197
11673
|
modelId: effectiveModelId,
|
|
11198
11674
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11199
|
-
}).where(
|
|
11675
|
+
}).where(eq20(agentSessions.projectId, projectId)).run();
|
|
11200
11676
|
}
|
|
11201
11677
|
const agent2 = createAeroSession({
|
|
11202
11678
|
projectName,
|
|
@@ -11225,7 +11701,7 @@ var SessionRegistry = class {
|
|
|
11225
11701
|
}
|
|
11226
11702
|
const { provider, modelId } = resolveSessionProviderAndModel(this.opts.config, preferences);
|
|
11227
11703
|
const systemPrompt = loadAeroSystemPrompt();
|
|
11228
|
-
const sessionId =
|
|
11704
|
+
const sessionId = crypto24.randomUUID();
|
|
11229
11705
|
const agent = createAeroSession({
|
|
11230
11706
|
projectName,
|
|
11231
11707
|
client: this.opts.client,
|
|
@@ -11430,7 +11906,7 @@ ${lines.join("\n")}
|
|
|
11430
11906
|
modelProvider: nextProvider,
|
|
11431
11907
|
modelId: nextModelId,
|
|
11432
11908
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11433
|
-
}).where(
|
|
11909
|
+
}).where(eq20(agentSessions.projectId, projectId)).run();
|
|
11434
11910
|
}
|
|
11435
11911
|
/** Persist a session's transcript back to the DB. Call after any run settles. */
|
|
11436
11912
|
save(projectName) {
|
|
@@ -11594,17 +12070,17 @@ ${lines.join("\n")}
|
|
|
11594
12070
|
return id;
|
|
11595
12071
|
}
|
|
11596
12072
|
tryResolveProjectId(projectName) {
|
|
11597
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
12073
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq20(projects.name, projectName)).get();
|
|
11598
12074
|
return row?.id;
|
|
11599
12075
|
}
|
|
11600
12076
|
loadRow(projectId) {
|
|
11601
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
12077
|
+
const row = this.opts.db.select().from(agentSessions).where(eq20(agentSessions.projectId, projectId)).get();
|
|
11602
12078
|
return row ?? null;
|
|
11603
12079
|
}
|
|
11604
12080
|
insertRow(params) {
|
|
11605
12081
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11606
12082
|
this.opts.db.insert(agentSessions).values({
|
|
11607
|
-
id: params.id ??
|
|
12083
|
+
id: params.id ?? crypto24.randomUUID(),
|
|
11608
12084
|
projectId: params.projectId,
|
|
11609
12085
|
systemPrompt: params.systemPrompt,
|
|
11610
12086
|
modelProvider: params.provider ?? params.modelProvider ?? AgentProviderIds.claude,
|
|
@@ -11617,14 +12093,14 @@ ${lines.join("\n")}
|
|
|
11617
12093
|
}
|
|
11618
12094
|
updateRow(projectId, patch) {
|
|
11619
12095
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11620
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
12096
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq20(agentSessions.projectId, projectId)).run();
|
|
11621
12097
|
}
|
|
11622
12098
|
};
|
|
11623
12099
|
|
|
11624
12100
|
// src/agent/agent-routes.ts
|
|
11625
|
-
import { eq as
|
|
12101
|
+
import { eq as eq21 } from "drizzle-orm";
|
|
11626
12102
|
function resolveProject(db, name) {
|
|
11627
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
12103
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq21(projects.name, name)).get();
|
|
11628
12104
|
if (!row) throw notFound("project", name);
|
|
11629
12105
|
return row;
|
|
11630
12106
|
}
|
|
@@ -11633,7 +12109,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
11633
12109
|
"/projects/:name/agent/transcript",
|
|
11634
12110
|
async (request) => {
|
|
11635
12111
|
const project = resolveProject(opts.db, request.params.name);
|
|
11636
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
12112
|
+
const row = opts.db.select().from(agentSessions).where(eq21(agentSessions.projectId, project.id)).get();
|
|
11637
12113
|
if (!row) {
|
|
11638
12114
|
return { messages: [], modelProvider: null, modelId: null, updatedAt: null };
|
|
11639
12115
|
}
|
|
@@ -11657,7 +12133,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
11657
12133
|
async (request) => {
|
|
11658
12134
|
const project = resolveProject(opts.db, request.params.name);
|
|
11659
12135
|
opts.sessionRegistry.reset(project.name);
|
|
11660
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
12136
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq21(agentSessions.projectId, project.id)).run();
|
|
11661
12137
|
return { status: "reset" };
|
|
11662
12138
|
}
|
|
11663
12139
|
);
|
|
@@ -12465,7 +12941,7 @@ function buildSnapshotSummary(companyName, queries2, providers, queryResults, au
|
|
|
12465
12941
|
competitorCounts.set(competitor, (competitorCounts.get(competitor) ?? 0) + 1);
|
|
12466
12942
|
}
|
|
12467
12943
|
}
|
|
12468
|
-
const topCompetitors = [...competitorCounts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 10).map(([name,
|
|
12944
|
+
const topCompetitors = [...competitorCounts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 10).map(([name, count2]) => ({ name, count: count2 }));
|
|
12469
12945
|
const defaultMeaning = totalComparisons > 0 ? `${companyName} was mentioned in ${mentionCount}/${totalComparisons} successful provider-query response${totalComparisons === 1 ? "" : "s"} across ${queries2.length} category queries.` : `No successful provider responses were returned across ${queries2.length} category queries.`;
|
|
12470
12946
|
const failureNote = failedComparisons > 0 ? [
|
|
12471
12947
|
`${failedComparisons} provider response${failedComparisons === 1 ? "" : "s"} failed and ${failedComparisons === 1 ? "was" : "were"} excluded from visibility totals.`
|
|
@@ -12600,20 +13076,20 @@ function clipText(value, length) {
|
|
|
12600
13076
|
}
|
|
12601
13077
|
|
|
12602
13078
|
// src/research-runner.ts
|
|
12603
|
-
import { and as
|
|
13079
|
+
import { and as and16, eq as eq22, inArray as inArray7, sql as sql10 } from "drizzle-orm";
|
|
12604
13080
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
12605
13081
|
function finalResearchRunStatus(completed, failed) {
|
|
12606
13082
|
if (failed === 0) return ResearchRunStatuses.completed;
|
|
12607
13083
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
12608
13084
|
}
|
|
12609
13085
|
async function executeResearchRun(db, registry, runId, projectId) {
|
|
12610
|
-
const run = db.select().from(researchRuns).where(
|
|
13086
|
+
const run = db.select().from(researchRuns).where(and16(eq22(researchRuns.id, runId), eq22(researchRuns.projectId, projectId))).get();
|
|
12611
13087
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
12612
|
-
const project = db.select().from(projects).where(
|
|
13088
|
+
const project = db.select().from(projects).where(eq22(projects.id, projectId)).get();
|
|
12613
13089
|
if (!project) return;
|
|
12614
13090
|
const provider = registry.get(run.provider);
|
|
12615
13091
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12616
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(
|
|
13092
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and16(eq22(researchRuns.id, runId), eq22(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
12617
13093
|
if (claim.changes !== 1) return;
|
|
12618
13094
|
let reserved = 0;
|
|
12619
13095
|
let dispatched = 0;
|
|
@@ -12637,8 +13113,8 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12637
13113
|
}
|
|
12638
13114
|
reserved = run.totalQueries;
|
|
12639
13115
|
reservation = { scope, period };
|
|
12640
|
-
const rows = db.select().from(researchRunQueries).where(
|
|
12641
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(
|
|
13116
|
+
const rows = db.select().from(researchRunQueries).where(eq22(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
|
|
13117
|
+
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq22(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
12642
13118
|
const domains = effectiveDomains(project);
|
|
12643
13119
|
const brands = effectiveBrandNames(project);
|
|
12644
13120
|
const config = { ...provider.config, model: run.resolvedModel };
|
|
@@ -12646,7 +13122,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12646
13122
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
12647
13123
|
try {
|
|
12648
13124
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12649
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(
|
|
13125
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and16(eq22(researchRunQueries.id, row.id), eq22(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
12650
13126
|
const raw = await gate.run(async () => {
|
|
12651
13127
|
dispatched++;
|
|
12652
13128
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -12673,7 +13149,7 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12673
13149
|
citationState: determineCitationState(normalized, domains),
|
|
12674
13150
|
rawResponse: raw.rawResponse,
|
|
12675
13151
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12676
|
-
}).where(
|
|
13152
|
+
}).where(and16(eq22(researchRunQueries.id, row.id), eq22(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
12677
13153
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
12678
13154
|
} catch (error) {
|
|
12679
13155
|
try {
|
|
@@ -12697,18 +13173,18 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
12697
13173
|
}
|
|
12698
13174
|
}
|
|
12699
13175
|
function incrementResearchProgress(db, runId, column) {
|
|
12700
|
-
db.update(researchRuns).set({ [column]:
|
|
13176
|
+
db.update(researchRuns).set({ [column]: sql10`${researchRuns[column]} + 1` }).where(eq22(researchRuns.id, runId)).run();
|
|
12701
13177
|
}
|
|
12702
13178
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
12703
13179
|
const message = error instanceof Error ? error.message : String(error);
|
|
12704
|
-
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
13180
|
+
const failed = db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error: message, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and16(eq22(researchRunQueries.id, queryId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
12705
13181
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
12706
13182
|
}
|
|
12707
13183
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
12708
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
13184
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and16(eq22(researchRunQueries.researchRunId, runId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
12709
13185
|
}
|
|
12710
13186
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
12711
|
-
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(
|
|
13187
|
+
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq22(researchRunQueries.researchRunId, runId)).all();
|
|
12712
13188
|
const completed = rows.filter((row) => row.status === ResearchQueryStatuses.completed).length;
|
|
12713
13189
|
const failed = rows.filter((row) => row.status === ResearchQueryStatuses.failed).length;
|
|
12714
13190
|
db.update(researchRuns).set({
|
|
@@ -12717,11 +13193,11 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
12717
13193
|
failedQueries: failed,
|
|
12718
13194
|
error: fatalError ?? (failed === rows.length ? "Every research query failed." : null),
|
|
12719
13195
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12720
|
-
}).where(
|
|
13196
|
+
}).where(eq22(researchRuns.id, runId)).run();
|
|
12721
13197
|
}
|
|
12722
13198
|
|
|
12723
13199
|
// src/server.ts
|
|
12724
|
-
var _require3 =
|
|
13200
|
+
var _require3 = createRequire4(import.meta.url);
|
|
12725
13201
|
var { version: PKG_VERSION2 } = _require3("../package.json");
|
|
12726
13202
|
var log20 = createLogger("Server");
|
|
12727
13203
|
var DEFAULT_QUOTA = {
|
|
@@ -12780,14 +13256,14 @@ function summarizeProviderConfig(config) {
|
|
|
12780
13256
|
};
|
|
12781
13257
|
}
|
|
12782
13258
|
function hashApiKey(key) {
|
|
12783
|
-
return
|
|
13259
|
+
return crypto25.createHash("sha256").update(key).digest("hex");
|
|
12784
13260
|
}
|
|
12785
13261
|
var DASHBOARD_SCRYPT_KEYLEN = 64;
|
|
12786
13262
|
var DASHBOARD_SCRYPT_COST = 1 << 15;
|
|
12787
13263
|
var DASHBOARD_SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
12788
13264
|
function hashDashboardPassword(password) {
|
|
12789
|
-
const salt =
|
|
12790
|
-
const derived =
|
|
13265
|
+
const salt = crypto25.randomBytes(16);
|
|
13266
|
+
const derived = crypto25.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
|
|
12791
13267
|
N: DASHBOARD_SCRYPT_COST,
|
|
12792
13268
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
12793
13269
|
});
|
|
@@ -12808,14 +13284,14 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
12808
13284
|
} catch {
|
|
12809
13285
|
return { ok: false, needsRehash: false };
|
|
12810
13286
|
}
|
|
12811
|
-
const derived =
|
|
13287
|
+
const derived = crypto25.scryptSync(password, salt, expected.length, {
|
|
12812
13288
|
N: DASHBOARD_SCRYPT_COST,
|
|
12813
13289
|
maxmem: DASHBOARD_SCRYPT_MAXMEM
|
|
12814
13290
|
});
|
|
12815
13291
|
if (derived.length !== expected.length)
|
|
12816
13292
|
return { ok: false, needsRehash: false };
|
|
12817
13293
|
return {
|
|
12818
|
-
ok:
|
|
13294
|
+
ok: crypto25.timingSafeEqual(derived, expected),
|
|
12819
13295
|
needsRehash: false
|
|
12820
13296
|
};
|
|
12821
13297
|
}
|
|
@@ -12824,7 +13300,7 @@ function verifyDashboardPassword(password, storedHash) {
|
|
|
12824
13300
|
const expected = Buffer.from(storedHash, "hex");
|
|
12825
13301
|
if (candidate.length !== expected.length)
|
|
12826
13302
|
return { ok: false, needsRehash: false };
|
|
12827
|
-
const ok =
|
|
13303
|
+
const ok = crypto25.timingSafeEqual(candidate, expected);
|
|
12828
13304
|
return { ok, needsRehash: ok };
|
|
12829
13305
|
}
|
|
12830
13306
|
return { ok: false, needsRehash: false };
|
|
@@ -13032,7 +13508,7 @@ async function createServer(opts) {
|
|
|
13032
13508
|
(runId, projectId, result) => notifier.dispatchInsightWebhooks(runId, projectId, result),
|
|
13033
13509
|
async (ctx) => {
|
|
13034
13510
|
if (!sessionRegistry) return;
|
|
13035
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
13511
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq23(projects.id, ctx.projectId)).get();
|
|
13036
13512
|
if (!project) return;
|
|
13037
13513
|
let content;
|
|
13038
13514
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -13382,7 +13858,7 @@ async function createServer(opts) {
|
|
|
13382
13858
|
void (async () => {
|
|
13383
13859
|
try {
|
|
13384
13860
|
const report = await aeroClient.runDoctor({ project: projectName });
|
|
13385
|
-
const project = opts.db.select().from(projects).where(
|
|
13861
|
+
const project = opts.db.select().from(projects).where(eq23(projects.name, projectName)).get();
|
|
13386
13862
|
if (!project) {
|
|
13387
13863
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
13388
13864
|
return;
|
|
@@ -13410,9 +13886,9 @@ async function createServer(opts) {
|
|
|
13410
13886
|
});
|
|
13411
13887
|
if (!probed) return;
|
|
13412
13888
|
const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(
|
|
13413
|
-
|
|
13414
|
-
|
|
13415
|
-
|
|
13889
|
+
and17(
|
|
13890
|
+
eq23(ccReleaseSyncs.release, probed.release),
|
|
13891
|
+
eq23(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
|
|
13416
13892
|
)
|
|
13417
13893
|
).limit(1).get();
|
|
13418
13894
|
if (alreadySynced) {
|
|
@@ -13567,7 +14043,7 @@ async function createServer(opts) {
|
|
|
13567
14043
|
return removed;
|
|
13568
14044
|
}
|
|
13569
14045
|
};
|
|
13570
|
-
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ??
|
|
14046
|
+
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto25.randomBytes(32).toString("hex");
|
|
13571
14047
|
const googleConnectionStore = {
|
|
13572
14048
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
13573
14049
|
getConnection: (domain, connectionType) => getGoogleConnection(opts.config, domain, connectionType),
|
|
@@ -13642,11 +14118,11 @@ async function createServer(opts) {
|
|
|
13642
14118
|
const googlePublicUrl = resolveGooglePublicUrl(opts.config, basePath);
|
|
13643
14119
|
if (opts.config.apiKey) {
|
|
13644
14120
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
13645
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
14121
|
+
const existing = opts.db.select().from(apiKeys).where(eq23(apiKeys.keyHash, keyHash)).get();
|
|
13646
14122
|
if (!existing) {
|
|
13647
14123
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
13648
14124
|
opts.db.insert(apiKeys).values({
|
|
13649
|
-
id: `key_${
|
|
14125
|
+
id: `key_${crypto25.randomBytes(8).toString("hex")}`,
|
|
13650
14126
|
name: "default",
|
|
13651
14127
|
keyHash,
|
|
13652
14128
|
keyPrefix: prefix,
|
|
@@ -13670,7 +14146,7 @@ async function createServer(opts) {
|
|
|
13670
14146
|
};
|
|
13671
14147
|
const createSession = (apiKeyId) => {
|
|
13672
14148
|
pruneExpiredSessions();
|
|
13673
|
-
const sessionId =
|
|
14149
|
+
const sessionId = crypto25.randomBytes(32).toString("hex");
|
|
13674
14150
|
sessions.set(sessionId, {
|
|
13675
14151
|
apiKeyId,
|
|
13676
14152
|
expiresAt: Date.now() + SESSION_TTL_MS
|
|
@@ -13694,7 +14170,7 @@ async function createServer(opts) {
|
|
|
13694
14170
|
};
|
|
13695
14171
|
const getDefaultApiKey = () => {
|
|
13696
14172
|
if (!opts.config.apiKey) return void 0;
|
|
13697
|
-
return opts.db.select().from(apiKeys).where(
|
|
14173
|
+
return opts.db.select().from(apiKeys).where(eq23(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
13698
14174
|
};
|
|
13699
14175
|
const createPasswordSession = (reply) => {
|
|
13700
14176
|
const key = getDefaultApiKey();
|
|
@@ -13718,7 +14194,7 @@ async function createServer(opts) {
|
|
|
13718
14194
|
if (!header) return false;
|
|
13719
14195
|
const parts = header.split(" ");
|
|
13720
14196
|
if (parts.length !== 2 || parts[0] !== "Bearer") return false;
|
|
13721
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
14197
|
+
const key = opts.db.select().from(apiKeys).where(eq23(apiKeys.keyHash, hashApiKey(parts[1]))).get();
|
|
13722
14198
|
return Boolean(key && !key.revokedAt);
|
|
13723
14199
|
};
|
|
13724
14200
|
const namedAccountsInUse = () => anyUsersExist(opts.db);
|
|
@@ -13808,12 +14284,12 @@ async function createServer(opts) {
|
|
|
13808
14284
|
return reply.send({ authenticated: true });
|
|
13809
14285
|
}
|
|
13810
14286
|
if (apiKey) {
|
|
13811
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
14287
|
+
const key = opts.db.select().from(apiKeys).where(eq23(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
13812
14288
|
if (!key || key.revokedAt) {
|
|
13813
14289
|
const err2 = authInvalid();
|
|
13814
14290
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
13815
14291
|
}
|
|
13816
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
14292
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq23(apiKeys.id, key.id)).run();
|
|
13817
14293
|
const sessionId = createSession(key.id);
|
|
13818
14294
|
reply.header(
|
|
13819
14295
|
"set-cookie",
|
|
@@ -14030,7 +14506,7 @@ async function createServer(opts) {
|
|
|
14030
14506
|
deps: {
|
|
14031
14507
|
enqueueAutoExtract: ({ projectId, release: r }) => {
|
|
14032
14508
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14033
|
-
const runId =
|
|
14509
|
+
const runId = crypto25.randomUUID();
|
|
14034
14510
|
opts.db.insert(runs).values({
|
|
14035
14511
|
id: runId,
|
|
14036
14512
|
projectId,
|
|
@@ -14155,7 +14631,7 @@ async function createServer(opts) {
|
|
|
14155
14631
|
...inspectOpts,
|
|
14156
14632
|
config: opts.config
|
|
14157
14633
|
}).then(() => {
|
|
14158
|
-
const finished = opts.db.select({ status: runs.status }).from(runs).where(
|
|
14634
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq23(runs.id, runId)).get();
|
|
14159
14635
|
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
14160
14636
|
return maybeRefreshGscCoverage(
|
|
14161
14637
|
opts.db,
|
|
@@ -14263,7 +14739,7 @@ async function createServer(opts) {
|
|
|
14263
14739
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
14264
14740
|
opts.db.insert(auditLog).values(
|
|
14265
14741
|
targetProjectIds.map((projectId) => ({
|
|
14266
|
-
id:
|
|
14742
|
+
id: crypto25.randomUUID(),
|
|
14267
14743
|
projectId,
|
|
14268
14744
|
actor: "api",
|
|
14269
14745
|
action: existing ? "provider.updated" : "provider.created",
|
|
@@ -14401,7 +14877,7 @@ async function createServer(opts) {
|
|
|
14401
14877
|
}
|
|
14402
14878
|
];
|
|
14403
14879
|
},
|
|
14404
|
-
onGenerateQueries: async (providerName,
|
|
14880
|
+
onGenerateQueries: async (providerName, count2, project) => {
|
|
14405
14881
|
const provider = registry.get(providerName);
|
|
14406
14882
|
if (!provider)
|
|
14407
14883
|
throw new Error(`Provider "${providerName}" is not configured`);
|
|
@@ -14413,10 +14889,10 @@ async function createServer(opts) {
|
|
|
14413
14889
|
language: project.language,
|
|
14414
14890
|
existingQueries: project.existingQueries,
|
|
14415
14891
|
siteText,
|
|
14416
|
-
count
|
|
14892
|
+
count: count2
|
|
14417
14893
|
});
|
|
14418
14894
|
const raw = await provider.adapter.generateText(prompt, provider.config);
|
|
14419
|
-
return parseQueryResponse(raw,
|
|
14895
|
+
return parseQueryResponse(raw, count2);
|
|
14420
14896
|
},
|
|
14421
14897
|
onSnapshotRequested: async (input) => {
|
|
14422
14898
|
return snapshotService.createReport(input);
|
|
@@ -14531,7 +15007,7 @@ async function createServer(opts) {
|
|
|
14531
15007
|
}
|
|
14532
15008
|
checkLatestVersionForServer();
|
|
14533
15009
|
scheduler.start();
|
|
14534
|
-
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(
|
|
15010
|
+
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq23(researchRuns.status, ResearchRunStatuses.queued)).all()) {
|
|
14535
15011
|
dispatchResearchRun(run.id, run.projectId);
|
|
14536
15012
|
}
|
|
14537
15013
|
app.addHook("onClose", async () => {
|
|
@@ -14575,7 +15051,7 @@ function buildQueryGenerationPrompt(ctx) {
|
|
|
14575
15051
|
);
|
|
14576
15052
|
return lines.join("\n");
|
|
14577
15053
|
}
|
|
14578
|
-
function parseQueryResponse(raw,
|
|
15054
|
+
function parseQueryResponse(raw, count2) {
|
|
14579
15055
|
const seen = /* @__PURE__ */ new Set();
|
|
14580
15056
|
const results = [];
|
|
14581
15057
|
for (const line of raw.split("\n")) {
|
|
@@ -14591,7 +15067,7 @@ function parseQueryResponse(raw, count) {
|
|
|
14591
15067
|
if (seen.has(key)) continue;
|
|
14592
15068
|
seen.add(key);
|
|
14593
15069
|
results.push(cleaned);
|
|
14594
|
-
if (results.length >=
|
|
15070
|
+
if (results.length >= count2) break;
|
|
14595
15071
|
}
|
|
14596
15072
|
return results;
|
|
14597
15073
|
}
|