@canonry/canonry 5.34.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +15 -3
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -8
- package/assets/assets/{AuditHistoryPanel-Ddg6JIi3.js → AuditHistoryPanel-Bz2NVB6-.js} +1 -1
- package/assets/assets/{BacklinksPage-DI9BmaYt.js → BacklinksPage-Dte5-usf.js} +1 -1
- package/assets/assets/{HistoryPage-CACIL005.js → HistoryPage-C1S4nw3I.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-D_i3t0DT.js → MeasurementPropertyPage-DWjsHZdt.js} +1 -1
- package/assets/assets/ProjectPage-CAJ9XvNt.js +8 -0
- package/assets/assets/RunsPage-D9wEJFfV.js +1 -0
- package/assets/assets/SettingsPage-CN3Gew7Y.js +1 -0
- package/assets/assets/SiteHealthSection-Bft5TfoU.js +4 -0
- package/assets/assets/TrafficPage-tkNvySQH.js +40 -0
- package/assets/assets/{TrafficSourceDetailPage-BPqNmuws.js → TrafficSourceDetailPage-RhRfPizo.js} +1 -1
- package/assets/assets/{extract-error-message-B7OeWj0x.js → extract-error-message-tfl_SU7q.js} +1 -1
- package/assets/assets/index-B11OXdEC.css +1 -0
- package/assets/assets/index-Cu_xgdb1.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min-jiMNkILi.js → react-sigma_core.esm.min-BUu3mcQJ.js} +1 -1
- package/assets/assets/{v2-overview-adapter-Caj3bHQE.js → v2-overview-adapter-CzeGhaXn.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-VHPGBX4P.js → chunk-4NC5ZAC2.js} +3 -3
- package/dist/{chunk-NZCKJMLC.js → chunk-6M7HSNXJ.js} +2 -2
- package/dist/{chunk-AIAVZY6L.js → chunk-F4G7I7T7.js} +18 -132
- package/dist/{chunk-NOIK3FUO.js → chunk-HAALQW4R.js} +48 -15
- package/dist/{chunk-25H4K7KL.js → chunk-K4356AGP.js} +7 -7
- package/dist/{chunk-QTETJEHI.js → chunk-XGERCNUR.js} +6 -3
- package/dist/{chunk-G2K5KCUY.js → chunk-ZO62Z2WN.js} +27 -15
- package/dist/cli.js +13 -11
- package/dist/{demo-server-UI24OAHX.js → demo-server-54OUTZMY.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-ZKFLGJ23.js → intelligence-service-MEIAMMLT.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +12 -12
- package/assets/assets/ProjectPage-d1djqfh3.js +0 -8
- package/assets/assets/RunRow-CjhvvA6g.js +0 -1
- package/assets/assets/RunsPage-DuBhw2uc.js +0 -1
- package/assets/assets/SettingsPage-MUpfB0HH.js +0 -1
- package/assets/assets/SiteHealthSection-6qdXlFcO.js +0 -4
- package/assets/assets/TrafficPage--nGSOgqn.js +0 -40
- package/assets/assets/index-C4lreGwR.js +0 -86
- package/assets/assets/index-DX5M4MHr.css +0 -1
|
@@ -128,7 +128,7 @@ import {
|
|
|
128
128
|
trafficSeriesGranularitySchema,
|
|
129
129
|
visibilityCompareSelectionSchema,
|
|
130
130
|
visibilityReportRequestSchema
|
|
131
|
-
} from "./chunk-
|
|
131
|
+
} from "./chunk-HAALQW4R.js";
|
|
132
132
|
|
|
133
133
|
// src/cli-error.ts
|
|
134
134
|
function isMachineFormat(format) {
|
|
@@ -456,7 +456,7 @@ var MEASUREMENT_CHANGES_AGENT_ROWS = 20;
|
|
|
456
456
|
var measurementChangesInputSchema = measurementChangesQuerySchema.safeExtend({
|
|
457
457
|
project: projectNameSchema,
|
|
458
458
|
queryClass: measurementQueryClassFilterSchema.default(AGENT_DEFAULT_QUERY_CLASS).describe("Defaults to non-brand. Call once per class; all pools both classes in rows and distribution."),
|
|
459
|
-
limit: z3.number().int().positive().max(MEASUREMENT_CHANGES_AGENT_ROWS).optional().describe(`Changed rows, largest move first. Default
|
|
459
|
+
limit: z3.number().int().positive().max(MEASUREMENT_CHANGES_AGENT_ROWS).optional().describe(`Changed rows, largest move first, every move beyond noise before any within it. Default and maximum ${MEASUREMENT_CHANGES_AGENT_ROWS}; distribution counts every Property whatever the limit.`)
|
|
460
460
|
}).strict();
|
|
461
461
|
var measurementDataQualityInputSchema = measurementDataQualityQuerySchema.extend({
|
|
462
462
|
project: projectNameSchema
|
|
@@ -948,7 +948,7 @@ var contentTargetsInputSchema = z3.object({
|
|
|
948
948
|
var contentBriefInputSchema = z3.object({
|
|
949
949
|
project: projectNameSchema,
|
|
950
950
|
targetRef: z3.string().min(1).describe("Stable target ref from canonry_content_targets. The target must be ownable; ceded targets are rejected."),
|
|
951
|
-
provider: z3.string().optional().describe("Optional provider override (claude|openai|gemini|zai
|
|
951
|
+
provider: z3.string().optional().describe("Optional provider override (claude|openai|gemini|zai)."),
|
|
952
952
|
model: z3.string().optional().describe("Optional model override within the chosen provider."),
|
|
953
953
|
forceRefresh: z3.boolean().optional().describe("Force a fresh synthesis even if a cached brief exists.")
|
|
954
954
|
});
|
|
@@ -1515,7 +1515,7 @@ var canonryMcpTools = [
|
|
|
1515
1515
|
defineTool({
|
|
1516
1516
|
name: "canonry_analytics_sources",
|
|
1517
1517
|
title: "Get cited-source rankings",
|
|
1518
|
-
description: "Where AI engines cite from for a project: cited domains ranked by how many answers cite them, each tagged with a category and a surface class (own / direct-competitor / ota-aggregator / editorial-media / other), plus a surface-class roll-up and a per-provider breakdown. Project-wide: without `queryClass` it pools branded and non-brand answers, where branded queries inflate your own domain. With `queryClass` set and neither `runId` nor `window`, this tool reads the latest sweep (`runId` latest); pass `window` to pool every sweep in it. `pooledAcrossRuns` true (`runCount` above 1) means the counts sum several runs and `runIds` is their scope; the legacy `runId` field is then only the newest run in the window. On a v2 measurement plan the class comes from the plan, the same answers as canonry_competitor_landscape with scope=all-markets. `countUnits` says what each count counts: `domainTotal` is distinct domains, never answers or citations. Compare engines by answer count or `answerShare`, never by rank position. Counts come from each answer's stored source list, so every engine is included, Gemini too. A domain counts at most once per answer; `answerShare` is the share of all answers in scope that cite it. `providersWithoutSources` names engines that answered but cited nothing. `limit` caps each ranked list (default 10 for this tool, which keeps each engine list whole); a long-tail rollup keeps the totals. The per-query breakdown is omitted unless `includeByQuery` is true. No LLM calls; probe runs excluded.",
|
|
1518
|
+
description: "Where AI engines cite from for a project: cited domains ranked by how many answers cite them, each tagged with a category and a surface class (own / direct-competitor / ota-aggregator / editorial-media / other), plus a surface-class roll-up and a per-provider breakdown. Every entry carries rank, its position by count in that list over the whole scope (equal counts share a rank); state where a domain places only from its rank, never by counting rows. Project-wide: without `queryClass` it pools branded and non-brand answers, where branded queries inflate your own domain. With `queryClass` set and neither `runId` nor `window`, this tool reads the latest sweep (`runId` latest); pass `window` to pool every sweep in it. `pooledAcrossRuns` true (`runCount` above 1) means the counts sum several runs and `runIds` is their scope; the legacy `runId` field is then only the newest run in the window. On a v2 measurement plan the class comes from the plan, the same answers as canonry_competitor_landscape with scope=all-markets. `countUnits` says what each count counts: `domainTotal` is distinct domains, never answers or citations. Compare engines by answer count or `answerShare`, never by rank position. Counts come from each answer's stored source list, so every engine is included, Gemini too. A domain counts at most once per answer; `answerShare` is the share of all answers in scope that cite it. `providersWithoutSources` names engines that answered but cited nothing. `limit` caps each ranked list (default 10 for this tool, which keeps each engine list whole); a long-tail rollup keeps the totals. The per-query breakdown is omitted unless `includeByQuery` is true. No LLM calls; probe runs excluded.",
|
|
1519
1519
|
access: "read",
|
|
1520
1520
|
tier: "monitoring",
|
|
1521
1521
|
inputSchema: z3.object({
|
|
@@ -2842,7 +2842,7 @@ var canonryMcpTools = [
|
|
|
2842
2842
|
defineTool({
|
|
2843
2843
|
name: "canonry_measurement_portfolio_summary",
|
|
2844
2844
|
title: "Summarize measured Properties",
|
|
2845
|
-
description: "Start here for best and worst Properties. One call returns the weakest Properties, the names answers wrote instead of them, and the domains engines cited for them. Defaults to non-brand queries; state the returned queryClass and never pool branded with non-brand. Returns at most 4 Property rows per list, the most that fit one result; a larger limit is lowered and limitNote says so. For other rows pass groupKey (one metro's weakest), or page canonry_measurement_overview. Mention (answer text names the Property) and citation (a source URL on its page) are separate: report each as numerator/denominator, with any unattributed count beside mention. Denominators count answers, one per query per engine (queries x engines = answers). Group Properties only by each row's metro and submarkets, never by label. namedInsteadInAnswerText lists names written in the answer text of answers that neither named nor cited the Property, counted by answer: they are not citations, and namedInsteadInAnswerTextTotal counts distinct names, not answers. citedDomains and weakestAnswerSources count answers citing each domain, every engine included; weakestAnswerSources pools the weakest rows plus every tied Property, each answer once, and its domainTotal counts distinct domains, not citations. When tiedAtWeakest is set, tiedAtWeakest.count Properties share the weakest rates and the rows are the first of them by name, not a rank: report the count, tiedAtWeakest.byMetro (tied Properties per metro over the whole tie; a Property in two metros counts in both) and tiedAtWeakest.namedInstead (names written instead across the whole tie, counted by distinct answer; namedInsteadTotal is distinct names), and call the rows examples. mentionRanking.strongest/.weakest rank every Property with an available mention rate; .excluded lists the rest with reasons. markets lists every market at one level, worst-first (every metro, or a groupKey's submarkets), whatever the limit. Markets may share Properties and never sum to portfolio totals. Reads stored data only; never starts provider work.",
|
|
2845
|
+
description: "Start here for best and worst Properties. One call returns the weakest Properties, the names answers wrote instead of them, and the domains engines cited for them. Defaults to non-brand queries; state the returned queryClass and never pool branded with non-brand. Returns at most 4 Property rows per list, the most that fit one result; a larger limit is lowered and limitNote says so. For other rows pass groupKey (one metro's weakest), or page canonry_measurement_overview. Mention (answer text names the Property) and citation (a source URL on its page) are separate: report each as numerator/denominator, with any unattributed count beside mention. Denominators count answers, one per query per engine (queries x engines = answers). Group Properties only by each row's metro and submarkets, never by label. namedInsteadInAnswerText lists names written in the answer text of answers that neither named nor cited the Property, counted by answer: they are not citations, and namedInsteadInAnswerTextTotal counts distinct names, not answers. citedDomains and weakestAnswerSources count answers citing each domain, every engine included; weakestAnswerSources pools the weakest rows plus every tied Property, each answer once, and its domainTotal counts distinct domains, not citations. weakestAnswerSources.ownDomainAnswers counts those answers that cite any of your own domains. A Property's citationCoverage counts only answers citing that Property's own page, so 0% citation does not mean your domain went uncited; use ownDomainAnswers for that. When tiedAtWeakest is set, tiedAtWeakest.count Properties share the weakest rates and the rows are the first of them by name, not a rank: report the count, tiedAtWeakest.byMetro (tied Properties per metro over the whole tie; a Property in two metros counts in both) and tiedAtWeakest.namedInstead (names written instead across the whole tie, counted by distinct answer; namedInsteadTotal is distinct names), and call the rows examples. mentionRanking.strongest/.weakest rank every Property with an available mention rate; .excluded lists the rest with reasons. markets lists every market at one level, worst-first (every metro, or a groupKey's submarkets), whatever the limit. Markets may share Properties and never sum to portfolio totals. Reads stored data only; never starts provider work.",
|
|
2846
2846
|
access: "read",
|
|
2847
2847
|
tier: "monitoring",
|
|
2848
2848
|
inputSchema: measurementPortfolioSummaryInputSchema,
|
|
@@ -2902,7 +2902,7 @@ var canonryMcpTools = [
|
|
|
2902
2902
|
defineTool({
|
|
2903
2903
|
name: "canonry_measurement_changes",
|
|
2904
2904
|
title: "Compare stored measurement changes",
|
|
2905
|
-
description: "What changed since the previous sweep: compares the displayed run with the previous run of the same plan revision and execution identity; an incompatible pair returns unavailable with a reason, never a trend. Defaults to non-brand queries: call once per class (queryClass branded for the branded move) and never report one class as the whole. queryClass all pools both classes in rows and distribution and adds metricsByClass, the headline metrics per class; report those per class, never the pooled metrics alone. changedProperties come largest move first (answers that named the Property, then answers that cited it; sort label for alphabetical),
|
|
2905
|
+
description: "What changed since the previous sweep: compares the displayed run with the previous run of the same plan revision and execution identity; an incompatible pair returns unavailable with a reason, never a trend. Defaults to non-brand queries: call once per class (queryClass branded for the branded move) and never report one class as the whole. queryClass all pools both classes in rows and distribution and adds metricsByClass, the headline metrics per class; report those per class, never the pooled metrics alone. changedProperties come largest move first (answers that named the Property, then answers that cited it; sort label for alphabetical), every move beyond noise before any within it, 20 rows by default and at most; totalProperties above the rows returned means rows you did not see. distribution counts every Property in scope: improved, declined, mixed, withinNoise, unchanged, notComparable. mentionAnswersDelta and citationAnswersDelta are signed answer counts; denominatorChanged true means a rate was taken over a different number of answers, so those counts are not like for like and the coverage rates give the direction. A move is the rate change times the larger answer count (the count change when the denominator held). withinNoise true means every move was 2 answers or fewer: within noise, never a gain, loss, trend or regression. mentionWithinNoise and citationWithinNoise apply the same rule to each signal alone: a Property beyond noise can still have one signal within noise, and that signal is not a gain or loss. Report every headline metric, branded included, as previous numerator/denominator to current numerator/denominator. Reads stored data only; it never starts provider work.",
|
|
2906
2906
|
access: "read",
|
|
2907
2907
|
tier: "monitoring",
|
|
2908
2908
|
inputSchema: measurementChangesInputSchema,
|
|
@@ -2910,7 +2910,7 @@ var canonryMcpTools = [
|
|
|
2910
2910
|
openApiOperations: ["GET /api/v1/projects/{name}/measurement-changes"],
|
|
2911
2911
|
handler: (client, input) => {
|
|
2912
2912
|
const { project, ...query } = input;
|
|
2913
|
-
return client.getMeasurementChanges(project, { ...query, queryClass: agentQueryClass(query.queryClass) });
|
|
2913
|
+
return client.getMeasurementChanges(project, { ...query, limit: query.limit ?? MEASUREMENT_CHANGES_AGENT_ROWS, queryClass: agentQueryClass(query.queryClass) });
|
|
2914
2914
|
}
|
|
2915
2915
|
}),
|
|
2916
2916
|
defineTool({
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
PACKAGE_VERSION,
|
|
6
6
|
canonryMcpTools,
|
|
7
7
|
isMachineFormat
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-K4356AGP.js";
|
|
9
9
|
import {
|
|
10
10
|
AGENT_PROVIDER_IDS,
|
|
11
11
|
CANONRY_NPM_PACKAGE_URL,
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
formatCanonryPluginVersionMismatch,
|
|
29
29
|
isInstallMethod,
|
|
30
30
|
isReadOnlyKey,
|
|
31
|
+
isRetiredAgentProviderId,
|
|
31
32
|
isSensitiveDiagnosticQueryKey,
|
|
32
33
|
isStrictSemver,
|
|
33
34
|
normalizeAgentSlug,
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
skillsClientSchema,
|
|
50
51
|
upgradeCaveatFor,
|
|
51
52
|
upgradeCommandFor
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-HAALQW4R.js";
|
|
53
54
|
|
|
54
55
|
// src/config.ts
|
|
55
56
|
import fs from "fs";
|
|
@@ -429,9 +430,11 @@ Do not use "canonry init --force" for recovery. It replaces credentials without
|
|
|
429
430
|
});
|
|
430
431
|
}
|
|
431
432
|
if (!agentProviderSchema.safeParse(parsed.agent?.provider).success) {
|
|
433
|
+
const pinned = parsed.agent?.provider;
|
|
434
|
+
const retired = typeof pinned === "string" && isRetiredAgentProviderId(pinned);
|
|
432
435
|
throw new CliError({
|
|
433
436
|
code: "CONFIG_INVALID",
|
|
434
|
-
message: `Invalid config at ${configPath}: agent.provider must be one of ${AGENT_PROVIDER_IDS.join(", ")}, or left blank.`
|
|
437
|
+
message: retired ? `Invalid config at ${configPath}: agent.provider ${pinned} was removed. Pin one of ${AGENT_PROVIDER_IDS.join(", ")} (with a key under providers.<name>.apiKey), or leave it blank.` : `Invalid config at ${configPath}: agent.provider must be one of ${AGENT_PROVIDER_IDS.join(", ")}, or left blank.`
|
|
435
438
|
});
|
|
436
439
|
}
|
|
437
440
|
if (!agentModelSchema.safeParse(parsed.agent?.model).success) {
|
|
@@ -869,7 +869,7 @@ import {
|
|
|
869
869
|
wordpressSchemaDeployResultDtoSchema,
|
|
870
870
|
wordpressSchemaStatusResultDtoSchema,
|
|
871
871
|
wordpressStatusDtoSchema
|
|
872
|
-
} from "./chunk-
|
|
872
|
+
} from "./chunk-HAALQW4R.js";
|
|
873
873
|
|
|
874
874
|
// src/intelligence-service.ts
|
|
875
875
|
import { eq as eq82, desc as desc36, asc as asc15, and as and69, ne as ne14, or as or20, inArray as inArray32, gte as gte22, lte as lte20, isNull as isNull16, sql as sql31, exists } from "drizzle-orm";
|
|
@@ -11294,7 +11294,7 @@ function gapTone(gapCount, totalCount) {
|
|
|
11294
11294
|
|
|
11295
11295
|
// ../intelligence/src/visibility-score.ts
|
|
11296
11296
|
function buildVisibilityScore(snapshots, options) {
|
|
11297
|
-
const tooltip = "An
|
|
11297
|
+
const tooltip = "An AI engine used a page on your domain as a source for its answer.";
|
|
11298
11298
|
if (snapshots.length === 0) {
|
|
11299
11299
|
return {
|
|
11300
11300
|
label: "Citation Coverage",
|
|
@@ -11326,13 +11326,13 @@ function buildVisibilityScore(snapshots, options) {
|
|
|
11326
11326
|
tooltip,
|
|
11327
11327
|
trend: [],
|
|
11328
11328
|
progress: score,
|
|
11329
|
-
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length}
|
|
11329
|
+
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length} engines` : void 0
|
|
11330
11330
|
};
|
|
11331
11331
|
}
|
|
11332
11332
|
|
|
11333
11333
|
// ../intelligence/src/mention-coverage.ts
|
|
11334
11334
|
function buildMentionCoverage(snapshots, options) {
|
|
11335
|
-
const tooltip = "Your domain or company name was in the answer returned by the
|
|
11335
|
+
const tooltip = "Your domain or company name was in the answer returned by the AI engine.";
|
|
11336
11336
|
if (snapshots.length === 0) {
|
|
11337
11337
|
return {
|
|
11338
11338
|
label: "Mention Coverage",
|
|
@@ -11364,7 +11364,7 @@ function buildMentionCoverage(snapshots, options) {
|
|
|
11364
11364
|
tooltip,
|
|
11365
11365
|
trend: [],
|
|
11366
11366
|
progress: score,
|
|
11367
|
-
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length}
|
|
11367
|
+
providerCoverage: isPartialProviderRun ? `${runApiProviderCount} of ${options.configuredApiProviders.length} engines` : void 0
|
|
11368
11368
|
};
|
|
11369
11369
|
}
|
|
11370
11370
|
|
|
@@ -11398,7 +11398,7 @@ function buildGapQueryScore(snapshots) {
|
|
|
11398
11398
|
return {
|
|
11399
11399
|
label: "Citation Gaps",
|
|
11400
11400
|
value: `${gapCount}`,
|
|
11401
|
-
delta: `${gapCount} of ${totalCount}
|
|
11401
|
+
delta: `${gapCount} of ${totalCount} ${totalCount === 1 ? "query" : "queries"}`,
|
|
11402
11402
|
tone: gapTone(gapCount, totalCount),
|
|
11403
11403
|
description: gapCount > 0 ? `${gapCount} tracked ${gapQueryLabel} currently cite competitors without citing your domain.` : "No competitive citation gaps detected in the latest visibility run.",
|
|
11404
11404
|
tooltip,
|
|
@@ -11435,7 +11435,7 @@ function buildMentionGapScore(snapshots) {
|
|
|
11435
11435
|
return {
|
|
11436
11436
|
label: "Mention Gaps",
|
|
11437
11437
|
value: `${gapCount}`,
|
|
11438
|
-
delta: `${gapCount} of ${totalCount}
|
|
11438
|
+
delta: `${gapCount} of ${totalCount} ${totalCount === 1 ? "query" : "queries"}`,
|
|
11439
11439
|
tone: gapTone(gapCount, totalCount),
|
|
11440
11440
|
description: gapCount > 0 ? `${gapCount} tracked ${gapQueryLabel} mention competitors but never your brand.` : "No competitive mention gaps detected in the latest visibility run.",
|
|
11441
11441
|
tooltip,
|
|
@@ -25735,7 +25735,7 @@ function mentionRanking(rows, limit) {
|
|
|
25735
25735
|
truncated: eligible.length > limit
|
|
25736
25736
|
};
|
|
25737
25737
|
}
|
|
25738
|
-
function portfolioResponse(db, active, plan, query) {
|
|
25738
|
+
function portfolioResponse(db, active, plan, query, ownDomains) {
|
|
25739
25739
|
const group = query.groupKey === void 0 ? void 0 : requireGroup2(plan, query.groupKey);
|
|
25740
25740
|
const run = selectMeasurementQuestionRun(db, active.version.projectId, active, query.runId);
|
|
25741
25741
|
const requestedTargetKeys = group?.targetKeys ?? plan.targets.map((target) => target.stableKey);
|
|
@@ -25831,7 +25831,7 @@ function portfolioResponse(db, active, plan, query) {
|
|
|
25831
25831
|
},
|
|
25832
25832
|
weakestProperties: rows,
|
|
25833
25833
|
tiedAtWeakest: tie.summary,
|
|
25834
|
-
weakestAnswerSources: weakestAnswerSources([...displayed, ...tie.rows]),
|
|
25834
|
+
weakestAnswerSources: weakestAnswerSources([...displayed, ...tie.rows], ownDomains),
|
|
25835
25835
|
mentionRanking: mentionRanking(ranked, limit),
|
|
25836
25836
|
...marketRollup(plan, { run, evaluator }, group, includeNestedMarkets),
|
|
25837
25837
|
totalProperties: ranked.length,
|
|
@@ -25892,7 +25892,7 @@ function tieNamedInstead(rows) {
|
|
|
25892
25892
|
const counted = [...grouped.values()].map((row) => ({ name: row.name, answers: row.answers.size })).sort((left, right) => right.answers - left.answers || compareText13(left.name, right.name));
|
|
25893
25893
|
return { rows: counted.slice(0, MEASUREMENT_PORTFOLIO_TIE_NAMED_INSTEAD_LIMIT), total: counted.length };
|
|
25894
25894
|
}
|
|
25895
|
-
function weakestAnswerSources(rows) {
|
|
25895
|
+
function weakestAnswerSources(rows, ownDomains) {
|
|
25896
25896
|
const properties = /* @__PURE__ */ new Set();
|
|
25897
25897
|
const answers = /* @__PURE__ */ new Map();
|
|
25898
25898
|
for (const row of rows) {
|
|
@@ -25900,7 +25900,11 @@ function weakestAnswerSources(rows) {
|
|
|
25900
25900
|
for (const answer of row.population.sourceAnswers) answers.set(answer.snapshot.id, answer);
|
|
25901
25901
|
}
|
|
25902
25902
|
const domains = domainRows([...answers.values()], MEASUREMENT_PORTFOLIO_ANSWER_SOURCES_LIMIT);
|
|
25903
|
-
|
|
25903
|
+
let ownDomainAnswers = 0;
|
|
25904
|
+
for (const answer of answers.values()) {
|
|
25905
|
+
if ([...answerDomains(answer)].some((host) => hostMatchesAnyDomain(host, ownDomains))) ownDomainAnswers++;
|
|
25906
|
+
}
|
|
25907
|
+
return { properties: properties.size, answers: answers.size, domains: domains.rows, domainTotal: domains.total, ownDomainAnswers };
|
|
25904
25908
|
}
|
|
25905
25909
|
function propertyCompetitorsResponse(db, active, plan, query) {
|
|
25906
25910
|
const target = requireTarget3(plan, query.targetKey);
|
|
@@ -26112,11 +26116,14 @@ function classifyPropertyMove(mention, citation) {
|
|
|
26112
26116
|
const down = directions.some((move) => move < 0);
|
|
26113
26117
|
bucket = up && down ? "mixed" : up ? "improved" : "declined";
|
|
26114
26118
|
}
|
|
26119
|
+
const signalWithinNoise = (move) => move === null ? null : Math.abs(move) <= MEASUREMENT_CHANGES_NOISE_ANSWERS;
|
|
26115
26120
|
return {
|
|
26116
26121
|
mentionAnswersDelta: answersDelta(mention),
|
|
26117
26122
|
citationAnswersDelta: answersDelta(citation),
|
|
26118
26123
|
denominatorChanged: baseChanged(mention) || baseChanged(citation),
|
|
26119
26124
|
withinNoise: changed && withinNoise,
|
|
26125
|
+
mentionWithinNoise: signalWithinNoise(mentionMove),
|
|
26126
|
+
citationWithinNoise: signalWithinNoise(citationMove),
|
|
26120
26127
|
bucket,
|
|
26121
26128
|
mentionSize: mentionMove === null ? -1 : Math.abs(mentionMove),
|
|
26122
26129
|
citationSize: citationMove === null ? -1 : Math.abs(citationMove)
|
|
@@ -26205,7 +26212,9 @@ function changesResponse(db, active, plan, query) {
|
|
|
26205
26212
|
mentionAnswersDelta: move.mentionAnswersDelta,
|
|
26206
26213
|
citationAnswersDelta: move.citationAnswersDelta,
|
|
26207
26214
|
denominatorChanged: move.denominatorChanged,
|
|
26208
|
-
withinNoise: move.withinNoise
|
|
26215
|
+
withinNoise: move.withinNoise,
|
|
26216
|
+
mentionWithinNoise: move.mentionWithinNoise,
|
|
26217
|
+
citationWithinNoise: move.citationWithinNoise
|
|
26209
26218
|
},
|
|
26210
26219
|
move
|
|
26211
26220
|
}];
|
|
@@ -26386,7 +26395,7 @@ async function measurementPortfolioReadRoutes(app) {
|
|
|
26386
26395
|
const raw = nested === "true" || nested === "false" ? { ...request.query, includeNestedMarkets: nested === "true" } : request.query;
|
|
26387
26396
|
const query = parseLimitQuery(raw, measurementPortfolioSummaryQuerySchema, "Invalid measurement portfolio summary query");
|
|
26388
26397
|
const { active, plan } = activeV2Plan2(app.db, project.id, "Portfolio summary");
|
|
26389
|
-
return portfolioResponse(app.db, active, plan, query);
|
|
26398
|
+
return portfolioResponse(app.db, active, plan, query, effectiveDomains(project));
|
|
26390
26399
|
}
|
|
26391
26400
|
);
|
|
26392
26401
|
app.get(
|
|
@@ -31366,8 +31375,11 @@ function buildRankedList(domains, limit, answerTotal, answersWithSources) {
|
|
|
31366
31375
|
const domainTotal = all.length;
|
|
31367
31376
|
all.sort((a, b) => b.count - a.count || a.domain.localeCompare(b.domain));
|
|
31368
31377
|
const shownEntries = limit != null && limit < all.length ? all.slice(0, limit) : all;
|
|
31369
|
-
|
|
31378
|
+
let rank = 0;
|
|
31379
|
+
const ranks = all.map((d, i) => rank = i > 0 && d.count === all[i - 1].count ? rank : i + 1);
|
|
31380
|
+
const entries = shownEntries.map((d, i) => ({
|
|
31370
31381
|
domain: d.domain,
|
|
31382
|
+
rank: ranks[i],
|
|
31371
31383
|
count: d.count,
|
|
31372
31384
|
percentage: totalCitedSlots > 0 ? roundRatio(d.count / totalCitedSlots, RatioUnits.fraction) : 0,
|
|
31373
31385
|
answerShare: answerTotal > 0 ? roundRatio(d.count / answerTotal, RatioUnits.fraction) : 0,
|
|
@@ -86104,7 +86116,7 @@ var agentProvidersConfiguredCheck = {
|
|
|
86104
86116
|
status: CheckStatuses.warn,
|
|
86105
86117
|
code: "agent-providers.none-configured",
|
|
86106
86118
|
summary: "No agent LLM provider has credentials configured \u2014 the built-in Aero agent cannot run.",
|
|
86107
|
-
remediation: "Add a key for one of the agent providers (claude, openai, gemini, zai
|
|
86119
|
+
remediation: "Add a key for one of the agent providers (claude, openai, gemini, zai) under `providers.<name>.apiKey` in ~/.canonry/config.yaml, or export its env var (e.g. ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY).",
|
|
86108
86120
|
details
|
|
86109
86121
|
};
|
|
86110
86122
|
}
|
package/dist/cli.js
CHANGED
|
@@ -35,11 +35,11 @@ import {
|
|
|
35
35
|
trackCliCommandFinished,
|
|
36
36
|
trackEvent,
|
|
37
37
|
waitForServerRuntimeStartup
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-F4G7I7T7.js";
|
|
39
39
|
import {
|
|
40
40
|
autoSyncSkills,
|
|
41
41
|
formatAutoSyncNotice
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-4NC5ZAC2.js";
|
|
43
43
|
import {
|
|
44
44
|
configExists,
|
|
45
45
|
createApiClient,
|
|
@@ -56,13 +56,13 @@ import {
|
|
|
56
56
|
saveConfig,
|
|
57
57
|
saveConfigPatch,
|
|
58
58
|
shouldPrintServeSkillsNudge
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-XGERCNUR.js";
|
|
60
60
|
import {
|
|
61
61
|
DEFAULT_DEMO_TRUSTED_PROXIES,
|
|
62
62
|
trustedProxiesProblem,
|
|
63
63
|
trustedProxyProblem
|
|
64
64
|
} from "./chunk-RB2F4D3Z.js";
|
|
65
|
-
import "./chunk-
|
|
65
|
+
import "./chunk-6M7HSNXJ.js";
|
|
66
66
|
import {
|
|
67
67
|
CliError,
|
|
68
68
|
EXIT_SYSTEM_ERROR,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runMeasurementDraftAction,
|
|
75
75
|
systemError,
|
|
76
76
|
usageError
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-K4356AGP.js";
|
|
78
78
|
import {
|
|
79
79
|
CLOUDFLARE_WORKER_BINDINGS,
|
|
80
80
|
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
queries,
|
|
90
90
|
renderReportHtml,
|
|
91
91
|
runs
|
|
92
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-ZO62Z2WN.js";
|
|
93
93
|
import {
|
|
94
94
|
AdsDeliverySnapshotStatuses,
|
|
95
95
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -151,6 +151,7 @@ import {
|
|
|
151
151
|
formatSignedPointDelta,
|
|
152
152
|
gbpNegativeReviewMaxStarsSchema,
|
|
153
153
|
googleAdsMetricsWindowSchema,
|
|
154
|
+
isRetiredAgentProviderId,
|
|
154
155
|
isVisibilityCompareClassMetric,
|
|
155
156
|
logQuerySchema,
|
|
156
157
|
measurementChangesQuerySchema,
|
|
@@ -195,7 +196,7 @@ import {
|
|
|
195
196
|
visibilityCompareSelectionSchema,
|
|
196
197
|
visibilityReportRequestSchema,
|
|
197
198
|
winnabilityClassSchema
|
|
198
|
-
} from "./chunk-
|
|
199
|
+
} from "./chunk-HAALQW4R.js";
|
|
199
200
|
|
|
200
201
|
// src/cli.ts
|
|
201
202
|
import { pathToFileURL } from "url";
|
|
@@ -721,7 +722,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
721
722
|
async function demoCommand(options) {
|
|
722
723
|
const { host, port } = parseDemoListenOptions(options);
|
|
723
724
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
724
|
-
const { createDemoServer } = await import("./demo-server-
|
|
725
|
+
const { createDemoServer } = await import("./demo-server-54OUTZMY.js");
|
|
725
726
|
const app = await createDemoServer({ trustProxy });
|
|
726
727
|
let stopping = false;
|
|
727
728
|
const stop = () => {
|
|
@@ -11825,7 +11826,7 @@ Citation Rate Trends (${data.window})`);
|
|
|
11825
11826
|
for (const [provider, served] of servedEntries) {
|
|
11826
11827
|
const rawIds = served.latestServedModelIds.length > 0 ? served.latestServedModelIds.join(", ") : formatModelEvidence(served.latestObservation.state);
|
|
11827
11828
|
const substituted = mismatch[provider];
|
|
11828
|
-
const note = substituted ? `
|
|
11829
|
+
const note = substituted ? ` (not the ${formatModelEvidence(substituted.configured)} you selected)` : "";
|
|
11829
11830
|
console.log(` ${provider}: ${rawIds} at ${served.latestObservation.observedAt}${note}`);
|
|
11830
11831
|
for (const event of served.events) {
|
|
11831
11832
|
const dating = event.fromPreWindowAnchor ? " (on or before)" : "";
|
|
@@ -19874,9 +19875,10 @@ Usage: ${usage}`, {
|
|
|
19874
19875
|
}
|
|
19875
19876
|
const providerInput = getString(input.values, "provider");
|
|
19876
19877
|
if (providerInput && !coerceAgentProvider(providerInput)) {
|
|
19877
|
-
|
|
19878
|
+
const message = isRetiredAgentProviderId(providerInput) ? `--provider ${providerInput} was removed; use one of: ${listAgentProviders().join(", ")}` : `--provider must be one of: ${listAgentProviders().join(", ")}`;
|
|
19879
|
+
throw usageError(`Error: ${message}
|
|
19878
19880
|
Usage: ${usage}`, {
|
|
19879
|
-
message
|
|
19881
|
+
message,
|
|
19880
19882
|
details: {
|
|
19881
19883
|
command: "agent.ask",
|
|
19882
19884
|
usage,
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
computeFactorAverages,
|
|
6
6
|
layoutSiteCrawlGraphInput,
|
|
7
7
|
persistSiteCrawlGraphLayout
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6M7HSNXJ.js";
|
|
9
9
|
import {
|
|
10
10
|
PACKAGE_VERSION,
|
|
11
11
|
canonryMcpTools
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-K4356AGP.js";
|
|
13
13
|
import {
|
|
14
14
|
HISTORY_WINDOW_RUNS,
|
|
15
15
|
IntelligenceService,
|
|
@@ -85,7 +85,7 @@ import {
|
|
|
85
85
|
siteCrawlPages,
|
|
86
86
|
siteCrawlSnapshots,
|
|
87
87
|
trafficSources
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-ZO62Z2WN.js";
|
|
89
89
|
import {
|
|
90
90
|
RunKinds,
|
|
91
91
|
SiteCrawlFetchStates,
|
|
@@ -112,7 +112,7 @@ import {
|
|
|
112
112
|
roundPreservingTotal,
|
|
113
113
|
siteAuditPageFactorSchema,
|
|
114
114
|
wilsonInterval
|
|
115
|
-
} from "./chunk-
|
|
115
|
+
} from "./chunk-HAALQW4R.js";
|
|
116
116
|
|
|
117
117
|
// src/demo-server.ts
|
|
118
118
|
import { dirname, join as join2 } from "path";
|
package/dist/index.js
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
createGoogleMarketingCredentialStore,
|
|
4
4
|
createGoogleMarketingRuntime,
|
|
5
5
|
createServer
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-F4G7I7T7.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-XGERCNUR.js";
|
|
10
|
+
import "./chunk-6M7HSNXJ.js";
|
|
11
|
+
import "./chunk-K4356AGP.js";
|
|
12
|
+
import "./chunk-ZO62Z2WN.js";
|
|
13
|
+
import "./chunk-HAALQW4R.js";
|
|
14
14
|
export {
|
|
15
15
|
GoogleMarketingRuntimeError,
|
|
16
16
|
createGoogleMarketingCredentialStore,
|
package/dist/mcp.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
autoSyncSkills
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4NC5ZAC2.js";
|
|
4
4
|
import {
|
|
5
5
|
createApiClient,
|
|
6
6
|
createCanonryMcpServer
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-XGERCNUR.js";
|
|
8
|
+
import "./chunk-K4356AGP.js";
|
|
9
9
|
import {
|
|
10
10
|
isReadOnlyKey,
|
|
11
11
|
updateCheckEnvOptOut
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HAALQW4R.js";
|
|
13
13
|
|
|
14
14
|
// src/mcp/cli.ts
|
|
15
15
|
import { randomUUID } from "crypto";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonry/canonry",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
|
|
6
6
|
"keywords": [
|
|
@@ -86,33 +86,33 @@
|
|
|
86
86
|
"tsup": "^8.5.1",
|
|
87
87
|
"tsx": "^4.19.0",
|
|
88
88
|
"@ainyc/canonry-web": "0.0.0",
|
|
89
|
-
"@ainyc/canonry-api-client": "0.0.0",
|
|
90
89
|
"@ainyc/canonry-config": "0.0.0",
|
|
91
|
-
"@ainyc/canonry-
|
|
90
|
+
"@ainyc/canonry-api-client": "0.0.0",
|
|
92
91
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
93
92
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
94
|
-
"@ainyc/canonry-
|
|
93
|
+
"@ainyc/canonry-db": "0.0.0",
|
|
95
94
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
96
|
-
"@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
|
|
97
95
|
"@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
|
|
98
96
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
99
97
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
100
98
|
"@ainyc/canonry-integration-google-ads": "0.0.0",
|
|
101
|
-
"@ainyc/canonry-integration-
|
|
99
|
+
"@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
|
|
100
|
+
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
102
101
|
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
102
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
103
103
|
"@ainyc/canonry-integration-google-tag-manager": "0.0.0",
|
|
104
|
+
"@ainyc/canonry-intelligence": "0.0.0",
|
|
104
105
|
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
105
|
-
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
106
106
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
107
|
-
"@ainyc/canonry-
|
|
108
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
107
|
+
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
109
108
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
109
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
110
110
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
111
|
-
"@ainyc/canonry-provider-local": "0.0.0",
|
|
112
111
|
"@ainyc/canonry-provider-muse": "0.0.0",
|
|
113
|
-
"@ainyc/canonry-provider-
|
|
112
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
113
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
114
114
|
"@ainyc/canonry-integration-typesafe": "0.0.0",
|
|
115
|
-
"@ainyc/canonry-provider-
|
|
115
|
+
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
116
116
|
},
|
|
117
117
|
"scripts": {
|
|
118
118
|
"build": "pnpm run build:cli && pnpm run build:web",
|