@canonry/canonry 5.19.0 → 5.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/assets/agent-workspace/skills/aero/SKILL.md +13 -8
  2. package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +27 -10
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +7 -2
  4. package/assets/assets/{AuditHistoryPanel-C_p8jX7Q.js → AuditHistoryPanel-BSh3Oezj.js} +1 -1
  5. package/assets/assets/{BacklinksPage-jD4nOGw1.js → BacklinksPage-DQ59dhCx.js} +1 -1
  6. package/assets/assets/{HistoryPage-DJGBbtBY.js → HistoryPage-BBvgid3o.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-Dmr3TVlo.js → MeasurementPropertyPage-Dmb18JbT.js} +1 -1
  8. package/assets/assets/{ProjectPage-B_haCKiA.js → ProjectPage-CPg0q2Dy.js} +1 -1
  9. package/assets/assets/{RunRow-BMbwR2yk.js → RunRow-BK8sZCID.js} +1 -1
  10. package/assets/assets/{RunsPage-D8hHakq2.js → RunsPage-CIcVBTdP.js} +1 -1
  11. package/assets/assets/{SettingsPage-QKnRcFA9.js → SettingsPage-Bc3aR-gz.js} +1 -1
  12. package/assets/assets/{SiteHealthSection-CDkUXqmb.js → SiteHealthSection-FF14aIXT.js} +3 -3
  13. package/assets/assets/{TrafficPage-CMif8RlM.js → TrafficPage-C_qoeGDl.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-DOCTP_4-.js → TrafficSourceDetailPage-B3i_B2MI.js} +1 -1
  15. package/assets/assets/{extract-error-message-Dm8RvYE5.js → extract-error-message-BisUn6nN.js} +1 -1
  16. package/assets/assets/{index-lj0fuFl4.js → index-Dht7JNX5.js} +44 -44
  17. package/assets/assets/{react-sigma_core.esm.min-DOPuwLtN.js → react-sigma_core.esm.min-tWhS09SS.js} +1 -1
  18. package/assets/assets/{v2-overview-adapter-C19tUcDS.js → v2-overview-adapter-CAoeWKQJ.js} +1 -1
  19. package/assets/index.html +1 -1
  20. package/dist/{chunk-QJWIOCEO.js → chunk-4PENJKRN.js} +2 -2
  21. package/dist/{chunk-SPCEXW3B.js → chunk-5FKR2K76.js} +11 -4
  22. package/dist/{chunk-GUJY3RIM.js → chunk-DRZWMFRV.js} +627 -263
  23. package/dist/{chunk-WLL7Q7JD.js → chunk-QF7SY7AF.js} +222 -29
  24. package/dist/{chunk-ABUO27BV.js → chunk-TSFKNQ5X.js} +3 -3
  25. package/dist/{chunk-P2EFPLIO.js → chunk-WL27HO5C.js} +565 -139
  26. package/dist/{chunk-YHKKCXGY.js → chunk-XBGPHZKA.js} +24 -8
  27. package/dist/cli.js +73 -17
  28. package/dist/{demo-server-BOIBS74F.js → demo-server-OCBV5OMS.js} +4 -4
  29. package/dist/index.js +6 -6
  30. package/dist/{intelligence-service-IWR2H6OW.js → intelligence-service-MKHINDHN.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +14 -14
@@ -105,7 +105,7 @@ import {
105
105
  trafficEventKindSchema,
106
106
  trafficSeriesGranularitySchema,
107
107
  visibilityReportRequestSchema
108
- } from "./chunk-WLL7Q7JD.js";
108
+ } from "./chunk-QF7SY7AF.js";
109
109
 
110
110
  // src/cli-error.ts
111
111
  function isMachineFormat(format) {
@@ -398,7 +398,12 @@ var measurementPropertyEvidenceInputSchema = measurementPropertyEvidenceQuerySch
398
398
  )
399
399
  }).strict();
400
400
  var measurementPortfolioSummaryInputSchema = measurementPortfolioSummaryQuerySchema.extend({
401
- project: projectNameSchema
401
+ project: projectNameSchema,
402
+ groupKey: measurementPortfolioSummaryQuerySchema.shape.groupKey.describe("A market stable key (a row's metro.groupKey). Scopes rows to that market and lists its direct submarkets."),
403
+ // Capped below the route's 50: past about 10 rows the per-row evidence
404
+ // (names given instead, cited domains) no longer fits the tool-result cap.
405
+ limit: z3.number().int().positive().max(10).optional().describe("Rows per list, at most 10. Default 4 keeps the whole result under the tool-result cap; prefer groupKey over a larger limit."),
406
+ includeNestedMarkets: measurementPortfolioSummaryQuerySchema.shape.includeNestedMarkets.describe("Every market at every level, uncapped. Off by default; large portfolios exceed the cap with it on.")
402
407
  }).strict();
