@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.
Files changed (35) hide show
  1. package/assets/agent-workspace/skills/aero/references/orchestration.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +14 -2
  4. package/assets/assets/{AuditHistoryPanel-BlbMBxOY.js → AuditHistoryPanel-gmJMVlXX.js} +1 -1
  5. package/assets/assets/{BacklinksPage-DA9EaV9q.js → BacklinksPage-Bv2sTCyN.js} +1 -1
  6. package/assets/assets/{HistoryPage-BTrIHjMQ.js → HistoryPage-13q7GkiI.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-CN6o5xFg.js → MeasurementPropertyPage-BgBqXT1p.js} +1 -1
  8. package/assets/assets/ProjectPage-EsGwKhot.js +15 -0
  9. package/assets/assets/{RunRow-ch_OlCPs.js → RunRow-bPzapIGJ.js} +1 -1
  10. package/assets/assets/{RunsPage-T89Y_yS8.js → RunsPage-Dqfz18lH.js} +1 -1
  11. package/assets/assets/{SettingsPage-DnmEq3F9.js → SettingsPage-_wMecn-V.js} +1 -1
  12. package/assets/assets/{TrafficPage-CW1UhaJt.js → TrafficPage-RYBdsOLB.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-r4Ji3wJm.js → TrafficSourceDetailPage-DpeMMyKU.js} +1 -1
  14. package/assets/assets/{extract-error-message-a8EErBoZ.js → extract-error-message-DUarAlyD.js} +1 -1
  15. package/assets/assets/index-BxegEwic.css +1 -0
  16. package/assets/assets/{index-BKq6k5Of.js → index-DP-fT2uI.js} +29 -29
  17. package/assets/assets/react-sigma_core.esm.min-DnypJl5w.js +312 -0
  18. package/assets/assets/vendor-lucide-BtYdosYk.js +1 -0
  19. package/assets/assets/{vendor-markdown-CpDJch4G.js → vendor-markdown-D274_wsI.js} +1 -1
  20. package/assets/assets/{vendor-radix-DnzzbllM.js → vendor-radix-BxMXSwbC.js} +1 -1
  21. package/assets/assets/{vendor-recharts-33ACLxsD.js → vendor-recharts-C4ktw_aE.js} +1 -1
  22. package/assets/assets/{vendor-tanstack-BjS7iY3g.js → vendor-tanstack-pFxw0eGP.js} +1 -1
  23. package/assets/index.html +7 -7
  24. package/dist/{chunk-VOL2BSM5.js → chunk-ABNN2ZVR.js} +1332 -29
  25. package/dist/{chunk-VIKPFI6F.js → chunk-AMPT2EP7.js} +328 -1
  26. package/dist/{chunk-OSHRDV36.js → chunk-CPASKGUE.js} +247 -1
  27. package/dist/{chunk-YSESZNVT.js → chunk-ZWESOFA5.js} +711 -235
  28. package/dist/cli.js +427 -5
  29. package/dist/index.js +4 -4
  30. package/dist/{intelligence-service-G4NV3X36.js → intelligence-service-WHC7B32V.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +11 -9
  33. package/assets/assets/ProjectPage-CsDbgp_0.js +0 -12
  34. package/assets/assets/index-DyL54s8x.css +0 -1
  35. 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-OSHRDV36.js";
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-VOL2BSM5.js";
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-VIKPFI6F.js";
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 createRequire3 } from "module";
565
- import crypto24 from "crypto";
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 and16, eq as eq22 } from "drizzle-orm";
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, count) {
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} + ${count}`, updatedAt: now }
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 crypto16 from "crypto";
6934
- import { and as and10, eq as eq13, sql as sql5 } from "drizzle-orm";
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(and10(eq13(runs.id, runId), eq13(runs.projectId, projectId), eq13(runs.status, "queued"))).run();
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: sql5`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq13(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
7069
- const attemptId = crypto16.randomUUID();
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
- eq13(siteCrawlEdges.projectId, projectId),
7104
- eq13(siteCrawlEdges.runId, runId),
7105
- eq13(siteCrawlEdges.attemptId, attemptId)
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(and10(
7540
+ tx.update(siteCrawlEdges).set({ sourceNodeKey: nodeKey, updatedAt: now }).where(and11(
7108
7541
  ...scope,
7109
- eq13(siteCrawlEdges.sourceUrl, url)
7542
+ eq14(siteCrawlEdges.sourceUrl, url)
7110
7543
  )).run();
7111
- tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(and10(
7544
+ tx.update(siteCrawlEdges).set({ targetNodeKey: nodeKey, updatedAt: now }).where(and11(
7112
7545
  ...scope,
7113
- eq13(siteCrawlEdges.targetUrl, url)
7546
+ eq14(siteCrawlEdges.targetUrl, url)
7114
7547
  )).run();
7115
- tx.update(siteCrawlPages).set({ canonicalNodeKey: nodeKey, updatedAt: now }).where(and10(
7116
- eq13(siteCrawlPages.projectId, projectId),
7117
- eq13(siteCrawlPages.runId, runId),
7118
- eq13(siteCrawlPages.attemptId, attemptId),
7119
- eq13(siteCrawlPages.canonicalUrl, url)
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: crypto16.randomUUID(),
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 ? { factors: audit.factors.map(toPageFactor) } : {},
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 : sql5`coalesce(${siteCrawlPages.fetchedAt}, ${now})`,
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 ? { factors: audit.factors.map(toPageFactor) } : {},
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: crypto16.randomUUID(),
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(and10(
7256
- eq13(siteCrawlPages.projectId, projectId),
7257
- eq13(siteCrawlPages.runId, runId),
7258
- eq13(siteCrawlPages.attemptId, attemptId),
7259
- eq13(siteCrawlPages.nodeKey, key)
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(and10(
7266
- eq13(siteCrawlEventReceipts.attemptId, attemptId),
7267
- eq13(siteCrawlEventReceipts.sequence, event.sequence),
7268
- eq13(siteCrawlEventReceipts.batchId, event.batchId)
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: crypto16.randomUUID(),
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 : sql5`${siteCrawlAttempts.pagesEligible}`,
7298
- pagesErrored: event.type === "summary" ? observedErrorCount(observedPages.values()) : sql5`${siteCrawlAttempts.pagesErrored}`,
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(eq13(siteCrawlAttempts.id, attemptId)).run();
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(eq13(siteCrawlAttempts.id, attemptId)).run();
7740
+ }).where(eq14(siteCrawlAttempts.id, attemptId)).run();
7308
7741
  }
7309
7742
  });
7310
7743
  };
7311
7744
  try {
7312
- const project = db.select().from(projects).where(eq13(projects.id, projectId)).get();
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(and10(eq13(runs.id, runId), eq13(runs.status, "running"))).run();
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: crypto16.randomUUID(),
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: crypto16.randomUUID(),
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: crypto16.randomUUID(),
7844
+ id: crypto17.randomUUID(),
7379
7845
  projectId,
7380
7846
  runId,
7381
7847
  attemptId,
7382
- rootUrl: crawlSummary.rootUrl,
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: crypto16.randomUUID(),
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(eq13(siteCrawlAttempts.id, attemptId)).run();
7911
+ }).where(eq14(siteCrawlAttempts.id, attemptId)).run();
7445
7912
  return true;
7446
7913
  });
7447
7914
  if (!published) {
7448
- const current = db.select({ status: runs.status }).from(runs).where(eq13(runs.id, runId)).get();
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(eq13(siteCrawlAttempts.id, attemptId)).run();
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(and10(eq13(runs.id, runId), eq13(runs.status, "running"))).run();
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 and11, eq as eq14, inArray as inArray4, isNull, sql as sql6 } from "drizzle-orm";
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(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
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(and11(
7493
- eq14(runs.kind, RunKinds["answer-visibility"]),
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(eq14(runs.kind, RunKinds["answer-visibility"])).all();
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(eq14(competitors.projectId, project.id)).all().map((row) => row.domain);
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(eq14(querySnapshots.id, update.id)).run();
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(eq14(gaTrafficSnapshots.projectId, opts.projectId));
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 ? and11(...baseConditions) : void 0).all();
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(eq14(gaTrafficSnapshots.id, row.id)).run();
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(eq14(projects.name, projectFilter)).get();
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(eq14(gaAiReferrals.projectId, opts.projectId));
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 ? and11(...baseConditions) : void 0).all();
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(eq14(gaAiReferrals.id, row.id)).run();
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(eq14(projects.name, projectFilter)).get();
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(eq14(projects.id, projectId)).get();
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(eq14(competitors.projectId, projectId)).all().map((row) => row.domain);
7791
- const runRows = db.select({ id: runs.id }).from(runs).where(and11(eq14(runs.kind, RunKinds["answer-visibility"]), eq14(runs.projectId, projectId))).all();
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(eq14(querySnapshots.id, update.id)).run();
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(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
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-G4NV3X36.js");
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(eq14(projects.name, opts.project)).get();
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(and11(
8111
- eq14(auditLog.projectId, project.id),
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, eq14(querySnapshots.runId, runs.id)).where(and11(
8120
- eq14(runs.projectId, project.id),
8121
- isNull(querySnapshots.queryId),
8122
- isNull(querySnapshots.queryText)
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(and11(
8143
- eq14(querySnapshots.runId, run.runId),
8144
- isNull(querySnapshots.queryId),
8145
- isNull(querySnapshots.queryText)
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(eq14(querySnapshots.id, u.id)).run();
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(eq14(projects.name, projectFilter)).all() : db.select().from(projects).all();
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: sql6`count(*)` }).from(rawEventSamples).where(and11(
8309
- eq14(rawEventSamples.eventType, "unknown"),
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(and11(
8322
- eq14(rawEventSamples.eventType, "unknown"),
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(eq14(rawEventSamples.id, snap.id)).run();
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: sql6`${aiUserFetchEventsHourly.hits} + 1`,
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: sql6`${crawlerEventsHourly.hits} + 1`,
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: sql6`count(*)` }).from(rawEventSamples).where(and11(
8426
- eq14(rawEventSamples.eventType, "unknown"),
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, count] of sorted) {
8451
- console.log(` ${count.toString().padStart(5)} ${bot}`);
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 crypto17 from "crypto";
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 crypto17.createHash("sha256").update(fs4.readFileSync(filePath)).digest("hex");
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 crypto18 from "crypto";
9334
+ import crypto19 from "crypto";
8859
9335
  import cron from "node-cron";
8860
- import { and as and12, eq as eq15, inArray as inArray5, notExists, sql as sql7 } from "drizzle-orm";
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: sql7`1` }).from(schedules).where(and12(
8893
- eq15(schedules.projectId, projects.id),
8894
- eq15(schedules.kind, SchedulableRunKinds.doctor)
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: crypto18.randomUUID(),
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(eq15(schedules.enabled, true)).all();
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(and12(eq15(schedules.projectId, projectId), eq15(schedules.kind, kind))).get();
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(eq15(schedules.id, scheduleId)).run();
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(eq15(schedules.id, scheduleId)).get();
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(eq15(projects.id, projectId)).get();
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(eq15(schedules.id, currentSchedule.id)).run();
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 = crypto18.randomUUID();
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(eq15(schedules.id, currentSchedule.id)).run();
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(and12(
9084
- eq15(runs.projectId, projectId),
9085
- eq15(runs.kind, RunKinds["ads-sync"]),
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(eq15(schedules.id, currentSchedule.id)).run();
9566
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq16(schedules.id, currentSchedule.id)).run();
9091
9567
  return;
9092
9568
  }
9093
- const runId2 = crypto18.randomUUID();
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(eq15(schedules.id, currentSchedule.id)).run();
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(eq15(schedules.id, currentSchedule.id)).run();
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(eq15(schedules.id, currentSchedule.id)).run();
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(eq15(schedules.id, currentSchedule.id)).run();
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(and12(
9159
- eq15(runs.projectId, projectId),
9160
- eq15(runs.kind, RunKinds["site-audit"]),
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(eq15(schedules.id, currentSchedule.id)).run();
9641
+ this.db.update(schedules).set({ nextRunAt, updatedAt: now }).where(eq16(schedules.id, currentSchedule.id)).run();
9166
9642
  return;
9167
9643
  }
9168
- const runId2 = crypto18.randomUUID();
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(eq15(schedules.id, currentSchedule.id)).run();
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(eq15(schedules.id, currentSchedule.id)).run();
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(eq15(schedules.id, currentSchedule.id)).run();
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 eq16, desc as desc6, and as and13, inArray as inArray6, or } from "drizzle-orm";
9266
- import crypto19 from "crypto";
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(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
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(eq16(runs.id, runId)).get();
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(eq16(projects.id, projectId)).get();
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(eq16(projects.id, projectId)).get();
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(eq16(doctorHealthState.projectId, projectId)).get();
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(eq16(doctorHealthState.projectId, projectId)).run();
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(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
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(eq16(doctorHealthState.projectId, projectId)).run();
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(eq16(notifications.projectId, projectId)).all().filter((n) => n.enabled);
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(eq16(runs.id, runId)).get();
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(eq16(projects.id, projectId)).get();
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(eq16(runs.id, runId)).get();
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(and13(
9477
- eq16(runs.projectId, projectId),
9478
- eq16(runs.kind, thisRun.kind),
9479
- eq16(runs.createdAt, thisRun.createdAt)
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(eq16(projects.id, projectId)).get();
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
- and13(
9503
- eq16(runs.projectId, projectId),
9504
- eq16(runs.kind, thisRun.kind),
9505
- or(eq16(runs.status, "completed"), eq16(runs.status, "partial"))
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(desc6(runs.createdAt), desc6(runs.id)).limit(RECENT_FETCH_LIMIT).all();
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, eq16(querySnapshots.queryId, queries.id)).where(inArray6(querySnapshots.runId, currentRunIds)).all();
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: crypto19.randomUUID(),
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 eq17 } from "drizzle-orm";
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(eq17(runs.id, runId)).get();
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(eq17(discoverySessions.runId, runId)).get();
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 crypto23 from "crypto";
9755
- import { eq as eq19 } from "drizzle-orm";
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 crypto20 from "crypto";
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: crypto20.randomUUID(),
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 crypto21 from "crypto";
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: crypto21.randomUUID(),
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 crypto22 from "crypto";
10884
- import { and as and14, desc as desc7, eq as eq18, like, sql as sql8 } from "drizzle-orm";
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(eq18(agentMemory.projectId, projectId)).orderBy(desc7(agentMemory.updatedAt));
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 = crypto22.randomUUID();
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(and14(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, args.key))).get();
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(and14(eq18(agentMemory.projectId, projectId), eq18(agentMemory.key, key))).run();
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 = crypto22.randomUUID();
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
- and14(
10964
- eq18(agentMemory.projectId, args.projectId),
11439
+ and15(
11440
+ eq19(agentMemory.projectId, args.projectId),
10965
11441
  like(agentMemory.key, `${sessionPrefix}%`)
10966
11442
  )
10967
- ).orderBy(desc7(agentMemory.updatedAt)).all();
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(sql8`${agentMemory.id} IN (${sql8.join(stale.map((s) => sql8`${s}`), sql8`, `)})`).run();
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(and14(eq18(agentMemory.projectId, args.projectId), eq18(agentMemory.key, key))).get();
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(eq19(agentSessions.projectId, projectId)).run();
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 = crypto23.randomUUID();
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(eq19(agentSessions.projectId, projectId)).run();
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(eq19(projects.name, projectName)).get();
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(eq19(agentSessions.projectId, projectId)).get();
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 ?? crypto23.randomUUID(),
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(eq19(agentSessions.projectId, projectId)).run();
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 eq20 } from "drizzle-orm";
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(eq20(projects.name, name)).get();
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(eq20(agentSessions.projectId, project.id)).get();
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(eq20(agentSessions.projectId, project.id)).run();
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, count]) => ({ name, count }));
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 and15, eq as eq21, inArray as inArray7, sql as sql9 } from "drizzle-orm";
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(and15(eq21(researchRuns.id, runId), eq21(researchRuns.projectId, projectId))).get();
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(eq21(projects.id, projectId)).get();
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(and15(eq21(researchRuns.id, runId), eq21(researchRuns.status, ResearchRunStatuses.queued))).run();
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(eq21(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
12641
- const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq21(competitors.projectId, projectId)).all().map((row) => row.domain);
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(and15(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
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(and15(eq21(researchRunQueries.id, row.id), eq21(researchRunQueries.status, ResearchQueryStatuses.running))).run();
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]: sql9`${researchRuns[column]} + 1` }).where(eq21(researchRuns.id, runId)).run();
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(and15(eq21(researchRunQueries.id, queryId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
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(and15(eq21(researchRunQueries.researchRunId, runId), inArray7(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
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(eq21(researchRunQueries.researchRunId, runId)).all();
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(eq21(researchRuns.id, runId)).run();
13196
+ }).where(eq22(researchRuns.id, runId)).run();
12721
13197
  }
12722
13198
 
12723
13199
  // src/server.ts
12724
- var _require3 = createRequire3(import.meta.url);
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 crypto24.createHash("sha256").update(key).digest("hex");
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 = crypto24.randomBytes(16);
12790
- const derived = crypto24.scryptSync(password, salt, DASHBOARD_SCRYPT_KEYLEN, {
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 = crypto24.scryptSync(password, salt, expected.length, {
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: crypto24.timingSafeEqual(derived, expected),
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 = crypto24.timingSafeEqual(candidate, expected);
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(eq22(projects.id, ctx.projectId)).get();
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(eq22(projects.name, projectName)).get();
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
- and16(
13414
- eq22(ccReleaseSyncs.release, probed.release),
13415
- eq22(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
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 ?? crypto24.randomBytes(32).toString("hex");
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(eq22(apiKeys.keyHash, keyHash)).get();
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_${crypto24.randomBytes(8).toString("hex")}`,
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 = crypto24.randomBytes(32).toString("hex");
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(eq22(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
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(eq22(apiKeys.keyHash, hashApiKey(parts[1]))).get();
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(eq22(apiKeys.keyHash, hashApiKey(apiKey))).get();
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(eq22(apiKeys.id, key.id)).run();
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 = crypto24.randomUUID();
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(eq22(runs.id, runId)).get();
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: crypto24.randomUUID(),
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, count, project) => {
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, count);
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(eq22(researchRuns.status, ResearchRunStatuses.queued)).all()) {
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, count) {
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 >= count) break;
15070
+ if (results.length >= count2) break;
14595
15071
  }
14596
15072
  return results;
14597
15073
  }