@canonry/canonry 7.9.1 → 7.11.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 (34) hide show
  1. package/assets/agent-workspace/skills/aero/references/reporting.md +2 -0
  2. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +1 -1
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +30 -4
  4. package/assets/assets/{AuditHistoryPanel-D0nukTwD.js → AuditHistoryPanel-D1WdkJKU.js} +1 -1
  5. package/assets/assets/{BacklinksPage-B525ij0g.js → BacklinksPage-Uf1djxaV.js} +1 -1
  6. package/assets/assets/{HistoryPage-DvupqQDM.js → HistoryPage-CZPxzlS_.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-CwDbi1XN.js → MeasurementPropertyPage-CaWq1f2G.js} +1 -1
  8. package/assets/assets/{ProjectPage-DmZZ2C7q.js → ProjectPage-BimakE9X.js} +7 -7
  9. package/assets/assets/{RunsPage-DHTvHcGm.js → RunsPage-CDX5RxHi.js} +1 -1
  10. package/assets/assets/{SettingsPage-BwAFzB7A.js → SettingsPage-BfzKP-IR.js} +1 -1
  11. package/assets/assets/SiteHealthSection-42_E1ueq.js +4 -0
  12. package/assets/assets/{TrafficPage-BOcXASAM.js → TrafficPage-RjkpfGMw.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DTq0cN6g.js → TrafficSourceDetailPage-DIWDPo-8.js} +1 -1
  14. package/assets/assets/{extract-error-message-BOTDI6g5.js → extract-error-message-D_e8B2s_.js} +1 -1
  15. package/assets/assets/index-BNeiohKS.css +1 -0
  16. package/assets/assets/{index-BLJfGYUw.js → index-DKl_jsi3.js} +24 -24
  17. package/assets/assets/{react-sigma_core.esm.min-BEBdJNtD.js → react-sigma_core.esm.min-Br-N5hoB.js} +1 -1
  18. package/assets/assets/{v2-overview-adapter-BPZ-H3VY.js → v2-overview-adapter-BCSXgy2l.js} +1 -1
  19. package/assets/index.html +2 -2
  20. package/dist/{chunk-ZLB7WX5D.js → chunk-7G7YVTDA.js} +56 -6
  21. package/dist/{chunk-DO7XPDG5.js → chunk-CF7G2HGX.js} +5790 -4451
  22. package/dist/{chunk-LO56QXPO.js → chunk-GQAESNJT.js} +90 -2
  23. package/dist/{chunk-KRFTA4I7.js → chunk-HA2BAJ6Z.js} +3507 -2290
  24. package/dist/{chunk-AAMKTL6B.js → chunk-JRBQFRNT.js} +3 -3
  25. package/dist/{chunk-T2V2PESH.js → chunk-OH7E7TJD.js} +2 -2
  26. package/dist/{chunk-4BKRZGLC.js → chunk-S46DWUNR.js} +706 -245
  27. package/dist/cli.js +166 -38
  28. package/dist/{demo-server-KDMG3APY.js → demo-server-BYLZFDKI.js} +4 -4
  29. package/dist/index.js +6 -6
  30. package/dist/{intelligence-service-LAXJFE3U.js → intelligence-service-TIM4XTYH.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +10 -10
  33. package/assets/assets/SiteHealthSection-hi54eSIt.js +0 -4
  34. package/assets/assets/index-Rcbm8bK1.css +0 -1
package/dist/cli.js CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  reparseStoredResult3,
26
26
  reparseStoredResult4,
27
27
  reparseStoredResult5,
28
+ resolveStoredApiResponse,
28
29
  setGoogleAuthConfig,
29
30
  setTelemetryPreference,
30
31
  setTelemetrySource,
@@ -32,11 +33,11 @@ import {
32
33
  trackCliCommandFinished,
33
34
  trackEvent,
34
35
  waitForServerRuntimeStartup
35
- } from "./chunk-4BKRZGLC.js";
36
+ } from "./chunk-S46DWUNR.js";
36
37
  import {
37
38
  autoSyncSkills,
38
39
  formatAutoSyncNotice
39
- } from "./chunk-AAMKTL6B.js";
40
+ } from "./chunk-JRBQFRNT.js";
40
41
  import {
41
42
  configExists,
42
43
  createApiClient,
@@ -53,13 +54,13 @@ import {
53
54
  saveConfig,
54
55
  saveConfigPatch,
55
56
  shouldPrintServeSkillsNudge
56
- } from "./chunk-LO56QXPO.js";
57
+ } from "./chunk-GQAESNJT.js";
57
58
  import {
58
59
  DEFAULT_DEMO_TRUSTED_PROXIES,
59
60
  trustedProxiesProblem,
60
61
  trustedProxyProblem
61
62
  } from "./chunk-RB2F4D3Z.js";
