@ainyc/canonry 5.0.1 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/assets/{AuditHistoryPanel-9F_74jKS.js → AuditHistoryPanel-Dc_pymFg.js} +1 -1
- package/assets/assets/{BacklinksPage-DmF2rbwy.js → BacklinksPage-BkSo48YX.js} +1 -1
- package/assets/assets/{HistoryPage-DKopwAjo.js → HistoryPage-BbswsdU7.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-Dpd_DYQv.js → MeasurementPropertyPage-BbgtalkM.js} +1 -1
- package/assets/assets/{ProjectPage-b1-57SzP.js → ProjectPage-CAcRQp-T.js} +1 -1
- package/assets/assets/{RunRow-BjrSRp6X.js → RunRow-2cNW8FHz.js} +1 -1
- package/assets/assets/{RunsPage-BFrymZ7D.js → RunsPage-Cz3lJ6VJ.js} +1 -1
- package/assets/assets/{SettingsPage-DdVa6pLK.js → SettingsPage-BQwB0r9k.js} +1 -1
- package/assets/assets/{SiteHealthSection-CizmiBjA.js → SiteHealthSection-C0aJ_UhN.js} +3 -3
- package/assets/assets/{TrafficPage-N7sZgb9x.js → TrafficPage-DTic3zhO.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BNQmMJ5D.js → TrafficSourceDetailPage-BKfabnCw.js} +1 -1
- package/assets/assets/{extract-error-message-Bh9mRIz9.js → extract-error-message-BzB8j3fC.js} +1 -1
- package/assets/assets/{index-BnR1Tu9j.js → index-DQGEzxrN.js} +3 -3
- package/assets/assets/{react-sigma_core.esm.min-DGaYwjLZ.js → react-sigma_core.esm.min-rYv0aKKx.js} +1 -1
- package/assets/assets/{v2-overview-adapter-eqN-8OK5.js → v2-overview-adapter-CWWPfsm-.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-MEQGGODM.js → chunk-5K2NUQOV.js} +34 -5
- package/dist/{chunk-3DL3KV6C.js → chunk-5ZIUROAQ.js} +48 -0
- package/dist/{chunk-GMNZNLVX.js → chunk-BS4ZC7FG.js} +258 -27
- package/dist/{chunk-66KGNC4U.js → chunk-NKEKDWGX.js} +2 -2
- package/dist/{chunk-LHHJVGH7.js → chunk-WSYJ4IT7.js} +43 -15
- package/dist/cli.js +17 -15
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-7XX5ZRYE.js → intelligence-service-D2HY2S5F.js} +2 -2
- package/dist/mcp.js +5 -4
- package/package.json +11 -11
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
loadConfig,
|
|
16
16
|
loadConfigRaw,
|
|
17
17
|
resolveOperatorApiKeyIds,
|
|
18
|
+
runWithUsageTags,
|
|
18
19
|
saveConfigPatch
|
|
19
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-5K2NUQOV.js";
|
|
20
21
|
import {
|
|
21
22
|
CC_CACHE_DIR,
|
|
22
23
|
DUCKDB_SPEC,
|
|
@@ -167,9 +168,10 @@ import {
|
|
|
167
168
|
siteCrawlSnapshots,
|
|
168
169
|
toAlertView,
|
|
169
170
|
usageCounters
|
|
170
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-WSYJ4IT7.js";
|
|
171
172
|
import {
|
|
172
173
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
174
|
+
AGENT_NONE,
|
|
173
175
|
AGENT_PROVIDER_IDS,
|
|
174
176
|
AI_ENGINE_DOMAINS,
|
|
175
177
|
AI_ENGINE_SELF_DOMAINS,
|
|
@@ -247,6 +249,7 @@ import {
|
|
|
247
249
|
deriveIndexCoverage,
|
|
248
250
|
deriveSiteHealthState,
|
|
249
251
|
describeError,
|
|
252
|
+
detectAgentRuntime,
|
|
250
253
|
determineAnswerMentioned,
|
|
251
254
|
dollarsToMicros,
|
|
252
255
|
effectiveBrandNames,
|
|
@@ -283,6 +286,7 @@ import {
|
|
|
283
286
|
mapWithConcurrency,
|
|
284
287
|
maskTelemetryAnonymousId,
|
|
285
288
|
nextScheduleUpdatedAt,
|
|
289
|
+
normalizeAgentSlug,
|
|
286
290
|
normalizeIdTokens,
|
|
287
291
|
normalizeMeasurementExecutionQueryText,
|
|
288
292
|
normalizeServedModel,
|
|
@@ -309,10 +313,19 @@ import {
|
|
|
309
313
|
templateLinkUbiquityAvailable,
|
|
310
314
|
textContainsBrandAlias,
|
|
311
315
|
textContainsDomain,
|
|
316
|
+
usageSurfaceSchema,
|
|
312
317
|
validationError,
|
|
313
318
|
winnabilityClassLabel,
|
|
314
319
|
withRetry
|
|
315
|
-
} from "./chunk-
|
|
320
|
+
} from "./chunk-5ZIUROAQ.js";
|
|
321
|
+
|
|
322
|
+
// src/runtime-context.ts
|
|
323
|
+
function cliRuntimeContext(env = process.env, stdio = process) {
|
|
324
|
+
return {
|
|
325
|
+
agent: detectAgentRuntime(env),
|
|
326
|
+
interactive: Boolean(stdio.stdin.isTTY && stdio.stdout.isTTY)
|
|
327
|
+
};
|
|
328
|
+
}
|
|
316
329
|
|
|
317
330
|
// src/telemetry.ts
|
|
318
331
|
import crypto from "crypto";
|
|
@@ -343,7 +356,8 @@ function trackCliCommandFinished(input) {
|
|
|
343
356
|
command: input.command,
|
|
344
357
|
success: input.success,
|
|
345
358
|
duration_bucket: bucketCliCommandDuration(input.durationMs),
|
|
346
|
-
...input.setupState ? { setup_state: input.setupState } : {}
|
|
359
|
+
...input.setupState ? { setup_state: input.setupState } : {},
|
|
360
|
+
...cliRuntimeContext()
|
|
347
361
|
},
|
|
348
362
|
input.errorCode ? { errorCode: input.errorCode } : void 0
|
|
349
363
|
);
|
|
@@ -418,7 +432,8 @@ function getOrCreateAnonymousId() {
|
|
|
418
432
|
try {
|
|
419
433
|
const config = loadConfig();
|
|
420
434
|
if (config.anonymousId) return config.anonymousId;
|
|
421
|
-
const
|
|
435
|
+
const carried = preConfigAnonymousId?.configPath === getConfigPath() ? preConfigAnonymousId.id : void 0;
|
|
436
|
+
const id = carried ?? crypto.randomUUID();
|
|
422
437
|
config.anonymousId = id;
|
|
423
438
|
try {
|
|
424
439
|
saveConfigPatch(config);
|
|
@@ -430,8 +445,11 @@ function getOrCreateAnonymousId() {
|
|
|
430
445
|
return getDeterministicAnonymousId();
|
|
431
446
|
}
|
|
432
447
|
}
|
|
433
|
-
|
|
448
|
+
const fallback = getDeterministicAnonymousId();
|
|
449
|
+
if (fallback) preConfigAnonymousId = { configPath: getConfigPath(), id: fallback };
|
|
450
|
+
return fallback;
|
|
434
451
|
}
|
|
452
|
+
var preConfigAnonymousId;
|
|
435
453
|
function readEnvAnonymousId() {
|
|
436
454
|
const raw = process.env[ANON_ID_ENV_VAR]?.trim();
|
|
437
455
|
if (!raw) return void 0;
|
|
@@ -506,10 +524,19 @@ function detectAndTrackUpgrade() {
|
|
|
506
524
|
trackEvent("cli.upgraded", { fromVersion: lastSeen, toVersion: VERSION });
|
|
507
525
|
}
|
|
508
526
|
function trackEvent(event, properties, options) {
|
|
509
|
-
|
|
510
|
-
|
|
527
|
+
void deliverEvent(event, properties, options);
|
|
528
|
+
}
|
|
529
|
+
function setTelemetryPreference(enabled, method) {
|
|
530
|
+
loadConfig();
|
|
531
|
+
const announce = !enabled && isTelemetryEnabled();
|
|
532
|
+
saveConfigPatch({ telemetry: enabled });
|
|
533
|
+
return announce ? deliverEvent("telemetry.disabled", { method }, void 0, { preferenceChecked: true }) : Promise.resolve();
|
|
534
|
+
}
|
|
535
|
+
function deliverEvent(event, properties, options, delivery = {}) {
|
|
536
|
+
if (!delivery.preferenceChecked && !isTelemetryEnabled()) return Promise.resolve();
|
|
537
|
+
if (shouldDropTelemetryEvent(event, properties)) return Promise.resolve();
|
|
511
538
|
const anonymousId = getOrCreateAnonymousId();
|
|
512
|
-
if (!anonymousId) return;
|
|
539
|
+
if (!anonymousId) return Promise.resolve();
|
|
513
540
|
const payload = {
|
|
514
541
|
eventId: options?.eventId ?? crypto.randomUUID(),
|
|
515
542
|
anonymousId,
|
|
@@ -529,15 +556,15 @@ function trackEvent(event, properties, options) {
|
|
|
529
556
|
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
530
557
|
timeout.unref();
|
|
531
558
|
try {
|
|
532
|
-
|
|
559
|
+
return fetch(TELEMETRY_ENDPOINT, {
|
|
533
560
|
method: "POST",
|
|
534
561
|
headers: { "Content-Type": "application/json" },
|
|
535
562
|
body: JSON.stringify(payload),
|
|
536
563
|
signal: controller.signal
|
|
537
|
-
}).
|
|
538
|
-
}).finally(() => clearTimeout(timeout));
|
|
564
|
+
}).then(() => void 0, () => void 0).finally(() => clearTimeout(timeout));
|
|
539
565
|
} catch {
|
|
540
566
|
clearTimeout(timeout);
|
|
567
|
+
return Promise.resolve();
|
|
541
568
|
}
|
|
542
569
|
}
|
|
543
570
|
|
|
@@ -6280,13 +6307,93 @@ function removeWordpressConnection(config, projectName) {
|
|
|
6280
6307
|
return true;
|
|
6281
6308
|
}
|
|
6282
6309
|
|
|
6310
|
+
// src/usage-telemetry.ts
|
|
6311
|
+
var API_REQUEST_BUCKET_CAPACITY = 20;
|
|
6312
|
+
var API_REQUEST_REFILL_PER_MS = 1 / 1e4;
|
|
6313
|
+
var MAX_TRACKED_MCP_SESSIONS = 2e3;
|
|
6314
|
+
var MAX_ROUTE_LENGTH = 200;
|
|
6315
|
+
var SKIPPED_ROUTE_PATTERN = /(?:^|\/)(?:health|openapi\.json|telemetry)(?:\/|$)/;
|
|
6316
|
+
var MCP_CALL_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6317
|
+
var MCP_TOOL_PATTERN = /^[a-z][a-z0-9_]{0,79}$/;
|
|
6318
|
+
function classifyUsageSurface(info) {
|
|
6319
|
+
const labelled = usageSurfaceSchema.safeParse(info.usageLabels.surface);
|
|
6320
|
+
if (labelled.success) return labelled.data;
|
|
6321
|
+
const ua = info.userAgent ?? "";
|
|
6322
|
+
if (ua.startsWith("canonry-cli")) return "cli";
|
|
6323
|
+
if (ua.startsWith("canonry-mcp/")) return "mcp-http";
|
|
6324
|
+
if (ua === "canonry-mcp") return "mcp-stdio";
|
|
6325
|
+
if (ua.startsWith("Mozilla/")) return "dashboard";
|
|
6326
|
+
return "api";
|
|
6327
|
+
}
|
|
6328
|
+
function statusClass(statusCode) {
|
|
6329
|
+
if (statusCode >= 500) return "5xx";
|
|
6330
|
+
if (statusCode >= 400) return "4xx";
|
|
6331
|
+
if (statusCode >= 300) return "3xx";
|
|
6332
|
+
return "2xx";
|
|
6333
|
+
}
|
|
6334
|
+
function createApiUsageTelemetry(options = {}) {
|
|
6335
|
+
const emit = options.emit ?? trackEvent;
|
|
6336
|
+
const now = options.now ?? Date.now;
|
|
6337
|
+
let tokens = API_REQUEST_BUCKET_CAPACITY;
|
|
6338
|
+
let refilledAt = now();
|
|
6339
|
+
let droppedBefore = 0;
|
|
6340
|
+
const seenSessions = /* @__PURE__ */ new Set();
|
|
6341
|
+
const takeToken = () => {
|
|
6342
|
+
const at = now();
|
|
6343
|
+
tokens = Math.min(API_REQUEST_BUCKET_CAPACITY, tokens + (at - refilledAt) * API_REQUEST_REFILL_PER_MS);
|
|
6344
|
+
refilledAt = at;
|
|
6345
|
+
if (tokens < 1) return false;
|
|
6346
|
+
tokens -= 1;
|
|
6347
|
+
return true;
|
|
6348
|
+
};
|
|
6349
|
+
return (info) => {
|
|
6350
|
+
if (info.method === "OPTIONS" || info.method === "HEAD") return;
|
|
6351
|
+
if (SKIPPED_ROUTE_PATTERN.test(info.route)) return;
|
|
6352
|
+
const surface = classifyUsageSurface(info);
|
|
6353
|
+
if (surface === "cli" || surface === "dashboard") return;
|
|
6354
|
+
const labels = info.usageLabels;
|
|
6355
|
+
const mcpClient = normalizeAgentSlug(labels.mcpClient);
|
|
6356
|
+
const detected = normalizeAgentSlug(labels.agent);
|
|
6357
|
+
const agent = detected && detected !== AGENT_NONE ? detected : mcpClient ?? detected ?? AGENT_NONE;
|
|
6358
|
+
const mcpTool = labels.mcpTool && MCP_TOOL_PATTERN.test(labels.mcpTool) ? labels.mcpTool : void 0;
|
|
6359
|
+
const mcpCallId = labels.mcpCall && MCP_CALL_ID_PATTERN.test(labels.mcpCall) ? labels.mcpCall.toLowerCase() : void 0;
|
|
6360
|
+
const isMcp = surface === "mcp-stdio" || surface === "mcp-http";
|
|
6361
|
+
if (isMcp && mcpTool && info.actorSession && !seenSessions.has(info.actorSession)) {
|
|
6362
|
+
if (seenSessions.size >= MAX_TRACKED_MCP_SESSIONS) {
|
|
6363
|
+
const oldest = seenSessions.values().next().value;
|
|
6364
|
+
if (oldest !== void 0) seenSessions.delete(oldest);
|
|
6365
|
+
}
|
|
6366
|
+
seenSessions.add(info.actorSession);
|
|
6367
|
+
emit("mcp.session.started", { surface, agent, ...mcpClient ? { mcpClient } : {} }, { source: "cli-server" });
|
|
6368
|
+
}
|
|
6369
|
+
if (!takeToken()) {
|
|
6370
|
+
droppedBefore += 1;
|
|
6371
|
+
return;
|
|
6372
|
+
}
|
|
6373
|
+
const properties = {
|
|
6374
|
+
surface,
|
|
6375
|
+
agent,
|
|
6376
|
+
method: info.method,
|
|
6377
|
+
route: info.route.slice(0, MAX_ROUTE_LENGTH),
|
|
6378
|
+
statusClass: statusClass(info.statusCode),
|
|
6379
|
+
durationBucket: bucketCliCommandDuration(info.durationMs),
|
|
6380
|
+
...mcpClient ? { mcpClient } : {},
|
|
6381
|
+
...mcpTool ? { mcpTool } : {},
|
|
6382
|
+
...mcpCallId ? { mcpCallId } : {},
|
|
6383
|
+
...droppedBefore > 0 ? { droppedBefore } : {}
|
|
6384
|
+
};
|
|
6385
|
+
droppedBefore = 0;
|
|
6386
|
+
emit("api.request", properties, { source: "cli-server" });
|
|
6387
|
+
};
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6283
6390
|
// src/instance-identity.ts
|
|
6284
6391
|
function nonBlank(value) {
|
|
6285
6392
|
const trimmed = value?.trim();
|
|
6286
6393
|
return trimmed ? trimmed : void 0;
|
|
6287
6394
|
}
|
|
6288
6395
|
function resolveBuildCommit(env = process.env) {
|
|
6289
|
-
const embedded = true ? "
|
|
6396
|
+
const embedded = true ? "267b6410fa27a2bf032d4957047859e1f9eee6d1" : void 0;
|
|
6290
6397
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
6291
6398
|
}
|
|
6292
6399
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -6342,6 +6449,30 @@ function buildRunCompletedProps(input) {
|
|
|
6342
6449
|
if (input.location) props.location = input.location;
|
|
6343
6450
|
return props;
|
|
6344
6451
|
}
|
|
6452
|
+
function buildSiteAuditCompletedProps(input) {
|
|
6453
|
+
const props = {
|
|
6454
|
+
status: input.status,
|
|
6455
|
+
durationMs: Date.now() - input.startTime
|
|
6456
|
+
};
|
|
6457
|
+
if (input.trigger) props.trigger = input.trigger;
|
|
6458
|
+
const domainHash = hashDomain(input.canonicalDomain ?? null);
|
|
6459
|
+
if (domainHash) props.domainHash = domainHash;
|
|
6460
|
+
const crawl = input.crawl;
|
|
6461
|
+
if (!crawl) return props;
|
|
6462
|
+
props.complete = crawl.complete;
|
|
6463
|
+
if (crawl.termination) props.termination = crawl.termination;
|
|
6464
|
+
props.pagesDiscovered = crawl.pagesDiscovered;
|
|
6465
|
+
props.pagesFetched = crawl.pagesFetched;
|
|
6466
|
+
props.pagesAudited = crawl.pagesAudited;
|
|
6467
|
+
props.pagesErrored = crawl.pagesErrored;
|
|
6468
|
+
if (crawl.pagesAudited > 0 && typeof crawl.aggregateScore === "number" && Number.isFinite(crawl.aggregateScore)) {
|
|
6469
|
+
props.aggregateScore = crawl.aggregateScore;
|
|
6470
|
+
}
|
|
6471
|
+
props.pageBudget = crawl.pageBudget;
|
|
6472
|
+
props.checkDeadLinks = crawl.checkDeadLinks;
|
|
6473
|
+
if (crawl.checkDeadLinks) props.deadLinksFound = crawl.deadLinksFound;
|
|
6474
|
+
return props;
|
|
6475
|
+
}
|
|
6345
6476
|
|
|
6346
6477
|
// src/provider-execution-gate.ts
|
|
6347
6478
|
var ProviderExecutionGate = class {
|
|
@@ -6610,7 +6741,14 @@ var JobRunner = class {
|
|
|
6610
6741
|
this.registry = registry;
|
|
6611
6742
|
}
|
|
6612
6743
|
recoverStaleRuns() {
|
|
6613
|
-
const stale = this.db.select({
|
|
6744
|
+
const stale = this.db.select({
|
|
6745
|
+
id: runs.id,
|
|
6746
|
+
projectId: runs.projectId,
|
|
6747
|
+
kind: runs.kind,
|
|
6748
|
+
status: runs.status,
|
|
6749
|
+
trigger: runs.trigger,
|
|
6750
|
+
startedAt: runs.startedAt
|
|
6751
|
+
}).from(runs).where(inArray(runs.status, ["running", "queued"])).all();
|
|
6614
6752
|
if (stale.length === 0) return;
|
|
6615
6753
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6616
6754
|
for (const run of stale) {
|
|
@@ -6628,6 +6766,35 @@ var JobRunner = class {
|
|
|
6628
6766
|
});
|
|
6629
6767
|
if (!recovered) continue;
|
|
6630
6768
|
log2.warn("run.recovered-stale", { runId: run.id, previousStatus: run.status });
|
|
6769
|
+
if (run.kind === RunKinds["site-audit"]) this.trackRecoveredSiteAudit(run);
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
/**
|
|
6773
|
+
* A crawl the process died under never reaches `executeSiteAudit`'s terminal
|
|
6774
|
+
* telemetry, so without this every interrupted audit vanishes from the data.
|
|
6775
|
+
* Emitted after the recovery transaction commits. `durationMs` runs from the
|
|
6776
|
+
* crawl's start to this recovery, so it includes the downtime and is an upper
|
|
6777
|
+
* bound; a run that was still queued has no start and reports 0.
|
|
6778
|
+
*
|
|
6779
|
+
* The source is set explicitly: recovery runs during server construction,
|
|
6780
|
+
* before `canonry serve` switches the process source to `cli-server`.
|
|
6781
|
+
*/
|
|
6782
|
+
trackRecoveredSiteAudit(run) {
|
|
6783
|
+
try {
|
|
6784
|
+
const startedAt = run.startedAt ? Date.parse(run.startedAt) : Number.NaN;
|
|
6785
|
+
const project = this.db.select({ canonicalDomain: projects.canonicalDomain }).from(projects).where(eq2(projects.id, run.projectId)).get();
|
|
6786
|
+
trackEvent(
|
|
6787
|
+
"site_audit.completed",
|
|
6788
|
+
buildSiteAuditCompletedProps({
|
|
6789
|
+
status: "failed",
|
|
6790
|
+
startTime: Number.isFinite(startedAt) ? startedAt : Date.now(),
|
|
6791
|
+
trigger: run.trigger,
|
|
6792
|
+
canonicalDomain: project?.canonicalDomain ?? null
|
|
6793
|
+
}),
|
|
6794
|
+
{ errorCode: "SERVER_RESTARTED", source: "cli-server" }
|
|
6795
|
+
);
|
|
6796
|
+
} catch (err) {
|
|
6797
|
+
log2.warn("telemetry.recovered-site-audit-failed", { runId: run.id, error: describeError(err) });
|
|
6631
6798
|
}
|
|
6632
6799
|
}
|
|
6633
6800
|
async executeRun(runId, projectId, providerOverride, locationOverride) {
|
|
@@ -7149,6 +7316,7 @@ var JobRunner = class {
|
|
|
7149
7316
|
if (existingRun.kind === "answer-visibility" && runTrigger !== "probe" && totalSnapshotsInserted > 0 && !this.hasPriorActivation(projectId, runId)) {
|
|
7150
7317
|
trackEvent("activation.completed", {
|
|
7151
7318
|
flowVersion: ONBOARDING_FLOW_VERSION,
|
|
7319
|
+
kind: "answer_visibility",
|
|
7152
7320
|
status: finalStatus,
|
|
7153
7321
|
providerCountBucket: bucketOnboardingCount(executionContext.providerCount),
|
|
7154
7322
|
queryCountBucket: bucketOnboardingCount(executionContext.queryCount),
|
|
@@ -10031,7 +10199,7 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
10031
10199
|
|
|
10032
10200
|
// src/execute-site-audit.ts
|
|
10033
10201
|
import crypto19 from "crypto";
|
|
10034
|
-
import { and as and13, eq as eq15, sql as sql6 } from "drizzle-orm";
|
|
10202
|
+
import { and as and13, eq as eq15, ne as ne3, sql as sql6 } from "drizzle-orm";
|
|
10035
10203
|
import { runSiteCrawl } from "@canonry/aeo-audit";
|
|
10036
10204
|
|
|
10037
10205
|
// src/site-audit-root.ts
|
|
@@ -11108,10 +11276,35 @@ function deadLinkCheckedCount(edges, pages) {
|
|
|
11108
11276
|
}
|
|
11109
11277
|
return new Set([...edges].filter((edge) => edge.type === "anchor" && edge.classification === "internal" && attemptedUrls.has(edge.to)).map((edge) => edge.to)).size;
|
|
11110
11278
|
}
|
|
11279
|
+
function trackSiteAuditOutcome(db, input) {
|
|
11280
|
+
try {
|
|
11281
|
+
const errorCode = input.status === "cancelled" ? "RUN_CANCELLED" : input.status === "failed" ? "UNKNOWN" : void 0;
|
|
11282
|
+
trackEvent(
|
|
11283
|
+
"site_audit.completed",
|
|
11284
|
+
buildSiteAuditCompletedProps(input),
|
|
11285
|
+
errorCode ? { errorCode } : void 0
|
|
11286
|
+
);
|
|
11287
|
+
const pagesAudited = input.crawl?.pagesAudited ?? 0;
|
|
11288
|
+
if (pagesAudited === 0 || input.trigger === RunTriggers.probe) return;
|
|
11289
|
+
const prior = db.select({ id: siteAuditSnapshots.id }).from(siteAuditSnapshots).where(and13(eq15(siteAuditSnapshots.projectId, input.projectId), ne3(siteAuditSnapshots.runId, input.runId))).limit(1).get();
|
|
11290
|
+
if (prior) return;
|
|
11291
|
+
trackEvent("activation.completed", {
|
|
11292
|
+
flowVersion: ONBOARDING_FLOW_VERSION,
|
|
11293
|
+
kind: "site_health",
|
|
11294
|
+
status: input.status,
|
|
11295
|
+
pagesAuditedBucket: bucketOnboardingCount(pagesAudited)
|
|
11296
|
+
});
|
|
11297
|
+
} catch (error) {
|
|
11298
|
+
log13.warn("telemetry.failed", { runId: input.runId, projectId: input.projectId, error: describeError(error) });
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11111
11301
|
async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
11112
11302
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11113
11303
|
const claim = db.update(runs).set({ status: "running", startedAt }).where(and13(eq15(runs.id, runId), eq15(runs.projectId, projectId), eq15(runs.status, "queued"))).run();
|
|
11114
11304
|
if (claim.changes === 0) return;
|
|
11305
|
+
const startTime = Date.now();
|
|
11306
|
+
const trigger = db.select({ trigger: runs.trigger }).from(runs).where(eq15(runs.id, runId)).get()?.trigger ?? null;
|
|
11307
|
+
let canonicalDomain = null;
|
|
11115
11308
|
const attemptNumber = (db.select({ value: sql6`coalesce(max(${siteCrawlAttempts.attemptNumber}), 0)` }).from(siteCrawlAttempts).where(eq15(siteCrawlAttempts.runId, runId)).get()?.value ?? 0) + 1;
|
|
11116
11309
|
const attemptId = crypto19.randomUUID();
|
|
11117
11310
|
db.insert(siteCrawlAttempts).values({
|
|
@@ -11391,6 +11584,7 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11391
11584
|
try {
|
|
11392
11585
|
const project = db.select().from(projects).where(eq15(projects.id, projectId)).get();
|
|
11393
11586
|
if (!project) throw new Error(`Project not found: ${projectId}`);
|
|
11587
|
+
canonicalDomain = project.canonicalDomain;
|
|
11394
11588
|
const homepageUrl = toHomepageUrl(project.canonicalDomain);
|
|
11395
11589
|
const maxPages = clampSiteAuditLimit(opts.maxPages ?? opts.limit);
|
|
11396
11590
|
const maxEdges = clampSiteAuditEdgeLimit(opts.maxEdges);
|
|
@@ -11610,6 +11804,26 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11610
11804
|
throw new Error(`Site audit run was no longer running before publication: ${runId}`);
|
|
11611
11805
|
}
|
|
11612
11806
|
log13.info("completed", { runId, projectId, status: terminalStatus, complete: crawlSummary.complete, pages: crawlSummary.pagesObserved });
|
|
11807
|
+
trackSiteAuditOutcome(db, {
|
|
11808
|
+
runId,
|
|
11809
|
+
projectId,
|
|
11810
|
+
status: crawlSummary.complete ? "completed" : "partial",
|
|
11811
|
+
startTime,
|
|
11812
|
+
trigger,
|
|
11813
|
+
canonicalDomain,
|
|
11814
|
+
crawl: {
|
|
11815
|
+
complete: crawlSummary.complete,
|
|
11816
|
+
termination: crawlSummary.terminationReason,
|
|
11817
|
+
pagesDiscovered: crawlSummary.pagesDiscovered,
|
|
11818
|
+
pagesFetched: crawlSummary.pagesFetched,
|
|
11819
|
+
pagesAudited: crawlSummary.auditRollup.auditedPages,
|
|
11820
|
+
pagesErrored: errorCount,
|
|
11821
|
+
aggregateScore: crawlSummary.auditRollup.aggregateScore,
|
|
11822
|
+
pageBudget: maxPages,
|
|
11823
|
+
checkDeadLinks: opts.checkDeadLinks ?? false,
|
|
11824
|
+
deadLinksFound
|
|
11825
|
+
}
|
|
11826
|
+
});
|
|
11613
11827
|
} catch (error) {
|
|
11614
11828
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11615
11829
|
const cancelled = isCancelled(error, opts.signal);
|
|
@@ -11628,6 +11842,14 @@ async function executeSiteAudit(db, runId, projectId, opts = {}) {
|
|
|
11628
11842
|
}).where(and13(eq15(runs.id, runId), eq15(runs.status, "running"))).run();
|
|
11629
11843
|
});
|
|
11630
11844
|
log13.error("failed", { runId, projectId, cancelled, error: message });
|
|
11845
|
+
trackSiteAuditOutcome(db, {
|
|
11846
|
+
runId,
|
|
11847
|
+
projectId,
|
|
11848
|
+
status: cancelled ? "cancelled" : "failed",
|
|
11849
|
+
startTime,
|
|
11850
|
+
trigger,
|
|
11851
|
+
canonicalDomain
|
|
11852
|
+
});
|
|
11631
11853
|
throw error;
|
|
11632
11854
|
}
|
|
11633
11855
|
}
|
|
@@ -12097,7 +12319,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
12097
12319
|
return result;
|
|
12098
12320
|
}
|
|
12099
12321
|
async function backfillInsightsCommand(project, opts) {
|
|
12100
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
12322
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-D2HY2S5F.js");
|
|
12101
12323
|
const config = loadConfig();
|
|
12102
12324
|
const db = createClient(config.database);
|
|
12103
12325
|
migrate(db);
|
|
@@ -14191,6 +14413,7 @@ import { Type as Type3 } from "@sinclair/typebox";
|
|
|
14191
14413
|
|
|
14192
14414
|
// src/agent/mcp-to-agent-tool.ts
|
|
14193
14415
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
14416
|
+
import { randomUUID } from "crypto";
|
|
14194
14417
|
var MAX_TOOL_RESULT_CHARS = 2e4;
|
|
14195
14418
|
var TRUNCATION_NOTE = "... (truncated \u2014 result too large)";
|
|
14196
14419
|
function serializeResult(value) {
|
|
@@ -14340,7 +14563,7 @@ function mcpToAgentTool(tool, ctx) {
|
|
|
14340
14563
|
const parameters = Type2.Unsafe(visibleSchema);
|
|
14341
14564
|
const execute = async (_toolCallId, params) => {
|
|
14342
14565
|
const handlerInput = hadProject ? { ...params, project: ctx.projectName } : params;
|
|
14343
|
-
const result = await tool.handler(ctx.client, handlerInput);
|
|
14566
|
+
const result = await runWithUsageTags(ctx.client, { mcpTool: tool.name, mcpCall: randomUUID() }, () => tool.handler(ctx.client, handlerInput));
|
|
14344
14567
|
return textResult2(result);
|
|
14345
14568
|
};
|
|
14346
14569
|
return {
|
|
@@ -15854,6 +16077,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
15854
16077
|
const client = new ApiClient(opts.selfApiUrl, sessionKey?.raw ?? bearer, {
|
|
15855
16078
|
skipProbe: true,
|
|
15856
16079
|
clientName: `canonry-mcp/${PACKAGE_VERSION}`,
|
|
16080
|
+
surface: "mcp-http",
|
|
15857
16081
|
// Correlation only: this is never accepted as caller identity or authority.
|
|
15858
16082
|
actorSession: crypto26.randomUUID()
|
|
15859
16083
|
});
|
|
@@ -17608,7 +17832,7 @@ function cloneGtmConfig(config) {
|
|
|
17608
17832
|
function createGoogleMarketingConfigCredentialStore(input) {
|
|
17609
17833
|
const persistConfigPatch = input.saveConfigPatch ?? saveConfigPatch;
|
|
17610
17834
|
const env = input.env ?? process.env;
|
|
17611
|
-
const
|
|
17835
|
+
const randomUUID2 = input.randomUUID ?? crypto27.randomUUID;
|
|
17612
17836
|
const snapshot = () => ({
|
|
17613
17837
|
googleAds: cloneGoogleAdsConfig(input.config.googleAds),
|
|
17614
17838
|
gtm: cloneGtmConfig(input.config.gtm)
|
|
@@ -17656,7 +17880,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
17656
17880
|
upsertGoogleAdsConnection(input.config, {
|
|
17657
17881
|
projectId: project.id,
|
|
17658
17882
|
projectName: project.name,
|
|
17659
|
-
credentialGeneration:
|
|
17883
|
+
credentialGeneration: randomUUID2(),
|
|
17660
17884
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
17661
17885
|
refreshToken: credential.refreshToken ?? null,
|
|
17662
17886
|
tokenExpiresAt: credential.expiresAt ?? null,
|
|
@@ -17668,7 +17892,7 @@ function createGoogleMarketingConfigCredentialStore(input) {
|
|
|
17668
17892
|
upsertGtmConnection(input.config, {
|
|
17669
17893
|
projectId: project.id,
|
|
17670
17894
|
projectName: project.name,
|
|
17671
|
-
credentialGeneration:
|
|
17895
|
+
credentialGeneration: randomUUID2(),
|
|
17672
17896
|
...credential.accessToken ? { accessToken: credential.accessToken } : {},
|
|
17673
17897
|
refreshToken: credential.refreshToken ?? null,
|
|
17674
17898
|
tokenExpiresAt: credential.expiresAt ?? null,
|
|
@@ -17781,6 +18005,10 @@ async function createServer(opts) {
|
|
|
17781
18005
|
const notifier = new Notifier(opts.db, serverUrl);
|
|
17782
18006
|
const intelligenceService = new IntelligenceService(opts.db);
|
|
17783
18007
|
const aeroClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, {
|
|
18008
|
+
skipProbe: true,
|
|
18009
|
+
surface: "aero"
|
|
18010
|
+
});
|
|
18011
|
+
const schedulerClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, {
|
|
17784
18012
|
skipProbe: true
|
|
17785
18013
|
});
|
|
17786
18014
|
const agentEnabled = resolveAgentEnabled(process.env, opts.config);
|
|
@@ -18221,7 +18449,7 @@ async function createServer(opts) {
|
|
|
18221
18449
|
getRunnableProviderNames: () => registry.getAll().map((provider) => provider.adapter.name),
|
|
18222
18450
|
getEffectiveProviderModels: () => effectiveProviderModels(registry),
|
|
18223
18451
|
onTrafficSyncRequested: (projectName, sourceId) => {
|
|
18224
|
-
|
|
18452
|
+
schedulerClient.trafficSync(projectName, sourceId).catch((err) => {
|
|
18225
18453
|
app.log.error(
|
|
18226
18454
|
{
|
|
18227
18455
|
projectName,
|
|
@@ -18241,7 +18469,7 @@ async function createServer(opts) {
|
|
|
18241
18469
|
onDoctorRequested: (projectName) => {
|
|
18242
18470
|
void (async () => {
|
|
18243
18471
|
try {
|
|
18244
|
-
const report = await
|
|
18472
|
+
const report = await schedulerClient.runDoctor({ project: projectName });
|
|
18245
18473
|
const project = opts.db.select().from(projects).where(eq26(projects.name, projectName)).get();
|
|
18246
18474
|
if (!project) {
|
|
18247
18475
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
@@ -18257,7 +18485,7 @@ async function createServer(opts) {
|
|
|
18257
18485
|
})();
|
|
18258
18486
|
},
|
|
18259
18487
|
onDataRefreshRequested: (projectName) => {
|
|
18260
|
-
void refreshAllIntegrations(
|
|
18488
|
+
void refreshAllIntegrations(schedulerClient, projectName);
|
|
18261
18489
|
},
|
|
18262
18490
|
onBacklinksSyncRequested: (projectName) => {
|
|
18263
18491
|
void (async () => {
|
|
@@ -18282,7 +18510,7 @@ async function createServer(opts) {
|
|
|
18282
18510
|
);
|
|
18283
18511
|
return;
|
|
18284
18512
|
}
|
|
18285
|
-
|
|
18513
|
+
schedulerClient.backlinksTriggerSync(probed.release).catch((err) => {
|
|
18286
18514
|
app.log.error(
|
|
18287
18515
|
{
|
|
18288
18516
|
projectName,
|
|
@@ -18783,6 +19011,9 @@ async function createServer(opts) {
|
|
|
18783
19011
|
await app.register(apiRoutes, {
|
|
18784
19012
|
db: opts.db,
|
|
18785
19013
|
routePrefix: apiPrefix,
|
|
19014
|
+
// Agent-surface usage (MCP, Aero, raw API). CLI and dashboard requests are
|
|
19015
|
+
// measured elsewhere and skipped inside.
|
|
19016
|
+
onRequestCompleted: createApiUsageTelemetry(),
|
|
18786
19017
|
skipAuth: false,
|
|
18787
19018
|
sessionCookieName: SESSION_COOKIE_NAME,
|
|
18788
19019
|
resolveSessionApiKeyId,
|
|
@@ -19313,9 +19544,7 @@ async function createServer(opts) {
|
|
|
19313
19544
|
listOperationalLogs: (query) => operationalLogs.list(query),
|
|
19314
19545
|
getTelemetryStatus,
|
|
19315
19546
|
setTelemetryEnabled: (enabled) => {
|
|
19316
|
-
|
|
19317
|
-
config.telemetry = enabled;
|
|
19318
|
-
saveConfigPatch(config);
|
|
19547
|
+
void setTelemetryPreference(enabled, "api");
|
|
19319
19548
|
opts.config.telemetry = enabled;
|
|
19320
19549
|
},
|
|
19321
19550
|
recordOnboardingEvent: (event) => {
|
|
@@ -19647,6 +19876,7 @@ function parseQueryResponse(raw, count2) {
|
|
|
19647
19876
|
}
|
|
19648
19877
|
|
|
19649
19878
|
export {
|
|
19879
|
+
cliRuntimeContext,
|
|
19650
19880
|
trackCliCommandFinished,
|
|
19651
19881
|
setTelemetrySource,
|
|
19652
19882
|
getTelemetryStatus,
|
|
@@ -19656,6 +19886,7 @@ export {
|
|
|
19656
19886
|
showFirstRunNotice,
|
|
19657
19887
|
detectAndTrackUpgrade,
|
|
19658
19888
|
trackEvent,
|
|
19889
|
+
setTelemetryPreference,
|
|
19659
19890
|
backfillAnswerVisibilityCommand,
|
|
19660
19891
|
backfillNormalizedPaths,
|
|
19661
19892
|
backfillNormalizedPathsCommand,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
installSkills,
|
|
6
6
|
loadConfigRaw,
|
|
7
7
|
saveConfigPatch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5K2NUQOV.js";
|
|
9
9
|
import {
|
|
10
10
|
SKILL_MANIFEST_FILENAME,
|
|
11
11
|
SkillsClients
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5ZIUROAQ.js";
|
|
13
13
|
|
|
14
14
|
// src/skills-autosync.ts
|
|
15
15
|
import fs from "fs";
|