@canonry/canonry 4.156.0 → 4.158.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/assets/agent-workspace/skills/canonry/SKILL.md +8 -3
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +454 -21
- package/assets/assets/{AuditHistoryPanel-gmJMVlXX.js → AuditHistoryPanel-McQfjoU9.js} +1 -1
- package/assets/assets/{BacklinksPage-Bv2sTCyN.js → BacklinksPage-Da8y1Wv8.js} +1 -1
- package/assets/assets/{HistoryPage-13q7GkiI.js → HistoryPage-9MJfhRyT.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BgBqXT1p.js → MeasurementPropertyPage-BiNAHEJW.js} +1 -1
- package/assets/assets/ProjectPage-mWSMjXrR.js +15 -0
- package/assets/assets/{RunRow-bPzapIGJ.js → RunRow-DCG2LBlS.js} +1 -1
- package/assets/assets/{RunsPage-Dqfz18lH.js → RunsPage-ByWA_FSk.js} +1 -1
- package/assets/assets/{SettingsPage-_wMecn-V.js → SettingsPage-DdSeKAKF.js} +1 -1
- package/assets/assets/{TrafficPage-RYBdsOLB.js → TrafficPage-5u8rPcBt.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DpeMMyKU.js → TrafficSourceDetailPage-Dm-fK87u.js} +1 -1
- package/assets/assets/{extract-error-message-DUarAlyD.js → extract-error-message-CtxkAd6E.js} +1 -1
- package/assets/assets/{index-DP-fT2uI.js → index-DUfg50Pl.js} +28 -28
- package/assets/assets/index-cXb3mfeQ.css +1 -0
- package/assets/assets/{react-sigma_core.esm.min-DnypJl5w.js → react-sigma_core.esm.min-ComIyDKi.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-ZWESOFA5.js → chunk-4VZP64GZ.js} +273 -15
- package/dist/{chunk-AMPT2EP7.js → chunk-DEVAEC2S.js} +121 -1
- package/dist/{chunk-CPASKGUE.js → chunk-LK6YFHG4.js} +35 -1
- package/dist/{chunk-ABNN2ZVR.js → chunk-YP4K2KXH.js} +1933 -451
- package/dist/cli.js +667 -142
- package/dist/index.d.ts +32 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-WHC7B32V.js → intelligence-service-LQ6ZRWMF.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +12 -10
- package/assets/assets/ProjectPage-EsGwKhot.js +0 -15
- package/assets/assets/index-BxegEwic.css +0 -1
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LK6YFHG4.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -136,7 +136,7 @@ import {
|
|
|
136
136
|
siteCrawlSnapshots,
|
|
137
137
|
toAlertView,
|
|
138
138
|
usageCounters
|
|
139
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-YP4K2KXH.js";
|
|
140
140
|
import {
|
|
141
141
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
142
142
|
AGENT_PROVIDER_IDS,
|
|
@@ -236,7 +236,7 @@ import {
|
|
|
236
236
|
validationError,
|
|
237
237
|
winnabilityClassLabel,
|
|
238
238
|
withRetry
|
|
239
|
-
} from "./chunk-
|
|
239
|
+
} from "./chunk-DEVAEC2S.js";
|
|
240
240
|
|
|
241
241
|
// src/telemetry.ts
|
|
242
242
|
import crypto from "crypto";
|
|
@@ -3167,8 +3167,67 @@ function removeVercelTrafficConnection(config, projectName) {
|
|
|
3167
3167
|
return true;
|
|
3168
3168
|
}
|
|
3169
3169
|
|
|
3170
|
-
// src/
|
|
3170
|
+
// src/cloudflare-traffic-config.ts
|
|
3171
|
+
function unsupportedDeliveryMode(deliveryMode) {
|
|
3172
|
+
const label = typeof deliveryMode === "string" ? ` "${deliveryMode}"` : "";
|
|
3173
|
+
return new Error(`Unsupported Cloudflare traffic delivery mode${label}`);
|
|
3174
|
+
}
|
|
3171
3175
|
function ensureConnections6(config) {
|
|
3176
|
+
if (!config.cloudflareTraffic) config.cloudflareTraffic = {};
|
|
3177
|
+
if (!config.cloudflareTraffic.connections) config.cloudflareTraffic.connections = [];
|
|
3178
|
+
return config.cloudflareTraffic.connections;
|
|
3179
|
+
}
|
|
3180
|
+
function normalizeDirectPushConnection(connection) {
|
|
3181
|
+
const deliveryMode = connection.deliveryMode;
|
|
3182
|
+
if (deliveryMode !== void 0 && deliveryMode !== "direct-push") {
|
|
3183
|
+
throw unsupportedDeliveryMode(deliveryMode);
|
|
3184
|
+
}
|
|
3185
|
+
if (deliveryMode === "direct-push") return connection;
|
|
3186
|
+
return { ...connection, deliveryMode: "direct-push" };
|
|
3187
|
+
}
|
|
3188
|
+
function getCloudflareTrafficConnection(config, projectName) {
|
|
3189
|
+
const connection = (config.cloudflareTraffic?.connections ?? []).find((c) => c.projectName === projectName);
|
|
3190
|
+
return connection ? normalizeDirectPushConnection(connection) : void 0;
|
|
3191
|
+
}
|
|
3192
|
+
function getCloudflareTrafficConnectionBySourceId(config, sourceId) {
|
|
3193
|
+
const connection = (config.cloudflareTraffic?.connections ?? []).find((c) => c.sourceId === sourceId);
|
|
3194
|
+
return connection ? normalizeDirectPushConnection(connection) : void 0;
|
|
3195
|
+
}
|
|
3196
|
+
function upsertCloudflareTrafficConnection(config, connection) {
|
|
3197
|
+
const deliveryMode = connection.deliveryMode;
|
|
3198
|
+
if (deliveryMode !== "direct-push") {
|
|
3199
|
+
throw unsupportedDeliveryMode(deliveryMode);
|
|
3200
|
+
}
|
|
3201
|
+
const connections = ensureConnections6(config);
|
|
3202
|
+
const index = connections.findIndex((c) => c.projectName === connection.projectName);
|
|
3203
|
+
if (index === -1) {
|
|
3204
|
+
connections.push(connection);
|
|
3205
|
+
return connection;
|
|
3206
|
+
}
|
|
3207
|
+
connections[index] = connection;
|
|
3208
|
+
return connection;
|
|
3209
|
+
}
|
|
3210
|
+
function removeCloudflareTrafficConnection(config, projectName) {
|
|
3211
|
+
const connections = config.cloudflareTraffic?.connections;
|
|
3212
|
+
if (!connections?.length) return false;
|
|
3213
|
+
const next = connections.filter((c) => c.projectName !== projectName);
|
|
3214
|
+
if (next.length === connections.length) return false;
|
|
3215
|
+
if (!config.cloudflareTraffic) return false;
|
|
3216
|
+
config.cloudflareTraffic.connections = next;
|
|
3217
|
+
if (next.length === 0) {
|
|
3218
|
+
delete config.cloudflareTraffic;
|
|
3219
|
+
}
|
|
3220
|
+
return true;
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3223
|
+
// src/cloudflare-ingest-url.ts
|
|
3224
|
+
function buildCloudflareIngestUrlTemplate(config) {
|
|
3225
|
+
const base = (config.publicUrl ?? config.apiUrl).replace(/\/+$/, "");
|
|
3226
|
+
return `${base}/api/v1/projects/{name}/traffic/cloudflare/ingest`;
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
// src/wordpress-config.ts
|
|
3230
|
+
function ensureConnections7(config) {
|
|
3172
3231
|
if (!config.wordpress) config.wordpress = {};
|
|
3173
3232
|
if (!config.wordpress.connections) config.wordpress.connections = [];
|
|
3174
3233
|
return config.wordpress.connections;
|
|
@@ -3185,7 +3244,7 @@ function getWordpressConnection(config, projectName) {
|
|
|
3185
3244
|
return (config.wordpress?.connections ?? []).find((connection) => connection.projectName === projectName);
|
|
3186
3245
|
}
|
|
3187
3246
|
function upsertWordpressConnection(config, connection) {
|
|
3188
|
-
const connections =
|
|
3247
|
+
const connections = ensureConnections7(config);
|
|
3189
3248
|
const normalized = normalizeConnection(connection);
|
|
3190
3249
|
const index = connections.findIndex((entry) => entry.projectName === connection.projectName);
|
|
3191
3250
|
if (index === -1) {
|
|
@@ -5290,7 +5349,7 @@ async function getAdGroupInsights(apiKey, adGroupId, opts) {
|
|
|
5290
5349
|
}
|
|
5291
5350
|
|
|
5292
5351
|
// src/ads-config.ts
|
|
5293
|
-
function
|
|
5352
|
+
function ensureConnections8(config) {
|
|
5294
5353
|
if (!config.openaiAds) config.openaiAds = {};
|
|
5295
5354
|
if (!config.openaiAds.connections) config.openaiAds.connections = [];
|
|
5296
5355
|
return config.openaiAds.connections;
|
|
@@ -5299,7 +5358,7 @@ function getOpenAiAdsConnection(config, projectName) {
|
|
|
5299
5358
|
return (config.openaiAds?.connections ?? []).find((c) => c.projectName === projectName);
|
|
5300
5359
|
}
|
|
5301
5360
|
function upsertOpenAiAdsConnection(config, connection) {
|
|
5302
|
-
const connections =
|
|
5361
|
+
const connections = ensureConnections8(config);
|
|
5303
5362
|
const index = connections.findIndex((c) => c.projectName === connection.projectName);
|
|
5304
5363
|
if (index === -1) {
|
|
5305
5364
|
connections.push(connection);
|
|
@@ -6945,7 +7004,6 @@ import { createRequire as createRequire3 } from "module";
|
|
|
6945
7004
|
import { Worker } from "worker_threads";
|
|
6946
7005
|
import { and as and10, asc, count, desc as desc6, eq as eq13, isNull, ne as ne2, or, sql as sql5 } from "drizzle-orm";
|
|
6947
7006
|
import { alias } from "drizzle-orm/sqlite-core";
|
|
6948
|
-
var SITE_CRAWL_GRAPH_LAYOUT_VERSION = "site-health-fa2-v1";
|
|
6949
7007
|
var SITE_CRAWL_GRAPH_LAYOUT_TIMEOUT_MS = 15e3;
|
|
6950
7008
|
var SITE_CRAWL_GRAPH_SEED_SCALE = 100;
|
|
6951
7009
|
var graphSourcePage = alias(siteCrawlPages, "layout_graph_source_page");
|
|
@@ -6955,20 +7013,81 @@ var FORCE_ATLAS_SETTINGS = Object.freeze({
|
|
|
6955
7013
|
barnesHutOptimize: true,
|
|
6956
7014
|
barnesHutTheta: 0.5,
|
|
6957
7015
|
edgeWeightInfluence: 0.5,
|
|
6958
|
-
gravity:
|
|
6959
|
-
linLogMode:
|
|
6960
|
-
outboundAttractionDistribution:
|
|
6961
|
-
scalingRatio:
|
|
7016
|
+
gravity: 0.2,
|
|
7017
|
+
linLogMode: true,
|
|
7018
|
+
outboundAttractionDistribution: true,
|
|
7019
|
+
scalingRatio: 2,
|
|
6962
7020
|
slowDown: 10,
|
|
6963
7021
|
strongGravityMode: false
|
|
6964
7022
|
});
|
|
7023
|
+
var RENDER_MAX_NODE_SIZE = 14;
|
|
7024
|
+
var RENDER_HALF_EXTENT_PX = 420;
|
|
7025
|
+
var NODE_SIZE_EXTENT_FRACTION = RENDER_MAX_NODE_SIZE / RENDER_HALF_EXTENT_PX;
|
|
7026
|
+
var NOVERLAP_SETTINGS = Object.freeze({
|
|
7027
|
+
/** Half a node of clear space between two touching nodes. */
|
|
7028
|
+
marginFactor: 0.5,
|
|
7029
|
+
expansion: 1.1,
|
|
7030
|
+
ratio: 1,
|
|
7031
|
+
speed: 3,
|
|
7032
|
+
/**
|
|
7033
|
+
* Node discs may claim at most this share of the graph's bounding box. At
|
|
7034
|
+
* the 20k cap the extent-derived size asks for spacing the graph physically
|
|
7035
|
+
* does not have, and noverlap either runs for minutes or overflows its
|
|
7036
|
+
* collision set. This bound keeps the pass solvable at every graph size,
|
|
7037
|
+
* and it only binds on graphs far too dense to read node-by-node anyway.
|
|
7038
|
+
*/
|
|
7039
|
+
maxPackingFraction: 0.15,
|
|
7040
|
+
/** Cells hold a handful of nodes each, which is what keeps the pass linear. */
|
|
7041
|
+
gridSizeMin: 10,
|
|
7042
|
+
gridSizeMax: 200,
|
|
7043
|
+
iterationLadder: [
|
|
7044
|
+
{ maxNodes: 1e3, iterations: 120 },
|
|
7045
|
+
{ maxNodes: 5e3, iterations: 60 }
|
|
7046
|
+
],
|
|
7047
|
+
iterationsFloor: 20
|
|
7048
|
+
});
|
|
7049
|
+
function noverlapGridSize(nodeCount) {
|
|
7050
|
+
return Math.max(
|
|
7051
|
+
NOVERLAP_SETTINGS.gridSizeMin,
|
|
7052
|
+
Math.min(NOVERLAP_SETTINGS.gridSizeMax, Math.ceil(Math.sqrt(Math.max(1, nodeCount))))
|
|
7053
|
+
);
|
|
7054
|
+
}
|
|
7055
|
+
function noverlapMaxIterations(nodeCount) {
|
|
7056
|
+
for (const step of NOVERLAP_SETTINGS.iterationLadder) {
|
|
7057
|
+
if (nodeCount <= step.maxNodes) return step.iterations;
|
|
7058
|
+
}
|
|
7059
|
+
return NOVERLAP_SETTINGS.iterationsFloor;
|
|
7060
|
+
}
|
|
7061
|
+
var OUTLIER_RING_INNER_FACTOR = 1.25;
|
|
7062
|
+
var OUTLIER_RING_OUTER_FACTOR = 1.75;
|
|
7063
|
+
var OUTLIER_RING_MAX_RINGS = 4;
|
|
7064
|
+
var OUTLIER_RING_TARGET_PER_RING = 16;
|
|
7065
|
+
var OUTLIER_RING_MIN_RADIUS = 1;
|
|
7066
|
+
function siteCrawlGraphLayoutSettingsFingerprint(settings) {
|
|
7067
|
+
return crypto16.createHash("sha256").update(JSON.stringify(settings)).digest("hex").slice(0, 8);
|
|
7068
|
+
}
|
|
7069
|
+
var SITE_CRAWL_GRAPH_LAYOUT_ALGORITHM = "site-health-fa2-v2";
|
|
7070
|
+
var SITE_CRAWL_GRAPH_LAYOUT_VERSION = `${SITE_CRAWL_GRAPH_LAYOUT_ALGORITHM}-${siteCrawlGraphLayoutSettingsFingerprint({
|
|
7071
|
+
forceAtlas: FORCE_ATLAS_SETTINGS,
|
|
7072
|
+
noverlap: NOVERLAP_SETTINGS,
|
|
7073
|
+
nodeSizeExtentFraction: NODE_SIZE_EXTENT_FRACTION,
|
|
7074
|
+
outlierRing: {
|
|
7075
|
+
inner: OUTLIER_RING_INNER_FACTOR,
|
|
7076
|
+
outer: OUTLIER_RING_OUTER_FACTOR,
|
|
7077
|
+
maxRings: OUTLIER_RING_MAX_RINGS,
|
|
7078
|
+
targetPerRing: OUTLIER_RING_TARGET_PER_RING,
|
|
7079
|
+
minRadius: OUTLIER_RING_MIN_RADIUS
|
|
7080
|
+
}
|
|
7081
|
+
})}`;
|
|
6965
7082
|
var WORKER_SOURCE = String.raw`
|
|
6966
7083
|
const {parentPort, workerData} = require('node:worker_threads');
|
|
6967
7084
|
try {
|
|
6968
7085
|
const graphologyModule = require(workerData.graphologyPath);
|
|
6969
7086
|
const forceAtlasModule = require(workerData.forceAtlasPath);
|
|
7087
|
+
const noverlapModule = require(workerData.noverlapPath);
|
|
6970
7088
|
const MultiDirectedGraph = graphologyModule.MultiDirectedGraph;
|
|
6971
7089
|
const forceAtlas2 = forceAtlasModule.default || forceAtlasModule;
|
|
7090
|
+
const noverlap = noverlapModule.default || noverlapModule;
|
|
6972
7091
|
const graph = new MultiDirectedGraph({allowSelfLoops: true});
|
|
6973
7092
|
for (const node of workerData.input.nodes) {
|
|
6974
7093
|
graph.addNode(node.nodeKey, {x: node.x, y: node.y});
|
|
@@ -6984,6 +7103,45 @@ try {
|
|
|
6984
7103
|
settings: workerData.settings,
|
|
6985
7104
|
});
|
|
6986
7105
|
}
|
|
7106
|
+
// Anti-overlap runs on the spatialization ForceAtlas2 just produced. Node
|
|
7107
|
+
// size is uniform and derived from the graph's own extent, so the pass is
|
|
7108
|
+
// scale invariant and stays deterministic for a fixed input.
|
|
7109
|
+
if (graph.order > 1) {
|
|
7110
|
+
let sumX = 0;
|
|
7111
|
+
let sumY = 0;
|
|
7112
|
+
graph.forEachNode(function (nodeKey, attributes) {
|
|
7113
|
+
sumX += attributes.x;
|
|
7114
|
+
sumY += attributes.y;
|
|
7115
|
+
});
|
|
7116
|
+
const centerX = sumX / graph.order;
|
|
7117
|
+
const centerY = sumY / graph.order;
|
|
7118
|
+
let extent = 0;
|
|
7119
|
+
graph.forEachNode(function (nodeKey, attributes) {
|
|
7120
|
+
extent = Math.max(extent, Math.abs(attributes.x - centerX), Math.abs(attributes.y - centerY));
|
|
7121
|
+
});
|
|
7122
|
+
if (Number.isFinite(extent) && extent > 0) {
|
|
7123
|
+
// Whichever bound is tighter: one render-sized node, or the largest disc
|
|
7124
|
+
// that still leaves the graph mostly empty space at this node count.
|
|
7125
|
+
const byExtent = extent * workerData.nodeSizeExtentFraction;
|
|
7126
|
+
const byArea = Math.sqrt(
|
|
7127
|
+
(workerData.noverlap.maxPackingFraction * Math.pow(2 * extent, 2)) / graph.order,
|
|
7128
|
+
) / 2;
|
|
7129
|
+
const nodeSize = Math.min(byExtent, byArea);
|
|
7130
|
+
graph.forEachNode(function (nodeKey) {
|
|
7131
|
+
graph.setNodeAttribute(nodeKey, 'size', nodeSize);
|
|
7132
|
+
});
|
|
7133
|
+
noverlap.assign(graph, {
|
|
7134
|
+
maxIterations: workerData.noverlap.maxIterations,
|
|
7135
|
+
settings: {
|
|
7136
|
+
gridSize: workerData.noverlap.gridSize,
|
|
7137
|
+
margin: nodeSize * workerData.noverlap.marginFactor,
|
|
7138
|
+
expansion: workerData.noverlap.expansion,
|
|
7139
|
+
ratio: workerData.noverlap.ratio,
|
|
7140
|
+
speed: workerData.noverlap.speed,
|
|
7141
|
+
},
|
|
7142
|
+
});
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
6987
7145
|
const positions = [];
|
|
6988
7146
|
graph.forEachNode((nodeKey, attributes) => {
|
|
6989
7147
|
positions.push({nodeKey, x: attributes.x, y: attributes.y});
|
|
@@ -7054,8 +7212,15 @@ async function computeForceAtlasPositions(input, options = {}) {
|
|
|
7054
7212
|
workerData: {
|
|
7055
7213
|
graphologyPath: requireFromPackage.resolve("graphology"),
|
|
7056
7214
|
forceAtlasPath: requireFromPackage.resolve("graphology-layout-forceatlas2"),
|
|
7215
|
+
noverlapPath: requireFromPackage.resolve("graphology-layout-noverlap"),
|
|
7057
7216
|
input,
|
|
7058
|
-
settings: FORCE_ATLAS_SETTINGS
|
|
7217
|
+
settings: FORCE_ATLAS_SETTINGS,
|
|
7218
|
+
noverlap: {
|
|
7219
|
+
...NOVERLAP_SETTINGS,
|
|
7220
|
+
gridSize: noverlapGridSize(input.nodes.length),
|
|
7221
|
+
maxIterations: noverlapMaxIterations(input.nodes.length)
|
|
7222
|
+
},
|
|
7223
|
+
nodeSizeExtentFraction: NODE_SIZE_EXTENT_FRACTION
|
|
7059
7224
|
}
|
|
7060
7225
|
});
|
|
7061
7226
|
const timeoutMs = options.timeoutMs ?? SITE_CRAWL_GRAPH_LAYOUT_TIMEOUT_MS;
|
|
@@ -7084,6 +7249,77 @@ async function computeForceAtlasPositions(input, options = {}) {
|
|
|
7084
7249
|
});
|
|
7085
7250
|
});
|
|
7086
7251
|
}
|
|
7252
|
+
function siteCrawlGraphComponents(nodeKeys, edges) {
|
|
7253
|
+
const parent = new Map(nodeKeys.map((key) => [key, key]));
|
|
7254
|
+
const find = (key) => {
|
|
7255
|
+
let root = key;
|
|
7256
|
+
while (parent.get(root) !== root) root = parent.get(root);
|
|
7257
|
+
let cursor = key;
|
|
7258
|
+
while (parent.get(cursor) !== root) {
|
|
7259
|
+
const next = parent.get(cursor);
|
|
7260
|
+
parent.set(cursor, root);
|
|
7261
|
+
cursor = next;
|
|
7262
|
+
}
|
|
7263
|
+
return root;
|
|
7264
|
+
};
|
|
7265
|
+
for (const edge of edges) {
|
|
7266
|
+
if (!parent.has(edge.sourceNodeKey) || !parent.has(edge.targetNodeKey)) continue;
|
|
7267
|
+
const sourceRoot = find(edge.sourceNodeKey);
|
|
7268
|
+
const targetRoot = find(edge.targetNodeKey);
|
|
7269
|
+
if (sourceRoot === targetRoot) continue;
|
|
7270
|
+
if (sourceRoot < targetRoot) parent.set(targetRoot, sourceRoot);
|
|
7271
|
+
else parent.set(sourceRoot, targetRoot);
|
|
7272
|
+
}
|
|
7273
|
+
const members = /* @__PURE__ */ new Map();
|
|
7274
|
+
for (const key of [...nodeKeys].sort()) {
|
|
7275
|
+
const root = find(key);
|
|
7276
|
+
const group = members.get(root);
|
|
7277
|
+
if (group) group.push(key);
|
|
7278
|
+
else members.set(root, [key]);
|
|
7279
|
+
}
|
|
7280
|
+
let mainRoot = null;
|
|
7281
|
+
for (const [root, group] of members) {
|
|
7282
|
+
if (mainRoot === null) {
|
|
7283
|
+
mainRoot = root;
|
|
7284
|
+
continue;
|
|
7285
|
+
}
|
|
7286
|
+
const best = members.get(mainRoot);
|
|
7287
|
+
if (group.length > best.length || group.length === best.length && root < mainRoot) mainRoot = root;
|
|
7288
|
+
}
|
|
7289
|
+
const main = new Set(mainRoot === null ? [] : members.get(mainRoot));
|
|
7290
|
+
const outliers = [...members.entries()].filter(([root]) => root !== mainRoot).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).flatMap(([, group]) => group);
|
|
7291
|
+
return { main, outliers };
|
|
7292
|
+
}
|
|
7293
|
+
function frameDisconnectedNodes(positions, edges) {
|
|
7294
|
+
const { main, outliers } = siteCrawlGraphComponents(positions.map((p) => p.nodeKey), edges);
|
|
7295
|
+
if (outliers.length === 0 || main.size === 0) return [...positions];
|
|
7296
|
+
const mainPositions = positions.filter((position) => main.has(position.nodeKey));
|
|
7297
|
+
const centerX = mainPositions.reduce((sum, p) => sum + p.x, 0) / mainPositions.length;
|
|
7298
|
+
const centerY = mainPositions.reduce((sum, p) => sum + p.y, 0) / mainPositions.length;
|
|
7299
|
+
if (!Number.isFinite(centerX) || !Number.isFinite(centerY)) return [...positions];
|
|
7300
|
+
const mainRadius = Math.max(
|
|
7301
|
+
OUTLIER_RING_MIN_RADIUS,
|
|
7302
|
+
...mainPositions.map((p) => Math.hypot(p.x - centerX, p.y - centerY)).filter(Number.isFinite)
|
|
7303
|
+
);
|
|
7304
|
+
const ringCount = Math.max(1, Math.min(
|
|
7305
|
+
OUTLIER_RING_MAX_RINGS,
|
|
7306
|
+
Math.ceil(outliers.length / OUTLIER_RING_TARGET_PER_RING)
|
|
7307
|
+
));
|
|
7308
|
+
const placed = /* @__PURE__ */ new Map();
|
|
7309
|
+
for (const [index, nodeKey] of outliers.entries()) {
|
|
7310
|
+
const ring = index % ringCount;
|
|
7311
|
+
const ringRadius = mainRadius * (ringCount === 1 ? OUTLIER_RING_INNER_FACTOR : OUTLIER_RING_INNER_FACTOR + (OUTLIER_RING_OUTER_FACTOR - OUTLIER_RING_INNER_FACTOR) * (ring / (ringCount - 1)));
|
|
7312
|
+
const slot = Math.floor(index / ringCount);
|
|
7313
|
+
const slotsOnRing = Math.ceil((outliers.length - ring) / ringCount);
|
|
7314
|
+
const angle = (slot + ring % 2 * 0.5) / Math.max(1, slotsOnRing) * Math.PI * 2;
|
|
7315
|
+
placed.set(nodeKey, {
|
|
7316
|
+
nodeKey,
|
|
7317
|
+
x: centerX + Math.cos(angle) * ringRadius,
|
|
7318
|
+
y: centerY + Math.sin(angle) * ringRadius
|
|
7319
|
+
});
|
|
7320
|
+
}
|
|
7321
|
+
return positions.map((position) => placed.get(position.nodeKey) ?? position);
|
|
7322
|
+
}
|
|
7087
7323
|
function normalizePositions(seededNodes, computed, rootNodeKey) {
|
|
7088
7324
|
const byKey = new Map(computed.map((position) => [position.nodeKey, position]));
|
|
7089
7325
|
if (byKey.size !== seededNodes.length) throw new Error("Graph layout returned an incomplete node set");
|
|
@@ -7131,6 +7367,7 @@ async function layoutSiteCrawlGraphInput(input, options = {}) {
|
|
|
7131
7367
|
iterations: adaptiveForceAtlasIterations(input.nodes.length, input.edges.length)
|
|
7132
7368
|
}, { signal: options.signal, timeoutMs: options.timeoutMs });
|
|
7133
7369
|
options.signal?.throwIfAborted();
|
|
7370
|
+
const framed = frameDisconnectedNodes(positions, input.edges);
|
|
7134
7371
|
return {
|
|
7135
7372
|
state: "ready",
|
|
7136
7373
|
layoutVersion: SITE_CRAWL_GRAPH_LAYOUT_VERSION,
|
|
@@ -7138,7 +7375,7 @@ async function layoutSiteCrawlGraphInput(input, options = {}) {
|
|
|
7138
7375
|
totalEdges: input.totalEdges,
|
|
7139
7376
|
nodeCount: input.nodes.length,
|
|
7140
7377
|
edgeCount: input.edges.length,
|
|
7141
|
-
nodes: normalizePositions(seeded,
|
|
7378
|
+
nodes: normalizePositions(seeded, framed, input.rootNodeKey),
|
|
7142
7379
|
edges: input.edges.map((edge, sampleRank) => ({ ...edge, sampleRank }))
|
|
7143
7380
|
};
|
|
7144
7381
|
} catch (error) {
|
|
@@ -8413,7 +8650,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
8413
8650
|
return result;
|
|
8414
8651
|
}
|
|
8415
8652
|
async function backfillInsightsCommand(project, opts) {
|
|
8416
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
8653
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-LQ6ZRWMF.js");
|
|
8417
8654
|
const config = loadConfig();
|
|
8418
8655
|
const db = createClient(config.database);
|
|
8419
8656
|
migrate(db);
|
|
@@ -14043,6 +14280,24 @@ async function createServer(opts) {
|
|
|
14043
14280
|
return removed;
|
|
14044
14281
|
}
|
|
14045
14282
|
};
|
|
14283
|
+
const cloudflareTrafficCredentialStore = {
|
|
14284
|
+
getConnection: (projectName) => {
|
|
14285
|
+
return getCloudflareTrafficConnection(opts.config, projectName);
|
|
14286
|
+
},
|
|
14287
|
+
getConnectionBySourceId: (sourceId) => {
|
|
14288
|
+
return getCloudflareTrafficConnectionBySourceId(opts.config, sourceId);
|
|
14289
|
+
},
|
|
14290
|
+
upsertConnection: (record) => {
|
|
14291
|
+
const updated = upsertCloudflareTrafficConnection(opts.config, record);
|
|
14292
|
+
saveConfigPatch(opts.config);
|
|
14293
|
+
return updated;
|
|
14294
|
+
},
|
|
14295
|
+
deleteConnection: (projectName) => {
|
|
14296
|
+
const removed = removeCloudflareTrafficConnection(opts.config, projectName);
|
|
14297
|
+
if (removed) saveConfigPatch(opts.config);
|
|
14298
|
+
return removed;
|
|
14299
|
+
}
|
|
14300
|
+
};
|
|
14046
14301
|
const googleStateSecret = process.env.GOOGLE_STATE_SECRET ?? crypto25.randomBytes(32).toString("hex");
|
|
14047
14302
|
const googleConnectionStore = {
|
|
14048
14303
|
listConnections: (domain) => listGoogleConnections(opts.config, domain),
|
|
@@ -14652,6 +14907,8 @@ async function createServer(opts) {
|
|
|
14652
14907
|
cloudRunCredentialStore,
|
|
14653
14908
|
wordpressTrafficCredentialStore,
|
|
14654
14909
|
vercelTrafficCredentialStore,
|
|
14910
|
+
cloudflareTrafficCredentialStore,
|
|
14911
|
+
cloudflareTrafficIngestUrl: buildCloudflareIngestUrlTemplate(opts.config),
|
|
14655
14912
|
onTrafficSynced: (event) => {
|
|
14656
14913
|
trackEvent(
|
|
14657
14914
|
"traffic.synced",
|
|
@@ -15090,6 +15347,7 @@ export {
|
|
|
15090
15347
|
backfillInsightsCommand,
|
|
15091
15348
|
backfillSnapshotAttributionCommand,
|
|
15092
15349
|
backfillTrafficClassificationCommand,
|
|
15350
|
+
getCloudflareTrafficConnection,
|
|
15093
15351
|
setGoogleAuthConfig,
|
|
15094
15352
|
installSkills,
|
|
15095
15353
|
listSkills,
|
|
@@ -8561,7 +8561,24 @@ var siteCrawlSummarySchema = z24.object({
|
|
|
8561
8561
|
counts: siteCrawlCountsSchema,
|
|
8562
8562
|
deadLinks: siteCrawlDeadLinksSummarySchema
|
|
8563
8563
|
});
|
|
8564
|
+
var siteCrawlTerminationSchema = z24.enum([
|
|
8565
|
+
"complete",
|
|
8566
|
+
"unknown",
|
|
8567
|
+
"max-pages",
|
|
8568
|
+
"max-edges",
|
|
8569
|
+
"max-fetches",
|
|
8570
|
+
"max-duration",
|
|
8571
|
+
"max-bytes",
|
|
8572
|
+
"max-page-bytes",
|
|
8573
|
+
"max-depth",
|
|
8574
|
+
"max-links-per-page",
|
|
8575
|
+
"max-query-variants",
|
|
8576
|
+
"max-sitemap-fanout",
|
|
8577
|
+
"max-sitemap-urls",
|
|
8578
|
+
"root-host-redirect"
|
|
8579
|
+
]);
|
|
8564
8580
|
var siteHealthStateSchema = z24.enum(["eligible", "hidden", "failed", "unchecked"]);
|
|
8581
|
+
var SiteHealthStates = siteHealthStateSchema.enum;
|
|
8565
8582
|
var SiteCrawlFetchStates = {
|
|
8566
8583
|
discovered: "discovered",
|
|
8567
8584
|
robotsBlocked: "robots-blocked",
|
|
@@ -8762,6 +8779,13 @@ var siteCrawlGraphEdgeSchema = z24.object({
|
|
|
8762
8779
|
followable: z24.boolean(),
|
|
8763
8780
|
occurrences: z24.number().int().positive()
|
|
8764
8781
|
});
|
|
8782
|
+
var siteCrawlGraphLayoutUnavailableReasonSchema = z24.enum([
|
|
8783
|
+
"no-crawl",
|
|
8784
|
+
"legacy-snapshot",
|
|
8785
|
+
"details-unavailable",
|
|
8786
|
+
"layout-failed",
|
|
8787
|
+
"empty-crawl"
|
|
8788
|
+
]);
|
|
8765
8789
|
var siteCrawlGraphLayoutSchema = z24.discriminatedUnion("state", [
|
|
8766
8790
|
z24.object({
|
|
8767
8791
|
state: z24.literal("ready"),
|
|
@@ -8771,13 +8795,20 @@ var siteCrawlGraphLayoutSchema = z24.discriminatedUnion("state", [
|
|
|
8771
8795
|
z24.object({
|
|
8772
8796
|
state: z24.literal("unavailable"),
|
|
8773
8797
|
version: z24.null(),
|
|
8774
|
-
reason:
|
|
8798
|
+
reason: siteCrawlGraphLayoutUnavailableReasonSchema
|
|
8775
8799
|
})
|
|
8776
8800
|
]);
|
|
8777
8801
|
var siteCrawlGraphResponseSchema = z24.object({
|
|
8778
8802
|
project: z24.string(),
|
|
8779
8803
|
hasCrawlData: z24.boolean(),
|
|
8780
8804
|
runId: z24.string().nullable(),
|
|
8805
|
+
/**
|
|
8806
|
+
* Server-owned identity of the crawl root, resolved from the snapshot root
|
|
8807
|
+
* URL (or the depth-0 page). Consumers must not infer the home page from a
|
|
8808
|
+
* path or a link score. It is null when the crawl persisted no page detail,
|
|
8809
|
+
* and it can name a page that graph sampling left out of `nodes`.
|
|
8810
|
+
*/
|
|
8811
|
+
rootNodeKey: z24.string().nullable(),
|
|
8781
8812
|
layout: siteCrawlGraphLayoutSchema,
|
|
8782
8813
|
/** Total canonical pages in the persisted crawl before graph sampling. */
|
|
8783
8814
|
totalNodes: z24.number().int().nonnegative(),
|
|
@@ -9020,6 +9051,20 @@ var siteAuditRunResponseSchema = z24.object({
|
|
|
9020
9051
|
runId: z24.string(),
|
|
9021
9052
|
status: runStatusSchema
|
|
9022
9053
|
});
|
|
9054
|
+
var siteHealthScanSchema = z24.object({
|
|
9055
|
+
runId: z24.string(),
|
|
9056
|
+
status: runStatusSchema,
|
|
9057
|
+
createdAt: z24.string(),
|
|
9058
|
+
startedAt: z24.string().nullable(),
|
|
9059
|
+
finishedAt: z24.string().nullable(),
|
|
9060
|
+
hasCrawlData: z24.boolean()
|
|
9061
|
+
});
|
|
9062
|
+
var siteHealthScansResponseSchema = z24.object({
|
|
9063
|
+
project: z24.string(),
|
|
9064
|
+
scans: z24.array(siteHealthScanSchema).default([])
|
|
9065
|
+
});
|
|
9066
|
+
var SITE_HEALTH_SCANS_DEFAULT_LIMIT = 20;
|
|
9067
|
+
var SITE_HEALTH_SCANS_MAX_LIMIT = 100;
|
|
9023
9068
|
|
|
9024
9069
|
// ../contracts/src/schedule.ts
|
|
9025
9070
|
import { z as z25 } from "zod";
|
|
@@ -12745,6 +12790,71 @@ var trafficConnectVercelRequestSchema = z44.object({
|
|
|
12745
12790
|
environment: vercelTrafficEnvironmentSchema.optional(),
|
|
12746
12791
|
displayName: z44.string().min(1).optional()
|
|
12747
12792
|
});
|
|
12793
|
+
var cloudflareTrafficDeliveryModeSchema = z44.enum(["direct-push", "queue-pull"]);
|
|
12794
|
+
var CloudflareTrafficDeliveryModes = cloudflareTrafficDeliveryModeSchema.enum;
|
|
12795
|
+
var cloudflareTrafficSourceConfigSchema = z44.object({
|
|
12796
|
+
schemaVersion: z44.literal(1),
|
|
12797
|
+
deliveryMode: z44.literal(CloudflareTrafficDeliveryModes["direct-push"]).default("direct-push"),
|
|
12798
|
+
/** Semver of the Worker script bundle that was generated at connect/rotate time. */
|
|
12799
|
+
workerVersion: z44.string().min(1),
|
|
12800
|
+
/** Identifier of the bot/referer keyword set baked into the deployed Worker. */
|
|
12801
|
+
expectedBotListVersion: z44.string().min(1),
|
|
12802
|
+
/** Target Cloudflare zone for the operator-managed Worker route. */
|
|
12803
|
+
zoneId: z44.string().nullable(),
|
|
12804
|
+
/** Cloudflare account id used to target the correct account during Wrangler deploy. */
|
|
12805
|
+
accountId: z44.string().nullable()
|
|
12806
|
+
});
|
|
12807
|
+
var trafficConnectCloudflareRequestSchema = z44.object({
|
|
12808
|
+
deliveryMode: z44.literal(CloudflareTrafficDeliveryModes["direct-push"]).default("direct-push"),
|
|
12809
|
+
displayName: z44.string().min(1).optional(),
|
|
12810
|
+
/** Target Cloudflare zone. Canonry does not validate it against Cloudflare. */
|
|
12811
|
+
zoneId: z44.string().min(1).optional(),
|
|
12812
|
+
/** Cloudflare account id. Set it when Wrangler can access more than one account. */
|
|
12813
|
+
accountId: z44.string().min(1).optional()
|
|
12814
|
+
});
|
|
12815
|
+
var trafficConnectCloudflareResponseSchema = z44.object({
|
|
12816
|
+
sourceId: z44.string().min(1),
|
|
12817
|
+
deliveryMode: z44.literal(CloudflareTrafficDeliveryModes["direct-push"]),
|
|
12818
|
+
workerScript: z44.string().min(1),
|
|
12819
|
+
wranglerToml: z44.string().min(1),
|
|
12820
|
+
workerVersion: z44.string().min(1),
|
|
12821
|
+
instructions: z44.string().min(1)
|
|
12822
|
+
});
|
|
12823
|
+
var cloudflareEdgeEventSchema = z44.object({
|
|
12824
|
+
/** Cloudflare `cf-ray` request id — globally unique per request. */
|
|
12825
|
+
eventId: z44.string().min(1),
|
|
12826
|
+
observedAt: z44.string().datetime(),
|
|
12827
|
+
method: z44.string().nullable(),
|
|
12828
|
+
host: z44.string().nullable(),
|
|
12829
|
+
path: z44.string().min(1),
|
|
12830
|
+
queryString: z44.string().nullable(),
|
|
12831
|
+
status: z44.number().int().nullable(),
|
|
12832
|
+
userAgent: z44.string().nullable(),
|
|
12833
|
+
remoteIp: z44.string().nullable(),
|
|
12834
|
+
referer: z44.string().nullable(),
|
|
12835
|
+
cf: z44.object({
|
|
12836
|
+
verifiedBot: z44.boolean().nullable(),
|
|
12837
|
+
botScore: z44.number().int().nullable(),
|
|
12838
|
+
country: z44.string().nullable(),
|
|
12839
|
+
asn: z44.number().int().nullable(),
|
|
12840
|
+
asOrganization: z44.string().nullable()
|
|
12841
|
+
}).nullable()
|
|
12842
|
+
});
|
|
12843
|
+
var cloudflareEdgeEventBatchSchema = z44.object({
|
|
12844
|
+
schemaVersion: z44.literal(1),
|
|
12845
|
+
workerVersion: z44.string().min(1),
|
|
12846
|
+
events: z44.array(cloudflareEdgeEventSchema).min(1).max(100)
|
|
12847
|
+
});
|
|
12848
|
+
var cloudflareWorkerIngestRequestSchema = cloudflareEdgeEventBatchSchema;
|
|
12849
|
+
var cloudflareWorkerIngestResponseSchema = z44.object({
|
|
12850
|
+
acceptedEvents: z44.number().int(),
|
|
12851
|
+
droppedEvents: z44.number().int(),
|
|
12852
|
+
workerVersionAck: z44.string(),
|
|
12853
|
+
crawlerBucketRows: z44.number().int(),
|
|
12854
|
+
aiUserFetchBucketRows: z44.number().int(),
|
|
12855
|
+
aiReferralBucketRows: z44.number().int(),
|
|
12856
|
+
sampleRows: z44.number().int()
|
|
12857
|
+
});
|
|
12748
12858
|
var trafficSyncResponseSchema = z44.object({
|
|
12749
12859
|
sourceId: z44.string(),
|
|
12750
12860
|
runId: z44.string(),
|
|
@@ -14634,6 +14744,7 @@ export {
|
|
|
14634
14744
|
siteAuditPagesResponseSchema,
|
|
14635
14745
|
siteAuditTrendResponseSchema,
|
|
14636
14746
|
siteCrawlSummarySchema,
|
|
14747
|
+
siteHealthStateSchema,
|
|
14637
14748
|
SiteCrawlFetchedStates,
|
|
14638
14749
|
deriveSiteHealthState,
|
|
14639
14750
|
siteCrawlAuditFactorSchema,
|
|
@@ -14669,6 +14780,9 @@ export {
|
|
|
14669
14780
|
normalizeSiteAuditRunRequest,
|
|
14670
14781
|
siteAuditRequestIdentity,
|
|
14671
14782
|
siteAuditRunResponseSchema,
|
|
14783
|
+
siteHealthScansResponseSchema,
|
|
14784
|
+
SITE_HEALTH_SCANS_DEFAULT_LIMIT,
|
|
14785
|
+
SITE_HEALTH_SCANS_MAX_LIMIT,
|
|
14672
14786
|
schedulableRunKindSchema,
|
|
14673
14787
|
SchedulableRunKinds,
|
|
14674
14788
|
scheduleDtoSchema,
|
|
@@ -14812,6 +14926,12 @@ export {
|
|
|
14812
14926
|
trafficConnectCloudRunRequestSchema,
|
|
14813
14927
|
trafficConnectWordpressRequestSchema,
|
|
14814
14928
|
trafficConnectVercelRequestSchema,
|
|
14929
|
+
CloudflareTrafficDeliveryModes,
|
|
14930
|
+
cloudflareTrafficSourceConfigSchema,
|
|
14931
|
+
trafficConnectCloudflareRequestSchema,
|
|
14932
|
+
trafficConnectCloudflareResponseSchema,
|
|
14933
|
+
cloudflareWorkerIngestRequestSchema,
|
|
14934
|
+
cloudflareWorkerIngestResponseSchema,
|
|
14815
14935
|
trafficSyncResponseSchema,
|
|
14816
14936
|
trafficResetRequestSchema,
|
|
14817
14937
|
trafficBackfillResponseSchema,
|
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
trafficConnectWordpressRequestSchema,
|
|
82
82
|
trafficEventKindSchema,
|
|
83
83
|
trafficSeriesGranularitySchema
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-DEVAEC2S.js";
|
|
85
85
|
|
|
86
86
|
// src/config.ts
|
|
87
87
|
import fs from "fs";
|
|
@@ -107,6 +107,14 @@ function normalizeWordpressConfig(config) {
|
|
|
107
107
|
defaultEnv: connection.defaultEnv ?? "live"
|
|
108
108
|
}));
|
|
109
109
|
}
|
|
110
|
+
function normalizeCloudflareTrafficConfig(config) {
|
|
111
|
+
for (const connection of config.cloudflareTraffic?.connections ?? []) {
|
|
112
|
+
const legacy = connection;
|
|
113
|
+
if (legacy.deliveryMode === void 0) {
|
|
114
|
+
legacy.deliveryMode = "direct-push";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
110
118
|
function getConfigDir() {
|
|
111
119
|
const override = process.env.CANONRY_CONFIG_DIR?.trim();
|
|
112
120
|
if (override) {
|
|
@@ -152,6 +160,7 @@ Do not write config.yaml by hand; use "canonry init", "canonry settings", or "ca
|
|
|
152
160
|
}
|
|
153
161
|
normalizeGoogleConfig(parsed);
|
|
154
162
|
normalizeWordpressConfig(parsed);
|
|
163
|
+
normalizeCloudflareTrafficConfig(parsed);
|
|
155
164
|
const portOverride = process.env.CANONRY_PORT?.trim();
|
|
156
165
|
if (portOverride) {
|
|
157
166
|
try {
|
|
@@ -4695,6 +4704,22 @@ var postApiV1ProjectsByNameTrafficConnectVercel = (options) => {
|
|
|
4695
4704
|
}
|
|
4696
4705
|
});
|
|
4697
4706
|
};
|
|
4707
|
+
var postApiV1ProjectsByNameTrafficConnectCloudflare = (options) => {
|
|
4708
|
+
return (options.client ?? client).post({
|
|
4709
|
+
security: [
|
|
4710
|
+
{
|
|
4711
|
+
scheme: "bearer",
|
|
4712
|
+
type: "http"
|
|
4713
|
+
}
|
|
4714
|
+
],
|
|
4715
|
+
url: "/api/v1/projects/{name}/traffic/connect/cloudflare",
|
|
4716
|
+
...options,
|
|
4717
|
+
headers: {
|
|
4718
|
+
"Content-Type": "application/json",
|
|
4719
|
+
...options.headers
|
|
4720
|
+
}
|
|
4721
|
+
});
|
|
4722
|
+
};
|
|
4698
4723
|
var postApiV1ProjectsByNameTrafficSourcesByIdSync = (options) => {
|
|
4699
4724
|
return (options.client ?? client).post({
|
|
4700
4725
|
security: [
|
|
@@ -6982,6 +7007,15 @@ var ApiClient = class {
|
|
|
6982
7007
|
})
|
|
6983
7008
|
);
|
|
6984
7009
|
}
|
|
7010
|
+
async trafficConnectCloudflare(project, body) {
|
|
7011
|
+
return this.invoke(
|
|
7012
|
+
() => postApiV1ProjectsByNameTrafficConnectCloudflare({
|
|
7013
|
+
client: this.heyClient,
|
|
7014
|
+
path: { name: project },
|
|
7015
|
+
body
|
|
7016
|
+
})
|
|
7017
|
+
);
|
|
7018
|
+
}
|
|
6985
7019
|
async trafficSync(project, sourceId, body) {
|
|
6986
7020
|
return this.invoke(
|
|
6987
7021
|
() => postApiV1ProjectsByNameTrafficSourcesByIdSync({
|