62
- import "./chunk-T2V2PESH.js";
63
+ import "./chunk-OH7E7TJD.js";
63
64
  import {
64
65
  CliError,
65
66
  EXIT_SYSTEM_ERROR,
@@ -71,7 +72,7 @@ import {
71
72
  runMeasurementDraftAction,
72
73
  systemError,
73
74
  usageError
74
- } from "./chunk-ZLB7WX5D.js";
75
+ } from "./chunk-7G7YVTDA.js";
75
76
  import {
76
77
  CLOUDFLARE_WORKER_BINDINGS,
77
78
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -83,6 +84,7 @@ import {
83
84
  backfillProjectAnswerMentions,
84
85
  classifyAiUserFetch,
85
86
  classifyCrawler,
87
+ competitorIdentityColumns,
86
88
  competitors,
87
89
  crawlerEventsHourly,
88
90
  createClient,
@@ -99,8 +101,9 @@ import {
99
101
  queries,
100
102
  querySnapshots,
101
103
  rawEventSamples,
104
+ readMarketCompetitorNames,
102
105
  runs
103
- } from "./chunk-KRFTA4I7.js";
106
+ } from "./chunk-HA2BAJ6Z.js";
104
107
  import {
105
108
  AdsDeliverySnapshotStatuses,
106
109
  AdsHistoricalCampaignRollupStatuses,
@@ -155,6 +158,7 @@ import {
155
158
  buildModelChangeNotice,
156
159
  canonicalizeGtmAccountId,
157
160
  canonicalizeGtmResourceSelection,
161
+ competitorAutoAliasModeSchema,
158
162
  competitorLandscapeQuerySchema,
159
163
  computeCompetitorOverlap,
160
164
  contentTargetDismissRequestSchema,
@@ -229,7 +233,7 @@ import {
229
233
  visibilityCompareSelectionSchema,
230
234
  visibilityReportRequestSchema,
231
235
  winnabilityClassSchema
232
- } from "./chunk-DO7XPDG5.js";
236
+ } from "./chunk-CF7G2HGX.js";
233
237
 
234
238
  // src/cli.ts
235
239
  import { pathToFileURL } from "url";
@@ -779,7 +783,7 @@ function parseDemoTrustedProxies(values) {
779
783
  async function demoCommand(options) {
780
784
  const { host, port } = parseDemoListenOptions(options);
781
785
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
782
- const { createDemoServer } = await import("./demo-server-KDMG3APY.js");
786
+ const { createDemoServer } = await import("./demo-server-BYLZFDKI.js");
783
787
  const app = await createDemoServer({ trustProxy });
784
788
  let stopping = false;
785
789
  const stop = () => {
@@ -857,7 +861,7 @@ async function backfillAnswerVisibilityCommand(opts) {
857
861
  else runIdsByProject.set(run.projectId, [run.id]);
858
862
  }
859
863
  for (const project2 of scopedProjects) {
860
- const competitorsForRun = createRunCompetitorResolver(db, db.select({ domain: competitors.domain, aliases: competitors.aliases }).from(competitors).where(eq(competitors.projectId, project2.id)).all());
864
+ const competitorsForRun = createRunCompetitorResolver(db, db.select(competitorIdentityColumns).from(competitors).where(eq(competitors.projectId, project2.id)).orderBy(competitors.domain).all(), readMarketCompetitorNames(db, project2.id));
861
865
  const runIds = runIdsByProject.get(project2.id) ?? [];
862
866
  if (runIds.length === 0) continue;
863
867
  const projectDomains = effectiveDomains({
@@ -1193,7 +1197,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
1193
1197
  }
1194
1198
  }
1195
1199
  async function backfillInsightsCommand(project2, opts) {
1196
- const { IntelligenceService } = await import("./intelligence-service-LAXJFE3U.js");
1200
+ const { IntelligenceService } = await import("./intelligence-service-TIM4XTYH.js");
1197
1201
  const config = loadConfig();
1198
1202
  const db = createClient(config.database);
1199
1203
  migrate(db);
@@ -1509,19 +1513,6 @@ function reparseProviderSnapshot(provider, rawResponse) {
1509
1513
  return null;
1510
1514
  }
1511
1515
  }
1512
- function resolveStoredApiResponse(parsed) {
1513
- const nested = parsed.apiResponse;
1514
- if (nested !== null && typeof nested === "object" && !Array.isArray(nested)) {
1515
- return nested;
1516
- }
1517
- if (looksLikeProviderApiResponse(parsed)) {
1518
- return parsed;
1519
- }
1520
- return null;
1521
- }
1522
- function looksLikeProviderApiResponse(value) {
1523
- return Array.isArray(value.output) || Array.isArray(value.content) || Array.isArray(value.candidates) || Array.isArray(value.choices);
1524
- }
1525
1516
  function stringifyStoredSnapshotEnvelope(rawResponse, reparsed) {
1526
1517
  const parsed = parseJsonColumn(rawResponse, {});
1527
1518
  const apiResponse = resolveStoredApiResponse(parsed);
@@ -5614,7 +5605,7 @@ async function gaAiReferralDaily(project2, opts) {
5614
5605
  console.log(JSON.stringify(result, null, 2));
5615
5606
  return;
5616
5607
  } else if (opts?.format === "jsonl") {
5617
- emitJsonl(result.days.map((day) => ({ project: project2, window: opts?.window, startDate: opts?.startDate, endDate: opts?.endDate, ...day })));
5608
+ emitJsonl(result.days.map((day2) => ({ project: project2, window: opts?.window, startDate: opts?.startDate, endDate: opts?.endDate, ...day2 })));
5618
5609
  return;
5619
5610
  }
5620
5611
  if (result.days.length === 0) {
@@ -5627,10 +5618,10 @@ async function gaAiReferralDaily(project2, opts) {
5627
5618
  `);
5628
5619
  console.log(` ${"DATE".padEnd(dateWidth)} ${"SOURCE".padEnd(sourceWidth)} ${"SESSIONS".padEnd(10)}${"PAID".padEnd(8)}${"ORGANIC".padEnd(9)}`);
5629
5620
  console.log(` ${"\u2500".repeat(dateWidth)} ${"\u2500".repeat(sourceWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(8)}${"\u2500".repeat(9)}`);
5630
- for (const day of result.days) {
5631
- for (const entry of day.bySource) {
5621
+ for (const day2 of result.days) {
5622
+ for (const entry of day2.bySource) {
5632
5623
  console.log(
5633
- ` ${day.date.padEnd(dateWidth)} ${entry.source.padEnd(sourceWidth)} ${String(entry.sessions).padEnd(10)}${String(entry.paidSessions).padEnd(8)}${String(entry.organicSessions).padEnd(9)}`
5624
+ ` ${day2.date.padEnd(dateWidth)} ${entry.source.padEnd(sourceWidth)} ${String(entry.sessions).padEnd(10)}${String(entry.paidSessions).padEnd(8)}${String(entry.organicSessions).padEnd(9)}`
5634
5625
  );
5635
5626
  }
5636
5627
  }
@@ -9139,7 +9130,11 @@ function sameAliasName(a, b) {
9139
9130
  async function competitorAliases(project2, domain, options) {
9140
9131
  const client = getClient10();
9141
9132
  let result;
9142
- if (options.set !== void 0 || options.clear === true) {
9133
+ if (options.block !== void 0) {
9134
+ result = await client.blockCompetitorAliases(project2, domain, options.block);
9135
+ } else if (options.unblock !== void 0) {
9136
+ result = await client.unblockCompetitorAliases(project2, domain, options.unblock);
9137
+ } else if (options.set !== void 0 || options.clear === true) {
9143
9138
  result = await client.setCompetitorAliases(project2, domain, options.clear ? [] : options.set ?? []);
9144
9139
  } else {
9145
9140
  const target = normalizeCompetitorDomain(domain.trim());
@@ -9161,7 +9156,12 @@ async function competitorAliases(project2, domain, options) {
9161
9156
  throw new CliError({
9162
9157
  code: "NOT_FOUND",
9163
9158
  message: `Competitor "${target}" is not tracked by project "${project2}"`,
9164
- displayMessage: `Error: competitor "${target}" is not tracked by "${project2}". Add it with: canonry competitor add ${project2} ${target}`,
9159
+ displayMessage: [
9160
+ `Error: competitor "${target}" is not a project competitor of "${project2}".`,
9161
+ `If an Advanced market pins it, list its plan names and learned names with: canonry competitor aliases detect ${project2}`,
9162
+ `and block or unblock a learned name with: canonry competitor aliases ${project2} ${target} --block <name> (or --unblock <name>); change its plan names in a measurement draft.`,
9163
+ `To track it in every market instead (it then counts outside its markets): canonry competitor add ${project2} ${target}`
9164
+ ].join("\n"),
9165
9165
  details: { project: project2, domain: target }
9166
9166
  });
9167
9167
  }
@@ -9184,7 +9184,81 @@ async function competitorAliases(project2, domain, options) {
9184
9184
  console.log(JSON.stringify(result, null, 2));
9185
9185
  return;
9186
9186
  }
9187
- console.log(`Aliases for ${result.domain}: ${formatAliases(storedAliases(result))}`);
9187
+ printCompetitorNames(result);
9188
+ }
9189
+ function day(iso) {
9190
+ return iso.slice(0, 10);
9191
+ }
9192
+ function autoAliasEvidence(record) {
9193
+ const precision = record.precision === null ? "n/a" : formatPercent(record.precision, RatioUnits.fraction);
9194
+ const lift = record.lift === void 0 || record.lift === null ? "" : ` (${record.lift}x the rate in answers that do not name it)`;
9195
+ const cased = record.nameCasedAnswers === void 0 ? "" : `; written as a name in ${record.nameCasedAnswers}`;
9196
+ const seen = record.firstSeen ? `; seen ${day(record.firstSeen)} to ${day(record.lastSeen)}` : "";
9197
+ return `paired in ${record.directPairs} answers over ${record.runs} sweeps; ${precision} of ${record.namingAnswers} answers naming it cite the site${lift}${cased}; co-occurs in ${record.cooccurrences}${seen}`;
9198
+ }
9199
+ function printCompetitorNames(competitor) {
9200
+ const auto = competitor.autoAliases ?? [];
9201
+ const blocked = competitor.blockedAliases ?? [];
9202
+ if (competitor.marketKeys) {
9203
+ console.log(`Plan names for ${competitor.domain} (Advanced markets: ${competitor.marketKeys.join(", ")}): ${formatAliases(storedAliases(competitor))}`);
9204
+ } else {
9205
+ console.log(`Aliases for ${competitor.domain}: ${formatAliases(storedAliases(competitor))}`);
9206
+ }
9207
+ if (auto.length === 0) {
9208
+ console.log("Auto-detected from stored answers: (none)");
9209
+ } else {
9210
+ console.log("Auto-detected from stored answers:");
9211
+ for (const record of auto) console.log(` ${record.name} (${autoAliasEvidence(record)})`);
9212
+ }
9213
+ console.log(`Blocked from auto-detection: ${formatAliases(blocked)}`);
9214
+ }
9215
+ var CANDIDATE_MARKS = {
9216
+ added: "+",
9217
+ kept: "=",
9218
+ removed: "-",
9219
+ review: "?",
9220
+ rejected: "x"
9221
+ };
9222
+ async function detectCompetitorAutoAliases(project2, options) {
9223
+ const client = getClient10();
9224
+ const result = options.apply ? await client.applyCompetitorAutoAliases(project2) : await client.previewCompetitorAutoAliases(project2);
9225
+ if (options.format === "json") {
9226
+ console.log(JSON.stringify(result, null, 2));
9227
+ return;
9228
+ }
9229
+ if (options.format === "jsonl") {
9230
+ emitJsonl(result.competitors.map((competitor) => ({ project: result.project, applied: result.applied, ...competitor })));
9231
+ return;
9232
+ }
9233
+ const { scan, thresholds } = result;
9234
+ console.log(`Competitor auto-alias detection for "${result.project}" (${result.applied ? "applied" : "dry run, nothing stored"})`);
9235
+ console.log(`Scanned ${scan.runs} runs, ${scan.snapshots} snapshots, ${scan.answers} answers (newest ${scan.maxRuns} runs, up to ${scan.maxSnapshots} snapshots; provider citations ${scan.providerCitations ? "read" : "unavailable, answer-text pairings only"})`);
9236
+ console.log(`Applies a name paired with the site in ${thresholds.minDirectPairs}+ answers over ${thresholds.minRuns}+ sweeps, named in ${thresholds.minNamingAnswers}+ answers, precision ${formatPercent(thresholds.minPrecision, RatioUnits.fraction)}+, cited ${thresholds.minLift}x+ more often when named than otherwise, written as a name in ${formatPercent(thresholds.minNameCasedShare, RatioUnits.fraction)}+ of them, ${thresholds.dominanceMultiple}x any other competitor's pairings, ${thresholds.minKeyLength}+ letters or digits, and visibly tied to the domain`);
9237
+ console.log("Legend: + added = kept - removed ? review only x rejected");
9238
+ if (result.competitors.length === 0) {
9239
+ console.log(`
9240
+ No competitors tracked for "${result.project}".`);
9241
+ return;
9242
+ }
9243
+ for (const competitor of result.competitors) {
9244
+ console.log(competitor.marketKeys ? `
9245
+ ${competitor.domain} (Advanced markets: ${competitor.marketKeys.join(", ")})` : `
9246
+ ${competitor.domain}`);
9247
+ if (competitor.aliases.length) console.log(` ${competitor.marketKeys ? "plan names" : "curated"}: ${competitor.aliases.join(", ")}`);
9248
+ if (competitor.blockedAliases.length) console.log(` blocked: ${competitor.blockedAliases.join(", ")}`);
9249
+ if (competitor.candidates.length === 0) {
9250
+ console.log(" (no candidate names paired with this site)");
9251
+ continue;
9252
+ }
9253
+ for (const candidate of competitor.candidates) {
9254
+ const reason = candidate.reason ? ` [${candidate.reason}${candidate.conflictsWith ? `: ${candidate.conflictsWith}` : ""}]` : "";
9255
+ console.log(` ${CANDIDATE_MARKS[candidate.status]} ${candidate.name}${reason} (${autoAliasEvidence(candidate)})`);
9256
+ }
9257
+ }
9258
+ if (!result.applied && result.changed) {
9259
+ console.log(`
9260
+ Run with --apply to store these changes now (the server also applies them after the next sweep).`);
9261
+ }
9188
9262
  }
9189
9263
  async function removeCompetitors(project2, domains, format) {
9190
9264
  const client = getClient10();
@@ -9233,7 +9307,12 @@ async function listCompetitors(project2, format) {
9233
9307
  `);
9234
9308
  for (const c of comps) {
9235
9309
  const aliases = storedAliases(c);
9236
- console.log(aliases.length > 0 ? ` ${c.domain} (aliases: ${aliases.join(", ")})` : ` ${c.domain}`);
9310
+ const auto = (c.autoAliases ?? []).map((record) => record.name);
9311
+ const names = [
9312
+ ...aliases.length > 0 ? [`aliases: ${aliases.join(", ")}`] : [],
9313
+ ...auto.length > 0 ? [`auto: ${auto.join(", ")}`] : []
9314
+ ];
9315
+ console.log(names.length > 0 ? ` ${c.domain} (${names.join("; ")})` : ` ${c.domain}`);
9237
9316
  }
9238
9317
  }
9239
9318
  async function showCompetitorLandscape(project2, options) {
@@ -9325,7 +9404,8 @@ function printLandscapeRows(heading, rows, showSampleCount = false, context, que
9325
9404
 
9326
9405
  // src/cli-commands/competitor.ts
9327
9406
  var ADD_USAGE = "canonry competitor add <project> <domain...> [--alias <name>]... [--format json]";
9328
- var ALIASES_USAGE = "canonry competitor aliases <project> <domain> [--set <name>]... [--add <name>]... [--remove <name>]... [--clear] [--format json]";
9407
+ var ALIASES_USAGE = "canonry competitor aliases <project> <domain> [--set <name>]... [--add <name>]... [--remove <name>]... [--clear] [--block <name>]... [--unblock <name>]... [--format json]";
9408
+ var DETECT_USAGE = "canonry competitor aliases detect <project> [--apply] [--format json|jsonl]";
9329
9409
  var LANDSCAPE_USAGE = "canonry competitor landscape <project> [--window 7d|30d|90d|all] [--group-key <key>|--scope all-markets] [--by-model] [--provider <provider> [--model <id>]] [--query-class all|branded|non-brand] [--answers all|not-mentioned] [--location <label>] [--run-id <id>] [--format json|jsonl]";
9330
9410
  function parseLandscapeScope(value) {
9331
9411
  if (value === void 0 || value === "") return void 0;
@@ -9366,6 +9446,20 @@ Usage: ${ADD_USAGE}`, {
9366
9446
  await addCompetitors(project2, domains, input.format, aliases);
9367
9447
  }
9368
9448
  },
9449
+ {
9450
+ // Longest path wins in dispatch, so `detect` is reserved as the first
9451
+ // positional: a project literally named "detect" reads its aliases with
9452
+ // `competitor list detect`.
9453
+ path: ["competitor", "aliases", "detect"],
9454
+ usage: DETECT_USAGE,
9455
+ options: {
9456
+ apply: { type: "boolean" }
9457
+ },
9458
+ run: async (input) => {
9459
+ const project2 = requireProject(input, "competitor.aliases.detect", DETECT_USAGE);
9460
+ await detectCompetitorAutoAliases(project2, { apply: getBoolean(input.values, "apply"), format: input.format });
9461
+ }
9462
+ },
9369
9463
  {
9370
9464
  path: ["competitor", "aliases"],
9371
9465
  usage: ALIASES_USAGE,
@@ -9373,7 +9467,9 @@ Usage: ${ADD_USAGE}`, {
9373
9467
  set: multiStringOption(),
9374
9468
  add: multiStringOption(),
9375
9469
  remove: multiStringOption(),
9376
- clear: { type: "boolean" }
9470
+ clear: { type: "boolean" },
9471
+ block: multiStringOption(),
9472
+ unblock: multiStringOption()
9377
9473
  },
9378
9474
  run: async (input) => {
9379
9475
  const project2 = requireProject(input, "competitor.aliases", ALIASES_USAGE);
@@ -9386,6 +9482,15 @@ Usage: ${ADD_USAGE}`, {
9386
9482
  const add = getStringArray(input.values, "add");
9387
9483
  const remove = getStringArray(input.values, "remove");
9388
9484
  const clear = getBoolean(input.values, "clear");
9485
+ const block = getStringArray(input.values, "block");
9486
+ const unblock = getStringArray(input.values, "unblock");
9487
+ if ((block !== void 0 || unblock !== void 0) && (block !== void 0 && unblock !== void 0 || set !== void 0 || clear || add !== void 0 || remove !== void 0)) {
9488
+ throw usageError(`Error: --block and --unblock change answer-derived auto-detection only, so they cannot be combined with each other or with curated edits (--set, --add, --remove, --clear)
9489
+ Usage: ${ALIASES_USAGE}`, {
9490
+ message: "--block and --unblock cannot be combined with each other or with --set/--add/--remove/--clear",
9491
+ details: { command: "competitor.aliases", usage: ALIASES_USAGE }
9492
+ });
9493
+ }
9389
9494
  const exclusive = [set !== void 0, clear].filter(Boolean).length;
9390
9495
  if (exclusive > 1 || exclusive === 1 && (add !== void 0 || remove !== void 0)) {
9391
9496
  throw usageError(`Error: --set and --clear replace the whole list, so they cannot be combined with each other or with --add/--remove
@@ -9394,7 +9499,7 @@ Usage: ${ALIASES_USAGE}`, {
9394
9499
  details: { command: "competitor.aliases", usage: ALIASES_USAGE }
9395
9500
  });
9396
9501
  }
9397
- await competitorAliases(project2, domain, { set, add, remove, clear, format: input.format });
9502
+ await competitorAliases(project2, domain, { set, add, remove, clear, block, unblock, format: input.format });
9398
9503
  }
9399
9504
  },
9400
9505
  {
@@ -13685,12 +13790,20 @@ async function showProject(name, format) {
13685
13790
  if (project2.siteAuditMaxPages !== void 0) {
13686
13791
  console.log(` Site Health page budget: ${formatSiteAuditPageBudget(project2.siteAuditMaxPages)}`);
13687
13792
  }
13793
+ if (project2.competitorAutoAliases !== void 0) {
13794
+ console.log(` Competitor auto aliases: ${formatCompetitorAutoAliasMode(project2.competitorAutoAliases)}`);
13795
+ }
13688
13796
  console.log(` Tags: ${project2.tags.length > 0 ? project2.tags.join(", ") : "(none)"}`);
13689
13797
  const labelEntries = Object.entries(project2.labels);
13690
13798
  console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
13691
13799
  if (project2.createdAt) console.log(` Created: ${project2.createdAt}`);
13692
13800
  if (project2.updatedAt) console.log(` Updated: ${project2.updatedAt}`);
13693
13801
  }
13802
+ function formatCompetitorAutoAliasMode(mode) {
13803
+ if (mode === "apply") return "apply (names detected after each sweep are stored and counted)";
13804
+ if (mode === "off") return "off (no detection after sweeps)";
13805
+ return "preview (detected after each sweep and logged; nothing stored)";
13806
+ }
13694
13807
  async function updateProjectSettings(name, opts) {
13695
13808
  const client = getClient22();
13696
13809
  const project2 = await client.getProject(name);
@@ -13771,7 +13884,9 @@ async function updateProjectSettings(name, opts) {
13771
13884
  ...opts.negativeReviewMaxStars !== void 0 ? { negativeReviewMaxStars: opts.negativeReviewMaxStars } : {},
13772
13885
  // Same for the page budget: never echoed from the read above, so an edit
13773
13886
  // that does not touch it cannot overwrite a newer one or send a stray null.
13774
- ...opts.siteAuditMaxPages !== void 0 ? { siteAuditMaxPages: opts.siteAuditMaxPages } : {}
13887
+ ...opts.siteAuditMaxPages !== void 0 ? { siteAuditMaxPages: opts.siteAuditMaxPages } : {},
13888
+ // Same for the detection mode.
13889
+ ...opts.competitorAutoAliases !== void 0 ? { competitorAutoAliases: opts.competitorAutoAliases } : {}
13775
13890
  });
13776
13891
  warnDroppedDispatchModes(touchesDispatch ? providerDispatchModes : project2.providerDispatchModes ?? {}, result);
13777
13892
  if (isMachineFormat(opts.format)) {
@@ -13925,7 +14040,7 @@ var PROJECT_CLI_COMMANDS = [
13925
14040
  },
13926
14041
  {
13927
14042
  path: ["project", "update"],
13928
- usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--add-qualified-alias <name>...] [--remove-qualified-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--site-audit-max-pages <1-50000|full>] [--format json]",
14043
+ usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--add-qualified-alias <name>...] [--remove-qualified-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--site-audit-max-pages <1-50000|full>] [--competitor-auto-aliases <off|preview|apply>] [--format json]",
13929
14044
  options: {
13930
14045
  domain: { type: "string", short: "d" },
13931
14046
  "owned-domain": multiStringOption(),
@@ -13946,13 +14061,14 @@ var PROJECT_CLI_COMMANDS = [
13946
14061
  "dispatch-mode": multiStringOption(),
13947
14062
  "clear-dispatch-mode": multiStringOption(),
13948
14063
  "negative-review-max-stars": stringOption(),
13949
- "site-audit-max-pages": stringOption()
14064
+ "site-audit-max-pages": stringOption(),
14065
+ "competitor-auto-aliases": stringOption()
13950
14066
  },
13951
14067
  run: async (input) => {
13952
14068
  const name = requireProject(
13953
14069
  input,
13954
14070
  "project.update",
13955
- "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--add-qualified-alias <name>...] [--remove-qualified-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--site-audit-max-pages <1-50000|full>] [--format json]"
14071
+ "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--add-qualified-alias <name>...] [--remove-qualified-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--site-audit-max-pages <1-50000|full>] [--competitor-auto-aliases <off|preview|apply>] [--format json]"
13956
14072
  );
13957
14073
  const providers = getStringArray(input.values, "provider");
13958
14074
  const allProviders = getBoolean(input.values, "all-providers");
@@ -13987,6 +14103,7 @@ var PROJECT_CLI_COMMANDS = [
13987
14103
  clearDispatchModes,
13988
14104
  negativeReviewMaxStars: parseNegativeReviewMaxStars(getString(input.values, "negative-review-max-stars")),
13989
14105
  siteAuditMaxPages: parseSiteAuditMaxPages(getString(input.values, "site-audit-max-pages"), "project.update"),
14106
+ competitorAutoAliases: parseCompetitorAutoAliasMode(getString(input.values, "competitor-auto-aliases")),
13990
14107
  format: input.format
13991
14108
  });
13992
14109
  }
@@ -14151,6 +14268,17 @@ function parseSiteAuditMaxPages(raw, command) {
14151
14268
  }
14152
14269
  return parsed.data;
14153
14270
  }
14271
+ function parseCompetitorAutoAliasMode(raw) {
14272
+ if (raw === void 0) return void 0;
14273
+ const parsed = competitorAutoAliasModeSchema.safeParse(raw.trim().toLowerCase());
14274
+ if (!parsed.success) {
14275
+ throw usageError("Error: --competitor-auto-aliases must be off, preview or apply", {
14276
+ message: "--competitor-auto-aliases must be off, preview or apply",
14277
+ details: { command: "project.update", option: "competitor-auto-aliases", value: raw }
14278
+ });
14279
+ }
14280
+ return parsed.data;
14281
+ }
14154
14282
 
14155
14283
  // src/commands/organic-evidence.ts
14156
14284
  async function showOrganicEvidence(project2, opts) {
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-T2V2PESH.js";
8
+ } from "./chunk-OH7E7TJD.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-ZLB7WX5D.js";
12
+ } from "./chunk-7G7YVTDA.js";
13
13
  import {
14
14
  HISTORY_WINDOW_RUNS,
15
15
  IntelligenceService,
@@ -83,7 +83,7 @@ import {
83
83
  siteCrawlPages,
84
84
  siteCrawlSnapshots,
85
85
  trafficSources
86
- } from "./chunk-KRFTA4I7.js";
86
+ } from "./chunk-HA2BAJ6Z.js";
87
87
  import {
88
88
  RunKinds,
89
89
  SiteCrawlFetchStates,
@@ -112,7 +112,7 @@ import {
112
112
  roundPreservingTotal,
113
113
  siteAuditPageFactorSchema,
114
114
  wilsonInterval
115
- } from "./chunk-DO7XPDG5.js";
115
+ } from "./chunk-CF7G2HGX.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-4BKRZGLC.js";
6
+ } from "./chunk-S46DWUNR.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-LO56QXPO.js";
10
- import "./chunk-T2V2PESH.js";
11
- import "./chunk-ZLB7WX5D.js";
12
- import "./chunk-KRFTA4I7.js";
13
- import "./chunk-DO7XPDG5.js";
9
+ } from "./chunk-GQAESNJT.js";
10
+ import "./chunk-OH7E7TJD.js";
11
+ import "./chunk-7G7YVTDA.js";
12
+ import "./chunk-HA2BAJ6Z.js";
13
+ import "./chunk-CF7G2HGX.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-KRFTA4I7.js";
5
- import "./chunk-DO7XPDG5.js";
4
+ } from "./chunk-HA2BAJ6Z.js";
5
+ import "./chunk-CF7G2HGX.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-AAMKTL6B.js";
3
+ } from "./chunk-JRBQFRNT.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-LO56QXPO.js";
8
- import "./chunk-ZLB7WX5D.js";
7
+ } from "./chunk-GQAESNJT.js";
8
+ import "./chunk-7G7YVTDA.js";
9
9
  import {
10
10
  isReadOnlyKey,
11
11
  updateCheckEnvOptOut
12
- } from "./chunk-DO7XPDG5.js";
12
+ } from "./chunk-CF7G2HGX.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": "7.9.1",
3
+ "version": "7.11.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": [
@@ -85,32 +85,32 @@
85
85
  "@types/node-cron": "^3.0.11",
86
86
  "tsup": "^8.5.1",
87
87
  "tsx": "^4.19.0",
88
- "@ainyc/canonry-web": "0.0.0",
89
- "@ainyc/canonry-api-routes": "0.0.0",
90
- "@ainyc/canonry-config": "0.0.0",
91
88
  "@ainyc/canonry-api-client": "0.0.0",
92
89
  "@ainyc/canonry-contracts": "0.0.0",
90
+ "@ainyc/canonry-api-routes": "0.0.0",
91
+ "@ainyc/canonry-config": "0.0.0",
93
92
  "@ainyc/canonry-db": "0.0.0",
93
+ "@ainyc/canonry-web": "0.0.0",
94
94
  "@ainyc/canonry-integration-bing": "0.0.0",
95
95
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
96
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
97
96
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
98
97
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
99
98
  "@ainyc/canonry-integration-google": "0.0.0",
100
99
  "@ainyc/canonry-integration-google-ads": "0.0.0",
101
- "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
100
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
102
101
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
102
+ "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
103
+ "@ainyc/canonry-integration-google-places": "0.0.0",
103
104
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
104
105
  "@ainyc/canonry-integration-traffic": "0.0.0",
105
106
  "@ainyc/canonry-integration-wordpress": "0.0.0",
106
- "@ainyc/canonry-intelligence": "0.0.0",
107
- "@ainyc/canonry-integration-google-places": "0.0.0",
108
107
  "@ainyc/canonry-provider-cdp": "0.0.0",
109
- "@ainyc/canonry-provider-gemini": "0.0.0",
108
+ "@ainyc/canonry-intelligence": "0.0.0",
110
109
  "@ainyc/canonry-provider-claude": "0.0.0",
111
110
  "@ainyc/canonry-provider-local": "0.0.0",
112
- "@ainyc/canonry-provider-openai": "0.0.0",
113
111
  "@ainyc/canonry-provider-muse": "0.0.0",
112
+ "@ainyc/canonry-provider-gemini": "0.0.0",
113
+ "@ainyc/canonry-provider-openai": "0.0.0",
114
114
  "@ainyc/canonry-provider-perplexity": "0.0.0",
115
115
  "@ainyc/canonry-integration-typesafe": "0.0.0"
116
116
  },