@canonry/canonry 4.136.2 → 4.137.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 -1
- package/assets/assets/{AuditHistoryPanel-DtUGiN21.js → AuditHistoryPanel-B6GEmlVM.js} +1 -1
- package/assets/assets/{BacklinksPage-BGwtmwfz.js → BacklinksPage-Cr3QegNo.js} +1 -1
- package/assets/assets/{ChartPrimitives-Df2UC1g3.js → ChartPrimitives-rhsGzU1A.js} +1 -1
- package/assets/assets/{HistoryPage-AtaMVEUx.js → HistoryPage-B3HrxyPP.js} +1 -1
- package/assets/assets/ProjectPage-D3WiL3RI.js +8 -0
- package/assets/assets/{RunRow-Cmf-5xVo.js → RunRow-MsV1RLPm.js} +1 -1
- package/assets/assets/{RunsPage-BAPy9Yri.js → RunsPage-BVlv0j3Y.js} +1 -1
- package/assets/assets/{SettingsPage-BXO7WpTo.js → SettingsPage-Dfd-T4o9.js} +1 -1
- package/assets/assets/{TrafficPage-IH-wiPu2.js → TrafficPage-Bl1jj73m.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CTTN-P52.js → TrafficSourceDetailPage-CinQcKmY.js} +1 -1
- package/assets/assets/{arrow-left-BwzAIeJ_.js → arrow-left-CHU6s_Xn.js} +1 -1
- package/assets/assets/extract-error-message-BDdyZgyf.js +1 -0
- package/assets/assets/index-BNulljlk.js +213 -0
- package/assets/assets/{trash-2-C50OeBAV.js → trash-2-BE5oH1Tj.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-GGIRLRAY.js → chunk-7RF5EDUO.js} +99 -3
- package/dist/{chunk-YVMQWVNY.js → chunk-PBTB3FCN.js} +117 -12
- package/dist/{chunk-SMFUCXHI.js → chunk-UVWI6VPP.js} +1 -1
- package/dist/{chunk-MXR3GI5L.js → chunk-XEX5CLQW.js} +113 -15
- package/dist/cli.js +147 -33
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-BD6BHTEJ.js → intelligence-service-JEFHUGVX.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
- package/assets/assets/ProjectPage-OlBjxXNx.js +0 -8
- package/assets/assets/extract-error-message-BIcIoJLN.js +0 -1
- package/assets/assets/index-BPpSrJY0.js +0 -210
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as a}from"./index-
|
|
1
|
+
import{c as a}from"./index-BNulljlk.js";const c=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],d=a("circle-check",c);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],k=a("circle-question-mark",e);const t=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=a("download",t);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],r=a("external-link",o);const n=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],y=a("trash-2",n);export{d as C,s as D,r as E,y as T,k as a};
|
package/assets/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-BNulljlk.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-DSxTZhaH.js">
|
|
@@ -53,6 +53,22 @@ function quotaExceeded(metric, details) {
|
|
|
53
53
|
function providerError(message, details) {
|
|
54
54
|
return new AppError("PROVIDER_ERROR", message, 502, details);
|
|
55
55
|
}
|
|
56
|
+
function noProvider(projectName, details) {
|
|
57
|
+
return new AppError(
|
|
58
|
+
"NO_PROVIDER",
|
|
59
|
+
`No runnable answer provider is configured for '${projectName}'. Configure a provider before starting a run.`,
|
|
60
|
+
503,
|
|
61
|
+
{ projectName, ...details }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function noQueries(projectName) {
|
|
65
|
+
return new AppError(
|
|
66
|
+
"NO_QUERIES",
|
|
67
|
+
`Project '${projectName}' has no tracked queries. Add at least one query before starting a run.`,
|
|
68
|
+
422,
|
|
69
|
+
{ projectName }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
56
72
|
function runInProgress(projectName) {
|
|
57
73
|
return new AppError("RUN_IN_PROGRESS", `A run is already in progress for '${projectName}'`, 409);
|
|
58
74
|
}
|
|
@@ -9364,14 +9380,14 @@ function round(value, dp = 4) {
|
|
|
9364
9380
|
const f = 10 ** dp;
|
|
9365
9381
|
return (Math.round(value * f) + 0) / f;
|
|
9366
9382
|
}
|
|
9367
|
-
function wilsonInterval(successes, n,
|
|
9383
|
+
function wilsonInterval(successes, n, z40 = 1.96) {
|
|
9368
9384
|
if (!Number.isFinite(n) || n <= 0) return null;
|
|
9369
9385
|
const s = Math.max(0, Math.min(successes, n));
|
|
9370
9386
|
const p = s / n;
|
|
9371
|
-
const z210 =
|
|
9387
|
+
const z210 = z40 * z40;
|
|
9372
9388
|
const denom = 1 + z210 / n;
|
|
9373
9389
|
const center = (p + z210 / (2 * n)) / denom;
|
|
9374
|
-
const margin =
|
|
9390
|
+
const margin = z40 / denom * Math.sqrt(p * (1 - p) / n + z210 / (4 * n * n));
|
|
9375
9391
|
return {
|
|
9376
9392
|
low: round(Math.max(0, center - margin)),
|
|
9377
9393
|
high: round(Math.min(1, center + margin))
|
|
@@ -10471,6 +10487,80 @@ function serializeForInlineScript(value) {
|
|
|
10471
10487
|
}
|
|
10472
10488
|
|
|
10473
10489
|
// ../contracts/src/telemetry.ts
|
|
10490
|
+
import { z as z39 } from "zod";
|
|
10491
|
+
var ONBOARDING_FLOW_VERSION = 1;
|
|
10492
|
+
var onboardingStepSchema = z39.enum([
|
|
10493
|
+
"system",
|
|
10494
|
+
"project",
|
|
10495
|
+
"queries",
|
|
10496
|
+
"competitors",
|
|
10497
|
+
"run"
|
|
10498
|
+
]);
|
|
10499
|
+
var onboardingCountBucketSchema = z39.enum([
|
|
10500
|
+
"0",
|
|
10501
|
+
"1",
|
|
10502
|
+
"2-3",
|
|
10503
|
+
"4-5",
|
|
10504
|
+
"6-10",
|
|
10505
|
+
"11+"
|
|
10506
|
+
]);
|
|
10507
|
+
function bucketOnboardingCount(value) {
|
|
10508
|
+
if (!Number.isFinite(value) || value <= 0) return "0";
|
|
10509
|
+
if (value < 2) return "1";
|
|
10510
|
+
if (value < 4) return "2-3";
|
|
10511
|
+
if (value < 6) return "4-5";
|
|
10512
|
+
if (value < 11) return "6-10";
|
|
10513
|
+
return "11+";
|
|
10514
|
+
}
|
|
10515
|
+
var onboardingBlockReasonSchema = z39.enum([
|
|
10516
|
+
"api_unavailable",
|
|
10517
|
+
"database_unavailable",
|
|
10518
|
+
"worker_unavailable",
|
|
10519
|
+
"no_provider",
|
|
10520
|
+
"no_queries",
|
|
10521
|
+
"provider_save_failed",
|
|
10522
|
+
"project_create_failed",
|
|
10523
|
+
"query_save_failed",
|
|
10524
|
+
"run_rejected",
|
|
10525
|
+
"run_failed",
|
|
10526
|
+
"run_cancelled",
|
|
10527
|
+
"unknown"
|
|
10528
|
+
]);
|
|
10529
|
+
var onboardingEventBaseSchema = z39.object({
|
|
10530
|
+
eventId: z39.string().uuid(),
|
|
10531
|
+
flowVersion: z39.literal(ONBOARDING_FLOW_VERSION),
|
|
10532
|
+
onboardingSessionId: z39.string().uuid()
|
|
10533
|
+
});
|
|
10534
|
+
var onboardingTelemetryEventSchema = z39.discriminatedUnion("event", [
|
|
10535
|
+
onboardingEventBaseSchema.extend({
|
|
10536
|
+
event: z39.literal("onboarding.started"),
|
|
10537
|
+
step: onboardingStepSchema,
|
|
10538
|
+
resumed: z39.boolean()
|
|
10539
|
+
}).strict(),
|
|
10540
|
+
onboardingEventBaseSchema.extend({
|
|
10541
|
+
event: z39.literal("onboarding.step_completed"),
|
|
10542
|
+
step: onboardingStepSchema,
|
|
10543
|
+
method: z39.enum(["existing", "inline", "manual", "generated", "skipped", "automatic"]),
|
|
10544
|
+
countBucket: onboardingCountBucketSchema.optional()
|
|
10545
|
+
}).strict(),
|
|
10546
|
+
onboardingEventBaseSchema.extend({
|
|
10547
|
+
event: z39.literal("onboarding.blocked"),
|
|
10548
|
+
step: onboardingStepSchema,
|
|
10549
|
+
action: z39.enum(["continue", "configure_provider", "generate_queries", "save", "launch_run", "retry_run"]),
|
|
10550
|
+
reasonCode: onboardingBlockReasonSchema
|
|
10551
|
+
}).strict(),
|
|
10552
|
+
onboardingEventBaseSchema.extend({
|
|
10553
|
+
event: z39.literal("run.requested"),
|
|
10554
|
+
origin: z39.literal("dashboard_setup"),
|
|
10555
|
+
result: z39.enum(["queued", "rejected"]),
|
|
10556
|
+
providerCountBucket: onboardingCountBucketSchema,
|
|
10557
|
+
queryCountBucket: onboardingCountBucketSchema,
|
|
10558
|
+
reasonCode: onboardingBlockReasonSchema.optional()
|
|
10559
|
+
}).strict()
|
|
10560
|
+
]);
|
|
10561
|
+
var telemetryEventAcceptedDtoSchema = z39.object({
|
|
10562
|
+
accepted: z39.boolean()
|
|
10563
|
+
});
|
|
10474
10564
|
var GHOST_TELEMETRY_TEST_LOCATIONS = /* @__PURE__ */ new Set(["nyc", "lax", "chi"]);
|
|
10475
10565
|
function isGhostTelemetryEvent(eventName, properties) {
|
|
10476
10566
|
if (eventName !== "run.completed" && eventName !== "run.aborted") return false;
|
|
@@ -10505,6 +10595,8 @@ export {
|
|
|
10505
10595
|
forbidden,
|
|
10506
10596
|
quotaExceeded,
|
|
10507
10597
|
providerError,
|
|
10598
|
+
noProvider,
|
|
10599
|
+
noQueries,
|
|
10508
10600
|
runInProgress,
|
|
10509
10601
|
operationInProgress,
|
|
10510
10602
|
runNotCancellable,
|
|
@@ -10901,5 +10993,9 @@ export {
|
|
|
10901
10993
|
normalizeIdTokens,
|
|
10902
10994
|
embedClientConfigForRequest,
|
|
10903
10995
|
serializeForInlineScript,
|
|
10996
|
+
ONBOARDING_FLOW_VERSION,
|
|
10997
|
+
bucketOnboardingCount,
|
|
10998
|
+
onboardingTelemetryEventSchema,
|
|
10999
|
+
telemetryEventAcceptedDtoSchema,
|
|
10904
11000
|
isGhostTelemetryEvent
|
|
10905
11001
|
};
|
|
@@ -264,6 +264,8 @@ import {
|
|
|
264
264
|
mentionStateFromAnswerMentioned,
|
|
265
265
|
missingDependency,
|
|
266
266
|
modelIdsEquivalent,
|
|
267
|
+
noProvider,
|
|
268
|
+
noQueries,
|
|
267
269
|
normalizeIdTokens,
|
|
268
270
|
normalizeModelId,
|
|
269
271
|
normalizeProjectAliases,
|
|
@@ -273,6 +275,7 @@ import {
|
|
|
273
275
|
notFound,
|
|
274
276
|
notImplemented,
|
|
275
277
|
notificationDtoSchema,
|
|
278
|
+
onboardingTelemetryEventSchema,
|
|
276
279
|
operationInProgress,
|
|
277
280
|
orderLocationsDefaultFirst,
|
|
278
281
|
organicEvidenceDtoSchema,
|
|
@@ -338,6 +341,7 @@ import {
|
|
|
338
341
|
summarizeCheckResults,
|
|
339
342
|
surfaceClassFromCompetitorType,
|
|
340
343
|
surfaceClassLabel,
|
|
344
|
+
telemetryEventAcceptedDtoSchema,
|
|
341
345
|
textContainsAnyBrandAlias,
|
|
342
346
|
textContainsBrandAlias,
|
|
343
347
|
textContainsDomain,
|
|
@@ -373,7 +377,7 @@ import {
|
|
|
373
377
|
wordpressSchemaDeployResultDtoSchema,
|
|
374
378
|
wordpressSchemaStatusResultDtoSchema,
|
|
375
379
|
wordpressStatusDtoSchema
|
|
376
|
-
} from "./chunk-
|
|
380
|
+
} from "./chunk-7RF5EDUO.js";
|
|
377
381
|
|
|
378
382
|
// src/intelligence-service.ts
|
|
379
383
|
import { eq as eq44, desc as desc21, asc as asc9, and as and35, ne as ne6, or as or10, inArray as inArray15, gte as gte12, lte as lte8 } from "drizzle-orm";
|
|
@@ -7969,9 +7973,20 @@ async function runRoutes(app, opts) {
|
|
|
7969
7973
|
rawProviders.splice(0, rawProviders.length, ...normalized);
|
|
7970
7974
|
}
|
|
7971
7975
|
const providers = rawProviders?.length ? rawProviders : void 0;
|
|
7976
|
+
const shouldPreflight = trigger !== RunTriggers.probe && opts.getRunnableProviderNames !== void 0;
|
|
7977
|
+
const trackedRows = body.queries?.length || shouldPreflight ? app.db.select({ query: queries.query }).from(queries).where(eq8(queries.projectId, project.id)).all() : [];
|
|
7978
|
+
if (shouldPreflight) {
|
|
7979
|
+
const preflightError = answerVisibilityPreflightError({
|
|
7980
|
+
projectName: project.name,
|
|
7981
|
+
projectProviders: project.providers,
|
|
7982
|
+
requestedProviders: providers,
|
|
7983
|
+
runnableProviderNames: opts.getRunnableProviderNames(),
|
|
7984
|
+
trackedQueryCount: trackedRows.length
|
|
7985
|
+
});
|
|
7986
|
+
if (preflightError) throw preflightError;
|
|
7987
|
+
}
|
|
7972
7988
|
let scopedQueries = null;
|
|
7973
7989
|
if (body.queries?.length) {
|
|
7974
|
-
const trackedRows = app.db.select({ query: queries.query }).from(queries).where(eq8(queries.projectId, project.id)).all();
|
|
7975
7990
|
const tracked = new Set(trackedRows.map((r) => r.query));
|
|
7976
7991
|
const missing = body.queries.filter((q) => !tracked.has(q));
|
|
7977
7992
|
if (missing.length) {
|
|
@@ -8135,7 +8150,28 @@ async function runRoutes(app, opts) {
|
|
|
8135
8150
|
const providers = rawProviders?.length ? rawProviders : void 0;
|
|
8136
8151
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8137
8152
|
const results = [];
|
|
8153
|
+
const runnableProviderNames = opts.getRunnableProviderNames?.();
|
|
8138
8154
|
for (const project of allProjects) {
|
|
8155
|
+
if (runnableProviderNames) {
|
|
8156
|
+
const trackedQuery = app.db.select({ id: queries.id }).from(queries).where(eq8(queries.projectId, project.id)).limit(1).get();
|
|
8157
|
+
const preflightError = answerVisibilityPreflightError({
|
|
8158
|
+
projectName: project.name,
|
|
8159
|
+
projectProviders: project.providers,
|
|
8160
|
+
requestedProviders: providers,
|
|
8161
|
+
runnableProviderNames,
|
|
8162
|
+
trackedQueryCount: trackedQuery ? 1 : 0
|
|
8163
|
+
});
|
|
8164
|
+
if (preflightError) {
|
|
8165
|
+
results.push({
|
|
8166
|
+
projectName: project.name,
|
|
8167
|
+
projectId: project.id,
|
|
8168
|
+
status: "error",
|
|
8169
|
+
error: preflightError.message,
|
|
8170
|
+
errorCode: preflightError.code
|
|
8171
|
+
});
|
|
8172
|
+
continue;
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8139
8175
|
const projectLocations = project.locations;
|
|
8140
8176
|
let resolvedLocation;
|
|
8141
8177
|
if (project.defaultLocation) {
|
|
@@ -8199,6 +8235,26 @@ async function runRoutes(app, opts) {
|
|
|
8199
8235
|
return reply.send(loadRunDetail(app, run));
|
|
8200
8236
|
});
|
|
8201
8237
|
}
|
|
8238
|
+
function answerVisibilityPreflightError(input) {
|
|
8239
|
+
if (input.trackedQueryCount === 0) {
|
|
8240
|
+
return noQueries(input.projectName);
|
|
8241
|
+
}
|
|
8242
|
+
const availableProviders = normalizeProviderNames(input.runnableProviderNames);
|
|
8243
|
+
const requestedProviders = normalizeProviderNames(input.requestedProviders ?? []);
|
|
8244
|
+
const projectProviders = normalizeProviderNames(input.projectProviders);
|
|
8245
|
+
const selectedProviders = requestedProviders.length > 0 ? requestedProviders : projectProviders.length > 0 ? projectProviders : availableProviders;
|
|
8246
|
+
const available = new Set(availableProviders);
|
|
8247
|
+
const runnableProviders = selectedProviders.filter((provider) => available.has(provider));
|
|
8248
|
+
if (runnableProviders.length > 0) return null;
|
|
8249
|
+
return noProvider(input.projectName, {
|
|
8250
|
+
availableProviders,
|
|
8251
|
+
selectedProviders,
|
|
8252
|
+
selectionSource: requestedProviders.length > 0 ? "request" : projectProviders.length > 0 ? "project" : "instance"
|
|
8253
|
+
});
|
|
8254
|
+
}
|
|
8255
|
+
function normalizeProviderNames(providerNames) {
|
|
8256
|
+
return [...new Set(providerNames.map((name) => name.trim().toLowerCase()).filter(Boolean))];
|
|
8257
|
+
}
|
|
8202
8258
|
function parseRunTriggerRequest(value) {
|
|
8203
8259
|
const result = runTriggerRequestSchema.safeParse(value);
|
|
8204
8260
|
if (result.success) return result.data;
|
|
@@ -17456,6 +17512,7 @@ var SCHEMA_TABLE = {
|
|
|
17456
17512
|
LatestProjectRunDto: latestProjectRunDtoSchema,
|
|
17457
17513
|
LocationContext: locationContextSchema,
|
|
17458
17514
|
NotificationDto: notificationDtoSchema,
|
|
17515
|
+
OnboardingTelemetryEvent: onboardingTelemetryEventSchema,
|
|
17459
17516
|
OrganicEvidenceDto: organicEvidenceDtoSchema,
|
|
17460
17517
|
ProjectDto: projectDtoSchema,
|
|
17461
17518
|
ProjectUpsertRequest: projectUpsertRequestSchema,
|
|
@@ -17477,6 +17534,7 @@ var SCHEMA_TABLE = {
|
|
|
17477
17534
|
SnapshotListResponse: snapshotListResponseSchema,
|
|
17478
17535
|
SnapshotReportDto: snapshotReportSchema,
|
|
17479
17536
|
SourceBreakdownDto: sourceBreakdownDtoSchema,
|
|
17537
|
+
TelemetryEventAcceptedDto: telemetryEventAcceptedDtoSchema,
|
|
17480
17538
|
TrafficBackfillResponse: trafficBackfillResponseSchema,
|
|
17481
17539
|
TrafficEventsResponse: trafficEventsResponseSchema,
|
|
17482
17540
|
TrafficSourceDetailDto: trafficSourceDetailDtoSchema,
|
|
@@ -18502,7 +18560,9 @@ var routeCatalog = [
|
|
|
18502
18560
|
},
|
|
18503
18561
|
responses: {
|
|
18504
18562
|
201: jsonResponse("Run queued.", "RunDto"),
|
|
18505
|
-
|
|
18563
|
+
422: errorResponse("Project has no tracked queries."),
|
|
18564
|
+
409: errorResponse("Run already in progress."),
|
|
18565
|
+
503: errorResponse("No runnable answer provider is configured.")
|
|
18506
18566
|
}
|
|
18507
18567
|
},
|
|
18508
18568
|
{
|
|
@@ -19127,6 +19187,24 @@ var routeCatalog = [
|
|
|
19127
19187
|
501: errorResponse("Telemetry configuration is not available.")
|
|
19128
19188
|
}
|
|
19129
19189
|
},
|
|
19190
|
+
{
|
|
19191
|
+
method: "post",
|
|
19192
|
+
path: "/api/v1/telemetry/onboarding",
|
|
19193
|
+
summary: "Record a privacy-safe onboarding milestone",
|
|
19194
|
+
tags: ["telemetry"],
|
|
19195
|
+
requestBody: {
|
|
19196
|
+
required: true,
|
|
19197
|
+
content: {
|
|
19198
|
+
"application/json": {
|
|
19199
|
+
schema: { $ref: "#/components/schemas/OnboardingTelemetryEvent" }
|
|
19200
|
+
}
|
|
19201
|
+
}
|
|
19202
|
+
},
|
|
19203
|
+
responses: {
|
|
19204
|
+
202: jsonResponse("Onboarding milestone accepted.", "TelemetryEventAcceptedDto"),
|
|
19205
|
+
400: errorResponse("Invalid onboarding telemetry event.")
|
|
19206
|
+
}
|
|
19207
|
+
},
|
|
19130
19208
|
{
|
|
19131
19209
|
method: "get",
|
|
19132
19210
|
path: "/api/v1/screenshots/{snapshotId}",
|
|
@@ -22777,6 +22855,19 @@ async function telemetryRoutes(app, opts) {
|
|
|
22777
22855
|
anonymousId: status?.anonymousId ? status.anonymousId.slice(0, 8) + "..." : void 0
|
|
22778
22856
|
};
|
|
22779
22857
|
});
|
|
22858
|
+
app.post("/telemetry/onboarding", async (request, reply) => {
|
|
22859
|
+
const parsed = onboardingTelemetryEventSchema.safeParse(request.body);
|
|
22860
|
+
if (!parsed.success) {
|
|
22861
|
+
throw validationError("Invalid onboarding telemetry event", {
|
|
22862
|
+
issues: parsed.error.issues.map((issue) => ({
|
|
22863
|
+
code: issue.code,
|
|
22864
|
+
path: issue.path.join(".")
|
|
22865
|
+
}))
|
|
22866
|
+
});
|
|
22867
|
+
}
|
|
22868
|
+
opts.recordOnboardingEvent?.(parsed.data);
|
|
22869
|
+
return reply.status(202).send({ accepted: Boolean(opts.recordOnboardingEvent) });
|
|
22870
|
+
});
|
|
22780
22871
|
}
|
|
22781
22872
|
|
|
22782
22873
|
// ../api-routes/src/schedules.ts
|
|
@@ -24798,6 +24889,24 @@ function stableStringify3(value) {
|
|
|
24798
24889
|
}
|
|
24799
24890
|
|
|
24800
24891
|
// ../api-routes/src/google.ts
|
|
24892
|
+
var GOOGLE_OAUTH_COMPLETE_MESSAGE = "canonry:google-oauth-complete";
|
|
24893
|
+
function googleOAuthSuccessHtml(type) {
|
|
24894
|
+
const message = JSON.stringify({
|
|
24895
|
+
type: GOOGLE_OAUTH_COMPLETE_MESSAGE,
|
|
24896
|
+
connectionType: type
|
|
24897
|
+
});
|
|
24898
|
+
return `<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
24899
|
+
<h2>Connected successfully!</h2>
|
|
24900
|
+
<p>Google ${type.toUpperCase()} has been linked to your domain.</p>
|
|
24901
|
+
<p style="color:#888">This window closes automatically.</p>
|
|
24902
|
+
<script>
|
|
24903
|
+
if (window.opener && !window.opener.closed) {
|
|
24904
|
+
window.opener.postMessage(${message}, '*')
|
|
24905
|
+
window.close()
|
|
24906
|
+
}
|
|
24907
|
+
</script>
|
|
24908
|
+
</body></html>`;
|
|
24909
|
+
}
|
|
24801
24910
|
function isOnProjectDomain(url, projectDomain) {
|
|
24802
24911
|
const urlHost = hostOf(url);
|
|
24803
24912
|
const projectHost = hostOf(projectDomain);
|
|
@@ -25123,13 +25232,7 @@ async function googleRoutes(app, opts) {
|
|
|
25123
25232
|
entityId: type,
|
|
25124
25233
|
diff: { domain, type, propertyId }
|
|
25125
25234
|
});
|
|
25126
|
-
return reply.type("text/html").send(
|
|
25127
|
-
`<html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
25128
|
-
<h2>Connected successfully!</h2>
|
|
25129
|
-
<p>Google ${type.toUpperCase()} has been linked to your domain.</p>
|
|
25130
|
-
<p style="color:#888">You can close this tab.</p>
|
|
25131
|
-
</body></html>`
|
|
25132
|
-
);
|
|
25235
|
+
return reply.type("text/html").send(googleOAuthSuccessHtml(type));
|
|
25133
25236
|
}
|
|
25134
25237
|
app.get("/google/callback", async (request, reply) => {
|
|
25135
25238
|
return handleOAuthCallback(request, reply);
|
|
@@ -44159,7 +44262,8 @@ async function apiRoutes(app, opts) {
|
|
|
44159
44262
|
await api.register(competitorRoutes);
|
|
44160
44263
|
await api.register(runRoutes, {
|
|
44161
44264
|
onRunCreated: opts.onRunCreated,
|
|
44162
|
-
validProviderNames: opts.providerAdapters?.map((a) => a.name)
|
|
44265
|
+
validProviderNames: opts.providerAdapters?.map((a) => a.name),
|
|
44266
|
+
getRunnableProviderNames: opts.getRunnableProviderNames
|
|
44163
44267
|
});
|
|
44164
44268
|
await api.register(applyRoutes, {
|
|
44165
44269
|
onScheduleUpdated: opts.onScheduleUpdated,
|
|
@@ -44210,7 +44314,8 @@ async function apiRoutes(app, opts) {
|
|
|
44210
44314
|
});
|
|
44211
44315
|
await api.register(telemetryRoutes, {
|
|
44212
44316
|
getTelemetryStatus: opts.getTelemetryStatus,
|
|
44213
|
-
setTelemetryEnabled: opts.setTelemetryEnabled
|
|
44317
|
+
setTelemetryEnabled: opts.setTelemetryEnabled,
|
|
44318
|
+
recordOnboardingEvent: opts.recordOnboardingEvent
|
|
44214
44319
|
});
|
|
44215
44320
|
await api.register(adsRoutes, {
|
|
44216
44321
|
adsCredentialStore: opts.adsCredentialStore,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UVWI6VPP.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
siteAuditPages,
|
|
120
120
|
siteAuditSnapshots,
|
|
121
121
|
usageCounters
|
|
122
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-PBTB3FCN.js";
|
|
123
123
|
import {
|
|
124
124
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
125
125
|
AGENT_PROVIDER_IDS,
|
|
@@ -135,6 +135,7 @@ import {
|
|
|
135
135
|
LLM_CAPABILITIES,
|
|
136
136
|
LlmCapabilities,
|
|
137
137
|
MemorySources,
|
|
138
|
+
ONBOARDING_FLOW_VERSION,
|
|
138
139
|
ProviderNames,
|
|
139
140
|
ResearchQueryStatuses,
|
|
140
141
|
ResearchRunStatuses,
|
|
@@ -159,6 +160,7 @@ import {
|
|
|
159
160
|
agentMemoryUpsertRequestSchema,
|
|
160
161
|
authInvalid,
|
|
161
162
|
authRequired,
|
|
163
|
+
bucketOnboardingCount,
|
|
162
164
|
buildRunErrorFromMessages,
|
|
163
165
|
classifySkillFile,
|
|
164
166
|
coerceSkillManifest,
|
|
@@ -199,7 +201,7 @@ import {
|
|
|
199
201
|
validationError,
|
|
200
202
|
winnabilityClassLabel,
|
|
201
203
|
withRetry
|
|
202
|
-
} from "./chunk-
|
|
204
|
+
} from "./chunk-7RF5EDUO.js";
|
|
203
205
|
|
|
204
206
|
// src/telemetry.ts
|
|
205
207
|
import crypto from "crypto";
|
|
@@ -214,8 +216,42 @@ var ANON_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]
|
|
|
214
216
|
function shouldDropTelemetryEvent(event, properties) {
|
|
215
217
|
return isGhostTelemetryEvent(event, properties);
|
|
216
218
|
}
|
|
219
|
+
function bucketCliCommandDuration(durationMs) {
|
|
220
|
+
const duration = Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0;
|
|
221
|
+
if (duration < 1e3) return "under_1s";
|
|
222
|
+
if (duration < 1e4) return "1s_to_10s";
|
|
223
|
+
if (duration < 6e4) return "10s_to_1m";
|
|
224
|
+
if (duration < 3e5) return "1m_to_5m";
|
|
225
|
+
if (duration < 18e5) return "5m_to_30m";
|
|
226
|
+
return "30m_or_more";
|
|
227
|
+
}
|
|
228
|
+
function trackCliCommandFinished(input) {
|
|
229
|
+
trackEvent(
|
|
230
|
+
"cli.command.finished",
|
|
231
|
+
{
|
|
232
|
+
command: input.command,
|
|
233
|
+
success: input.success,
|
|
234
|
+
duration_bucket: bucketCliCommandDuration(input.durationMs),
|
|
235
|
+
...input.setupState ? { setup_state: input.setupState } : {}
|
|
236
|
+
},
|
|
237
|
+
input.errorCode ? { errorCode: input.errorCode } : void 0
|
|
238
|
+
);
|
|
239
|
+
}
|
|
217
240
|
var SESSION_ID = crypto.randomUUID();
|
|
218
241
|
var CURRENT_SOURCE = "cli";
|
|
242
|
+
var TELEMETRY_SOURCE_VALUES = /* @__PURE__ */ new Set([
|
|
243
|
+
"cli",
|
|
244
|
+
"cli-server",
|
|
245
|
+
"api",
|
|
246
|
+
"mcp-server",
|
|
247
|
+
"wp-plugin",
|
|
248
|
+
"dashboard",
|
|
249
|
+
"agent-runtime"
|
|
250
|
+
]);
|
|
251
|
+
function envSourceOverride() {
|
|
252
|
+
const raw = process.env.CANONRY_TELEMETRY_SOURCE?.trim();
|
|
253
|
+
return raw && TELEMETRY_SOURCE_VALUES.has(raw) ? raw : void 0;
|
|
254
|
+
}
|
|
219
255
|
function setTelemetrySource(source) {
|
|
220
256
|
CURRENT_SOURCE = source;
|
|
221
257
|
}
|
|
@@ -331,9 +367,10 @@ function trackEvent(event, properties, options) {
|
|
|
331
367
|
const anonymousId = getOrCreateAnonymousId();
|
|
332
368
|
if (!anonymousId) return;
|
|
333
369
|
const payload = {
|
|
370
|
+
eventId: options?.eventId ?? crypto.randomUUID(),
|
|
334
371
|
anonymousId,
|
|
335
372
|
sessionId: SESSION_ID,
|
|
336
|
-
source: options?.source ?? CURRENT_SOURCE,
|
|
373
|
+
source: options?.source ?? envSourceOverride() ?? CURRENT_SOURCE,
|
|
337
374
|
event,
|
|
338
375
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
376
|
version: VERSION,
|
|
@@ -347,13 +384,17 @@ function trackEvent(event, properties, options) {
|
|
|
347
384
|
const controller = new AbortController();
|
|
348
385
|
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
349
386
|
timeout.unref();
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
387
|
+
try {
|
|
388
|
+
void fetch(TELEMETRY_ENDPOINT, {
|
|
389
|
+
method: "POST",
|
|
390
|
+
headers: { "Content-Type": "application/json" },
|
|
391
|
+
body: JSON.stringify(payload),
|
|
392
|
+
signal: controller.signal
|
|
393
|
+
}).catch(() => {
|
|
394
|
+
}).finally(() => clearTimeout(timeout));
|
|
395
|
+
} catch {
|
|
396
|
+
clearTimeout(timeout);
|
|
397
|
+
}
|
|
357
398
|
}
|
|
358
399
|
|
|
359
400
|
// src/update-check.ts
|
|
@@ -3071,7 +3112,7 @@ import crypto4 from "crypto";
|
|
|
3071
3112
|
import fs2 from "fs";
|
|
3072
3113
|
import path3 from "path";
|
|
3073
3114
|
import os3 from "os";
|
|
3074
|
-
import { and as and2, eq as eq2, inArray, sql as sql2 } from "drizzle-orm";
|
|
3115
|
+
import { and as and2, eq as eq2, inArray, ne, sql as sql2 } from "drizzle-orm";
|
|
3075
3116
|
|
|
3076
3117
|
// src/run-telemetry.ts
|
|
3077
3118
|
import crypto2 from "crypto";
|
|
@@ -3278,10 +3319,18 @@ function classifyOneProviderError(message) {
|
|
|
3278
3319
|
return "UNKNOWN";
|
|
3279
3320
|
}
|
|
3280
3321
|
var JobRunner = class {
|
|
3322
|
+
/**
|
|
3323
|
+
* Invoked exactly when `activation.completed` is emitted: the project's
|
|
3324
|
+
* first non-empty, non-probe answer-visibility result. The serve process
|
|
3325
|
+
* uses it to thank the operator once; it is a UX hook, not telemetry, so it
|
|
3326
|
+
* fires (and is given) independently of whether telemetry is enabled.
|
|
3327
|
+
*/
|
|
3328
|
+
onFirstActivation;
|
|
3281
3329
|
db;
|
|
3282
3330
|
registry;
|
|
3283
3331
|
onRunCompleted;
|
|
3284
|
-
constructor(db, registry) {
|
|
3332
|
+
constructor(db, registry, opts) {
|
|
3333
|
+
this.onFirstActivation = opts?.onFirstActivation;
|
|
3285
3334
|
this.db = db;
|
|
3286
3335
|
this.registry = registry;
|
|
3287
3336
|
}
|
|
@@ -3553,6 +3602,19 @@ var JobRunner = class {
|
|
|
3553
3602
|
}),
|
|
3554
3603
|
failureCode ? { errorCode: failureCode } : void 0
|
|
3555
3604
|
);
|
|
3605
|
+
if (existingRun.kind === "answer-visibility" && runTrigger !== "probe" && totalSnapshotsInserted > 0 && !this.hasPriorActivation(projectId, runId)) {
|
|
3606
|
+
trackEvent("activation.completed", {
|
|
3607
|
+
flowVersion: ONBOARDING_FLOW_VERSION,
|
|
3608
|
+
status: finalStatus,
|
|
3609
|
+
providerCountBucket: bucketOnboardingCount(executionContext.providerCount),
|
|
3610
|
+
queryCountBucket: bucketOnboardingCount(executionContext.queryCount),
|
|
3611
|
+
snapshotCountBucket: bucketOnboardingCount(totalSnapshotsInserted)
|
|
3612
|
+
});
|
|
3613
|
+
try {
|
|
3614
|
+
this.onFirstActivation?.();
|
|
3615
|
+
} catch {
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3556
3618
|
this.incrementUsage(projectId, "runs", 1);
|
|
3557
3619
|
if (this.onRunCompleted) {
|
|
3558
3620
|
this.onRunCompleted(runId, projectId).catch((err) => {
|
|
@@ -3641,8 +3703,18 @@ var JobRunner = class {
|
|
|
3641
3703
|
}
|
|
3642
3704
|
providerReservations.clear();
|
|
3643
3705
|
}
|
|
3706
|
+
hasPriorActivation(projectId, currentRunId) {
|
|
3707
|
+
return this.db.select({ id: querySnapshots.id }).from(querySnapshots).innerJoin(runs, eq2(querySnapshots.runId, runs.id)).where(and2(
|
|
3708
|
+
eq2(runs.projectId, projectId),
|
|
3709
|
+
eq2(runs.kind, "answer-visibility"),
|
|
3710
|
+
inArray(runs.status, ["completed", "partial"]),
|
|
3711
|
+
ne(runs.trigger, "probe"),
|
|
3712
|
+
ne(runs.id, currentRunId)
|
|
3713
|
+
)).limit(1).get() !== void 0;
|
|
3714
|
+
}
|
|
3644
3715
|
getRunState(runId) {
|
|
3645
3716
|
return this.db.select({
|
|
3717
|
+
kind: runs.kind,
|
|
3646
3718
|
status: runs.status,
|
|
3647
3719
|
finishedAt: runs.finishedAt,
|
|
3648
3720
|
error: runs.error,
|
|
@@ -3697,6 +3769,21 @@ function buildPhases(input) {
|
|
|
3697
3769
|
return { setup_ms, provider_call_ms, total_ms };
|
|
3698
3770
|
}
|
|
3699
3771
|
|
|
3772
|
+
// src/activation-notice.ts
|
|
3773
|
+
var ACTIVATION_NOTICE = "\n\u2605 First sweep finished. Your AI visibility baseline is in the dashboard.\n If Canonry is useful, a GitHub star helps others find it:\n https://github.com/Canonry/canonry\n\n";
|
|
3774
|
+
function maybeShowActivationNotice(io = {}) {
|
|
3775
|
+
const isTTY = io.isTTY ?? Boolean(process.stderr.isTTY);
|
|
3776
|
+
const write = io.write ?? ((line) => process.stderr.write(line));
|
|
3777
|
+
try {
|
|
3778
|
+
if (!isTTY) return;
|
|
3779
|
+
const raw = loadConfigRaw();
|
|
3780
|
+
if (!raw || raw.activationNoticeShown === true) return;
|
|
3781
|
+
saveConfigPatch({ activationNoticeShown: true });
|
|
3782
|
+
write(ACTIVATION_NOTICE);
|
|
3783
|
+
} catch {
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3700
3787
|
// src/gsc-sync.ts
|
|
3701
3788
|
import crypto5 from "crypto";
|
|
3702
3789
|
import { eq as eq3, and as and3, sql as sql3 } from "drizzle-orm";
|
|
@@ -7032,7 +7119,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
7032
7119
|
return result;
|
|
7033
7120
|
}
|
|
7034
7121
|
async function backfillInsightsCommand(project, opts) {
|
|
7035
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
7122
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-JEFHUGVX.js");
|
|
7036
7123
|
const config = loadConfig();
|
|
7037
7124
|
const db = createClient(config.database);
|
|
7038
7125
|
migrate(db);
|
|
@@ -11930,7 +12017,12 @@ async function createServer(opts) {
|
|
|
11930
12017
|
}
|
|
11931
12018
|
const port = opts.config.port ?? 4100;
|
|
11932
12019
|
const serverUrl = `http://localhost:${port}`;
|
|
11933
|
-
const jobRunner = new JobRunner(opts.db, registry
|
|
12020
|
+
const jobRunner = new JobRunner(opts.db, registry, {
|
|
12021
|
+
// The one-time first-sweep thank-you. Lives on the serve console because
|
|
12022
|
+
// runs execute here, including the foreground serve that init hands off
|
|
12023
|
+
// to. TTY-gated inside, so supervised deployments never see it.
|
|
12024
|
+
onFirstActivation: () => maybeShowActivationNotice()
|
|
12025
|
+
});
|
|
11934
12026
|
jobRunner.recoverStaleRuns();
|
|
11935
12027
|
const notifier = new Notifier(opts.db, serverUrl);
|
|
11936
12028
|
const intelligenceService = new IntelligenceService(opts.db);
|
|
@@ -13059,6 +13151,7 @@ async function createServer(opts) {
|
|
|
13059
13151
|
app.log.error({ runId, err }, "Job runner failed");
|
|
13060
13152
|
});
|
|
13061
13153
|
},
|
|
13154
|
+
getRunnableProviderNames: () => registry.getAll().map((provider) => provider.adapter.name),
|
|
13062
13155
|
onProviderUpdate: (providerName, apiKey, model, baseUrl, incomingQuota) => {
|
|
13063
13156
|
const name = providerName;
|
|
13064
13157
|
if (!adapterMap[name]) return null;
|
|
@@ -13196,6 +13289,10 @@ async function createServer(opts) {
|
|
|
13196
13289
|
saveConfigPatch(config);
|
|
13197
13290
|
opts.config.telemetry = enabled;
|
|
13198
13291
|
},
|
|
13292
|
+
recordOnboardingEvent: (event) => {
|
|
13293
|
+
const { event: eventName, eventId, ...properties } = event;
|
|
13294
|
+
trackEvent(eventName, properties, { source: "dashboard", eventId });
|
|
13295
|
+
},
|
|
13199
13296
|
onCdpConfigure: async (host, port2) => {
|
|
13200
13297
|
if (!opts.config.cdp) opts.config.cdp = {};
|
|
13201
13298
|
opts.config.cdp.host = host;
|
|
@@ -13434,6 +13531,7 @@ function parseQueryResponse(raw, count) {
|
|
|
13434
13531
|
}
|
|
13435
13532
|
|
|
13436
13533
|
export {
|
|
13534
|
+
trackCliCommandFinished,
|
|
13437
13535
|
setTelemetrySource,
|
|
13438
13536
|
isTelemetryEnabled,
|
|
13439
13537
|
getOrCreateAnonymousId,
|