403
408
  var measurementPropertyQuestionsInputSchema = measurementPropertyQuestionsQuerySchema.extend({
404
409
  project: projectNameSchema
@@ -1279,22 +1284,33 @@ var canonryMcpTools = [
1279
1284
  defineTool({
1280
1285
  name: "canonry_analytics_sources",
1281
1286
  title: "Get cited-source rankings",
1282
- description: "Where AI engines get the facts they cite for a project. Returns the FULL ranked list of cited domains (not truncated) \u2014 each tagged with a category and an actionable surface class (own / direct-competitor / ota-aggregator / editorial-media / other) \u2014 plus a surface-class roll-up and a per-provider breakdown (each provider's cited-domain mix + total cited slots). The surface class is deterministic (own/competitor from project data, the rest from the source allow-list) and enriched by discovery's stored per-domain classifications when present \u2014 no new LLM calls. Probe-excluded, window-filterable (7d/30d/90d/all). Use `limit` to cap each ranked list to the top N domains (an explicit long-tail rollup preserves the totals). All counts/shares/classification are computed server-side.",
1287
+ 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 `runId` it pools every sweep in the window (`runCount` says how many); without `queryClass` it pools branded and non-brand answers, where branded queries inflate your own domain. For the sources behind non-brand answers, set `queryClass=non-brand` and the latest `runId`; on a v2 measurement plan the class comes from the plan, the same answers as canonry_competitor_landscape with scope=all-markets. 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.",
1283
1288
  access: "read",
1284
1289
  tier: "monitoring",
1285
1290
  inputSchema: z3.object({
1286
1291
  project: projectNameSchema,
1287
1292
  window: analyticsWindowSchema.optional().describe("Time range: 7d, 30d, 90d, or all (default all)."),
1288
- limit: z3.number().int().positive().optional().describe("Cap each ranked list to the top N domains. Omit for the full list.")
1293
+ limit: z3.number().int().positive().max(50).optional().describe("Cap each ranked list to the top N domains. Default 10; larger lists can exceed the tool-result cap."),
1294
+ runId: z3.string().trim().min(1).optional().describe("Read one answer-visibility run (for example the latest sweep) instead of pooling every run in the window."),
1295
+ queryClass: z3.enum(["all", "branded", "non-brand"]).optional().describe("Branded or non-brand answers only. With an active v2 measurement plan the class comes from the plan assignments. Default all pools both classes."),
1296
+ includeByQuery: z3.boolean().optional().describe("Include the per-query breakdown. It is large; default false for this tool.")
1289
1297
  }),
1290
1298
  annotations: readAnnotations(),
1291
1299
  openApiOperations: ["GET /api/v1/projects/{name}/analytics/sources"],
1292
- handler: (client, input) => client.getAnalyticsSources(input.project, { window: input.window, limit: input.limit })
1300
+ handler: (client, input) => client.getAnalyticsSources(input.project, {
1301
+ window: input.window,
1302
+ // The full ranked lists run past the agent's tool-result cap on a large
1303
+ // project, and the per-engine lists are the first thing lost.
1304
+ limit: input.limit ?? 10,
1305
+ runId: input.runId,
1306
+ queryClass: input.queryClass,
1307
+ includeByQuery: input.includeByQuery ?? false
1308
+ })
1293
1309
  }),
1294
1310
  defineTool({
1295
1311
  name: "canonry_competitor_landscape",
1296
1312
  title: "Get historical competitor landscape",
1297
- description: "Returns pinned competitors first, then observed direct competitors and other cited sources from stored answer/source evidence only. Mention share is percentage points (0..100) from answer text; citations are independent. The response carries basis (tracked or observed), availability, and reason. Pins exclusively define the comparison set when present. Otherwise at least 3 stored direct competitors must each be mentioned in 3 answers in the selected scope; raw names and platforms never enter that denominator. Without a comparison set the ratio is null, never 100%. The full comparison array is uncapped. Share of voice needs ONE query class: `queryClass=all`, and omitting it, pool branded and non-brand, so every `shareOfVoice` comes back null and only the counts are published. Pass `queryClass=non-brand` (or `branded`) for a ratio, exactly as visibility-stats does. A project with no brand name or alias cannot be split by class, so a class-scoped read on one is refused rather than answered empty. Optional groupBy: model adds provider/requested-model groups with separate served-model evidence and sample counts. Optional model filters one exact requested model ID and requires provider. Unknown historical models remain explicit. Groups are not a matched-query or equal-weight comparison. Advanced reads support one market group or explicit scope: all-markets. No provider, discovery, or classifier work runs. Ranked lists are capped at 100 observed/other-source rows per group; pins remain complete. Model groups are capped at 50 and disclose truncation.",
1313
+ description: "Returns pinned competitors first, then observed direct competitors and other cited sources from stored answer/source evidence only. Mention share is percentage points (0..100) from answer text; citations are independent. The response carries basis (tracked or observed), availability, and reason. Pins exclusively define the comparison set when present. Otherwise at least 3 stored direct competitors must each be mentioned in 3 answers in the selected scope; raw names and platforms never enter that denominator. Without a comparison set the ratio is null, never 100%. The full comparison array is uncapped. Share of voice needs ONE query class: `queryClass=all`, and omitting it, pool branded and non-brand, so every `shareOfVoice` comes back null and only the counts are published. Pass `queryClass=non-brand` (or `branded`) for a ratio, exactly as visibility-stats does. A project with no brand name or alias cannot be split by class, so a class-scoped read on one is refused rather than answered empty. Optional groupBy: model adds provider/requested-model groups with separate served-model evidence and sample counts. Optional model filters one exact requested model ID and requires provider. Unknown historical models remain explicit. Groups are not a matched-query or equal-weight comparison. Advanced reads support one market group or explicit scope: all-markets. On a project with an active v2 measurement plan, queryClass=branded or non-brand with no scope or groupKey defaults to scope=all-markets, so classes come from the plan; pass scope=project to force the text classifier. No provider, discovery, or classifier work runs. Ranked lists are capped at 100 observed/other-source rows per group; pins remain complete. `observedNames` are names written in answer text, not cited sources, capped at the top 50 by answer count; `observedNamesTotal` is the full count. Model groups are capped at 50 and disclose truncation.",
1298
1314
  access: "read",
1299
1315
  tier: "monitoring",
1300
1316
  inputSchema: competitorLandscapeInputSchema,
@@ -2438,7 +2454,7 @@ var canonryMcpTools = [
2438
2454
  defineTool({
2439
2455
  name: "canonry_measurement_plan_get",
2440
2456
  title: "Get measurement plan",
2441
- description: "Get the active measurement plan for a project.",
2457
+ description: "Get the active measurement plan for a project: plan structure only (targets, groups, query assignments), no metrics. It can be very large on a big portfolio; for analysis prefer canonry_measurement_portfolio_summary and canonry_measurement_overview.",
2442
2458
  access: "read",
2443
2459
  tier: "setup",
2444
2460
  inputSchema: projectInputSchema,
@@ -2557,7 +2573,7 @@ var canonryMcpTools = [
2557
2573
  defineTool({
2558
2574
  name: "canonry_measurement_portfolio_summary",
2559
2575
  title: "Summarize measured Properties",
2560
- description: "Start here for best/worst Property mention performance. Defaults to non-brand questions; state the returned queryClass and keep branded comparisons separate unless all was requested. mentionRanking.strongest and .weakest rank every Property with an available mention rate before applying limit, independently of citation availability or an unavailable portfolio aggregate. mentionRanking.excluded names every excluded Property and its reason; flag those separately, never call all mention rankings unavailable because one identity is ambiguous. Report numerator/denominator with each rate, and any unattributed count beside it: answers left out of both because their Property identity was unresolved, never not-mentioned answers. Rates may tie (stable label/key order); this is descriptive coverage, not a confidence-adjusted or unique-winner claim. weakestProperties retains combined mention/citation weaknesses with stored replacement names; markets compares every named market worst-first. Each market is scoped to the displayed run; markets may share Properties, so they never sum to portfolio totals. Reads stored data only; it never starts provider work.",
2576
+ 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. Keep the default limit; for more rows narrow with groupKey (a metro) rather than raising limit, which can overflow the tool-result cap. 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. citedDomains and weakestAnswerSources count answers citing each domain, every engine included; weakestAnswerSources covers the weakest rows plus every tied Property, each answer once. When tiedAtWeakest is set, that many Properties share the weakest rates and are ordered by name, not ranked. mentionRanking.strongest/.weakest rank every Property with an available mention rate; .excluded lists the rest with reasons. markets lists one level worst-first (metros, or a groupKey's submarkets), capped at limit, with totalMarkets; includeNestedMarkets returns every level. Markets may share Properties and never sum to portfolio totals. Reads stored data only; never starts provider work.",
2561
2577
  access: "read",
2562
2578
  tier: "monitoring",
2563
2579
  inputSchema: measurementPortfolioSummaryInputSchema,
package/dist/cli.js CHANGED
@@ -35,11 +35,11 @@ import {
35
35
  trackCliCommandFinished,
36
36
  trackEvent,
37
37
  waitForServerRuntimeStartup
38
- } from "./chunk-GUJY3RIM.js";
38
+ } from "./chunk-DRZWMFRV.js";
39
39
  import {
40
40
  autoSyncSkills,
41
41
  formatAutoSyncNotice
42
- } from "./chunk-ABUO27BV.js";
42
+ } from "./chunk-TSFKNQ5X.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-SPCEXW3B.js";
59
+ } from "./chunk-5FKR2K76.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-QJWIOCEO.js";
65
+ import "./chunk-4PENJKRN.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-YHKKCXGY.js";
77
+ } from "./chunk-XBGPHZKA.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-P2EFPLIO.js";
92
+ } from "./chunk-WL27HO5C.js";
93
93
  import {
94
94
  AdsDeliverySnapshotStatuses,
95
95
  AdsHistoricalCampaignRollupStatuses,
@@ -165,6 +165,7 @@ import {
165
165
  normalizeProjectAliases,
166
166
  normalizeTelemetryStatus,
167
167
  notificationEventSchema,
168
+ queryClassFilterSchema,
168
169
  queryTrackingCommitRequestSchema,
169
170
  queryTrackingPreviewRequestSchema,
170
171
  reportUnattributedAnswers,
@@ -176,7 +177,7 @@ import {
176
177
  snapshotProviderModeSchema,
177
178
  visibilityReportRequestSchema,
178
179
  winnabilityClassSchema
179
- } from "./chunk-WLL7Q7JD.js";
180
+ } from "./chunk-QF7SY7AF.js";
180
181
 
181
182
  // src/cli.ts
182
183
  import { pathToFileURL } from "url";
@@ -441,7 +442,7 @@ function parseDemoTrustedProxies(values) {
441
442
  async function demoCommand(options) {
442
443
  const { host, port } = parseDemoListenOptions(options);
443
444
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
444
- const { createDemoServer } = await import("./demo-server-BOIBS74F.js");
445
+ const { createDemoServer } = await import("./demo-server-OCBV5OMS.js");
445
446
  const app = await createDemoServer({ trustProxy });
446
447
  let stopping = false;
447
448
  const stop = () => {
@@ -7808,7 +7809,8 @@ function printCompetitorLandscape(data) {
7808
7809
  `Evidence: ${data.evidence.answeredResults} answer-text result(s), ${data.evidence.sourceResults} source result(s); excluded: ${data.evidence.excludedProbeResults} probe, ${data.evidence.excludedNonCompletedResults} non-completed.`
7809
7810
  );
7810
7811
  if (data.observedNames?.length) {
7811
- console.log("Names observed in answers (not a comparison set):");
7812
+ const total = data.observedNamesTotal ?? data.observedNames.length;
7813
+ console.log(total > data.observedNames.length ? `Names observed in answers (not a comparison set), top ${data.observedNames.length} of ${total}:` : "Names observed in answers (not a comparison set):");
7812
7814
  for (const row of data.observedNames) console.log(` ${row.name} \xB7 ${row.answerCount} answers`);
7813
7815
  }
7814
7816
  if (data.modelComparison) printModelComparison(data.modelComparison, data.filters.queryClass);
@@ -11435,16 +11437,46 @@ Source Origin Breakdown`);
11435
11437
  function getClient18() {
11436
11438
  return createApiClient();
11437
11439
  }
11440
+ var SOURCES_USAGE = "canonry sources <project> [--rank] [--limit N] [--by-provider] [--window 7d|30d|90d|all] [--query-class all|branded|non-brand] [--run-id <id>] [--include-by-query true|false] [--format json|jsonl]";
11441
+ function parseSourcesQueryClass(value) {
11442
+ if (value === void 0) return void 0;
11443
+ const parsed = queryClassFilterSchema.safeParse(value);
11444
+ if (parsed.success) return parsed.data;
11445
+ const message = `--query-class must be one of ${queryClassFilterSchema.options.join(", ")}`;
11446
+ throw usageError(`Error: ${message}
11447
+ Usage: ${SOURCES_USAGE}`, {
11448
+ message,
11449
+ details: { command: "sources", usage: SOURCES_USAGE, option: "query-class", value }
11450
+ });
11451
+ }
11452
+ function parseSourcesIncludeByQuery(value) {
11453
+ if (value === void 0) return void 0;
11454
+ if (value === "true") return true;
11455
+ if (value === "false") return false;
11456
+ const message = "--include-by-query must be true or false";
11457
+ throw usageError(`Error: ${message}
11458
+ Usage: ${SOURCES_USAGE}`, {
11459
+ message,
11460
+ details: { command: "sources", usage: SOURCES_USAGE, option: "include-by-query", value }
11461
+ });
11462
+ }
11438
11463
  var pct = (n) => `${(n * 100).toFixed(1)}%`;
11439
11464
  async function showSources(project, options) {
11440
11465
  if (options.limit !== void 0 && (!Number.isInteger(options.limit) || options.limit <= 0)) {
11441
- throw usageError("Error: --limit must be a positive integer\nUsage: canonry sources <project> [--rank] [--limit N] [--by-provider] [--window 7d|30d|90d|all] [--format json|jsonl]", {
11466
+ throw usageError(`Error: --limit must be a positive integer
11467
+ Usage: ${SOURCES_USAGE}`, {
11442
11468
  message: "--limit must be a positive integer",
11443
11469
  details: { command: "sources", option: "limit", value: options.limit }
11444
11470
  });
11445
11471
  }
11446
11472
  const client = getClient18();
11447
- const data = await client.getAnalyticsSources(project, { window: options.window, limit: options.limit });
11473
+ const data = await client.getAnalyticsSources(project, {
11474
+ window: options.window,
11475
+ limit: options.limit,
11476
+ ...options.runId === void 0 ? {} : { runId: options.runId },
11477
+ ...options.queryClass === void 0 ? {} : { queryClass: options.queryClass },
11478
+ ...options.includeByQuery === void 0 ? {} : { includeByQuery: options.includeByQuery }
11479
+ });
11448
11480
  if (options.format === "jsonl") {
11449
11481
  emitJsonl(data.ranked.entries.map((e) => ({ project, ...e })));
11450
11482
  return;
@@ -11459,6 +11491,8 @@ function printSourceRankings(data, options) {
11459
11491
  console.log(`
11460
11492
  Source Rankings (${data.window})`);
11461
11493
  console.log("\u2500".repeat(50));
11494
+ const scope = describeScope(data);
11495
+ if (scope) console.log(` ${scope}`);
11462
11496
  if (data.ranked.totalCitedSlots === 0) {
11463
11497
  console.log(" No source data available");
11464
11498
  return;
@@ -11476,8 +11510,25 @@ Source Rankings (${data.window})`);
11476
11510
  ${provider} (${list.totalCitedSlots} cited slots):`);
11477
11511
  printRankedEntries(list, " ");
11478
11512
  }
11513
+ for (const provider of data.providersWithoutSources ?? []) {
11514
+ console.log(`
11515
+ ${provider}: answered, but no answer named a source`);
11516
+ }
11479
11517
  }
11480
11518
  }
11519
+ function describeScope(data) {
11520
+ const parts = [];
11521
+ if (data.answerTotal !== void 0) parts.push(`${data.answerTotal} answers`);
11522
+ if (data.filters?.runId) parts.push(`run ${data.filters.runId}`);
11523
+ else if (data.runCount !== void 0) parts.push(`${data.runCount} ${data.runCount === 1 ? "run" : "runs"} pooled`);
11524
+ if (data.filters) {
11525
+ parts.push(data.filters.queryClass === "all" ? "branded and non-brand pooled" : `${data.filters.queryClass} queries only`);
11526
+ }
11527
+ if (data.unclassifiedAnswers) {
11528
+ parts.push(`${data.unclassifiedAnswers} unclassified ${data.unclassifiedAnswers === 1 ? "answer" : "answers"} excluded`);
11529
+ }
11530
+ return parts.length > 0 ? parts.join(" \xB7 ") : null;
11531
+ }
11481
11532
  function printSurfaceClasses(list) {
11482
11533
  for (const c of list.bySurfaceClass) {
11483
11534
  const noun = c.domainCount === 1 ? "domain" : "domains";
@@ -11840,25 +11891,30 @@ var OPERATOR_CLI_COMMANDS = [
11840
11891
  },
11841
11892
  {
11842
11893
  path: ["sources"],
11843
- usage: "canonry sources <project> [--rank] [--limit N] [--by-provider] [--window 7d|30d|90d|all] [--format json|jsonl]",
11894
+ usage: SOURCES_USAGE,
11844
11895
  options: {
11845
11896
  rank: { type: "boolean", default: false },
11846
11897
  "by-provider": { type: "boolean", default: false },
11847
11898
  limit: stringOption(),
11848
- window: stringOption()
11899
+ window: stringOption(),
11900
+ "query-class": stringOption(),
11901
+ "run-id": stringOption(),
11902
+ "include-by-query": stringOption()
11849
11903
  },
11850
11904
  run: async (input) => {
11851
- const usage = "canonry sources <project> [--rank] [--limit N] [--by-provider] [--window 7d|30d|90d|all] [--format json|jsonl]";
11852
- const project = requireProject(input, "sources", usage);
11905
+ const project = requireProject(input, "sources", SOURCES_USAGE);
11853
11906
  await showSources(project, {
11854
11907
  rank: getBoolean(input.values, "rank"),
11855
11908
  byProvider: getBoolean(input.values, "by-provider"),
11856
11909
  limit: parseIntegerOption(input, "limit", {
11857
11910
  message: "--limit must be an integer",
11858
- usage,
11911
+ usage: SOURCES_USAGE,
11859
11912
  command: "sources"
11860
11913
  }),
11861
11914
  window: getString(input.values, "window"),
11915
+ runId: getString(input.values, "run-id"),
11916
+ queryClass: parseSourcesQueryClass(getString(input.values, "query-class")),
11917
+ includeByQuery: parseSourcesIncludeByQuery(getString(input.values, "include-by-query")),
11862
11918
  format: input.format
11863
11919
  });
11864
11920
  }
@@ -15455,7 +15511,7 @@ async function initCommand(opts) {
15455
15511
  }
15456
15512
  const perplexityApiKey = await promptProviderApiKey("Perplexity", "PERPLEXITY_API_KEY");
15457
15513
  if (perplexityApiKey) {
15458
- const perplexityModel = await prompt(" Perplexity model [sonar]: ") || "sonar";
15514
+ const perplexityModel = await prompt(" Perplexity preset or model [fast]: ") || "fast";
15459
15515
  providers.perplexity = { apiKey: perplexityApiKey, model: perplexityModel, quota: DEFAULT_QUOTA };
15460
15516
  }
15461
15517
  console.log("\nLocal LLM (Ollama, LM Studio, llama.cpp, vLLM \u2014 any OpenAI-compatible API):");
@@ -4,11 +4,11 @@ import {
4
4
  import {
5
5
  layoutSiteCrawlGraphInput,
6
6
  persistSiteCrawlGraphLayout
7
- } from "./chunk-QJWIOCEO.js";
7
+ } from "./chunk-4PENJKRN.js";
8
8
  import {
9
9
  PACKAGE_VERSION,
10
10
  canonryMcpTools
11
- } from "./chunk-YHKKCXGY.js";
11
+ } from "./chunk-XBGPHZKA.js";
12
12
  import {
13
13
  HISTORY_WINDOW_RUNS,
14
14
  IntelligenceService,
@@ -84,7 +84,7 @@ import {
84
84
  siteCrawlPages,
85
85
  siteCrawlSnapshots,
86
86
  trafficSources
87
- } from "./chunk-P2EFPLIO.js";
87
+ } from "./chunk-WL27HO5C.js";
88
88
  import {
89
89
  RunKinds,
90
90
  SiteCrawlFetchStates,
@@ -108,7 +108,7 @@ import {
108
108
  placementLinkDecision,
109
109
  siteAuditPageFactorSchema,
110
110
  wilsonInterval
111
- } from "./chunk-WLL7Q7JD.js";
111
+ } from "./chunk-QF7SY7AF.js";
112
112
 
113
113
  // src/demo-server.ts
114
114
  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-GUJY3RIM.js";
6
+ } from "./chunk-DRZWMFRV.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-SPCEXW3B.js";
10
- import "./chunk-QJWIOCEO.js";
11
- import "./chunk-YHKKCXGY.js";
12
- import "./chunk-P2EFPLIO.js";
13
- import "./chunk-WLL7Q7JD.js";
9
+ } from "./chunk-5FKR2K76.js";
10
+ import "./chunk-4PENJKRN.js";
11
+ import "./chunk-XBGPHZKA.js";
12
+ import "./chunk-WL27HO5C.js";
13
+ import "./chunk-QF7SY7AF.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
16
16
  createGoogleMarketingCredentialStore,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  HISTORY_WINDOW_RUNS,
3
3
  IntelligenceService
4
- } from "./chunk-P2EFPLIO.js";
5
- import "./chunk-WLL7Q7JD.js";
4
+ } from "./chunk-WL27HO5C.js";
5
+ import "./chunk-QF7SY7AF.js";
6
6
  export {
7
7
  HISTORY_WINDOW_RUNS,
8
8
  IntelligenceService
package/dist/mcp.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-ABUO27BV.js";
3
+ } from "./chunk-TSFKNQ5X.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-SPCEXW3B.js";
8
- import "./chunk-YHKKCXGY.js";
7
+ } from "./chunk-5FKR2K76.js";
8
+ import "./chunk-XBGPHZKA.js";
9
9
  import {
10
10
  isReadOnlyKey,
11
11
  updateCheckEnvOptOut
12
- } from "./chunk-WLL7Q7JD.js";
12
+ } from "./chunk-QF7SY7AF.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": "5.19.0",
3
+ "version": "5.20.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,32 +86,32 @@
86
86
  "@types/node-cron": "^3.0.11",
87
87
  "tsup": "^8.5.1",
88
88
  "tsx": "^4.19.0",
89
- "@ainyc/canonry-api-client": "0.0.0",
90
- "@ainyc/canonry-api-routes": "0.0.0",
91
89
  "@ainyc/canonry-web": "0.0.0",
90
+ "@ainyc/canonry-api-routes": "0.0.0",
91
+ "@ainyc/canonry-api-client": "0.0.0",
92
92
  "@ainyc/canonry-contracts": "0.0.0",
93
93
  "@ainyc/canonry-config": "0.0.0",
94
94
  "@ainyc/canonry-db": "0.0.0",
95
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
96
95
  "@ainyc/canonry-integration-bing": "0.0.0",
96
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
97
97
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
98
- "@ainyc/canonry-integration-google-ads": "0.0.0",
99
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
100
98
  "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
101
- "@ainyc/canonry-integration-google": "0.0.0",
102
- "@ainyc/canonry-integration-google-places": "0.0.0",
99
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
100
+ "@ainyc/canonry-integration-google-ads": "0.0.0",
103
101
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
104
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
105
- "@ainyc/canonry-provider-cdp": "0.0.0",
102
+ "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
106
103
  "@ainyc/canonry-integration-traffic": "0.0.0",
104
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
105
+ "@ainyc/canonry-integration-google": "0.0.0",
107
106
  "@ainyc/canonry-integration-wordpress": "0.0.0",
108
- "@ainyc/canonry-provider-claude": "0.0.0",
109
- "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
110
107
  "@ainyc/canonry-intelligence": "0.0.0",
108
+ "@ainyc/canonry-integration-google-places": "0.0.0",
109
+ "@ainyc/canonry-provider-claude": "0.0.0",
110
+ "@ainyc/canonry-provider-cdp": "0.0.0",
111
111
  "@ainyc/canonry-provider-gemini": "0.0.0",
112
- "@ainyc/canonry-provider-openai": "0.0.0",
112
+ "@ainyc/canonry-provider-local": "0.0.0",
113
113
  "@ainyc/canonry-provider-perplexity": "0.0.0",
114
- "@ainyc/canonry-provider-local": "0.0.0"
114
+ "@ainyc/canonry-provider-openai": "0.0.0"
115
115
  },
116
116
  "scripts": {
117
117
  "build": "pnpm run build:cli && pnpm run build:web",