@canonry/canonry 7.10.0 → 7.12.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 (35) 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-BgcvPo6g.js → AuditHistoryPanel-Bpkuj-iF.js} +1 -1
  5. package/assets/assets/{BacklinksPage-DZ4tauAs.js → BacklinksPage-CbRIZhu5.js} +1 -1
  6. package/assets/assets/{HistoryPage-DWG52dz1.js → HistoryPage-Dfq1vK9k.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-DTx2iQLV.js → MeasurementPropertyPage-DcH0zYNN.js} +1 -1
  8. package/assets/assets/ProjectPage-enF4twWt.js +8 -0
  9. package/assets/assets/{RunsPage-B6MhRqxN.js → RunsPage-aOeJ60pX.js} +1 -1
  10. package/assets/assets/{SettingsPage-Bxv7OSHT.js → SettingsPage-nA0_ImoQ.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-C5x7oiFL.js → SiteHealthSection-CUOt7jeo.js} +3 -3
  12. package/assets/assets/{TrafficPage-C5gyQopi.js → TrafficPage-C0fVYizx.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-Dv2ZRNvN.js → TrafficSourceDetailPage-LMhXbFlA.js} +1 -1
  14. package/assets/assets/{extract-error-message-C5pr51ph.js → extract-error-message-5aHy0fsK.js} +1 -1
  15. package/assets/assets/index-BJh8cX5E.js +86 -0
  16. package/assets/assets/index-BNeiohKS.css +1 -0
  17. package/assets/assets/{react-sigma_core.esm.min-P5bVaYly.js → react-sigma_core.esm.min-ldX35rMq.js} +1 -1
  18. package/assets/assets/{v2-overview-adapter-QtI4xO_4.js → v2-overview-adapter-CaO9vY-l.js} +1 -1
  19. package/assets/index.html +2 -2
  20. package/dist/{chunk-5ZBN3EEK.js → chunk-7I64KEGM.js} +2 -2
  21. package/dist/{chunk-66DVP4DN.js → chunk-AU7OUDWR.js} +5786 -4450
  22. package/dist/{chunk-VSZJVBVR.js → chunk-BTHEFJCI.js} +60 -8
  23. package/dist/{chunk-FVMWUT2R.js → chunk-DXI2FTSW.js} +706 -257
  24. package/dist/{chunk-HL3C6Z7T.js → chunk-EPSHIH63.js} +3 -3
  25. package/dist/{chunk-52PHEGHT.js → chunk-GQMSJEMZ.js} +3515 -2266
  26. package/dist/{chunk-IKXVLT4M.js → chunk-SM6ZZAPK.js} +93 -5
  27. package/dist/cli.js +177 -40
  28. package/dist/{demo-server-WZIB4JKJ.js → demo-server-WJUA4EIG.js} +4 -4
  29. package/dist/index.js +6 -6
  30. package/dist/{intelligence-service-KC62A4X5.js → intelligence-service-DJRH4Z3D.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +12 -12
  33. package/assets/assets/ProjectPage-CCf70zbI.js +0 -8
  34. package/assets/assets/index-BhXHm5nG.js +0 -86
  35. package/assets/assets/index-Rcbm8bK1.css +0 -1
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-VSZJVBVR.js";
8
+ } from "./chunk-BTHEFJCI.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -50,7 +50,7 @@ import {
50
50
  skillsClientSchema,
51
51
  upgradeCaveatFor,
52
52
  upgradeCommandFor
53
- } from "./chunk-66DVP4DN.js";
53
+ } from "./chunk-AU7OUDWR.js";
54
54
 
55
55
  // src/config.ts
56
56
  import fs from "fs";
@@ -2852,6 +2852,62 @@ var putApiV1ProjectsByNameCompetitorsByDomainAliases = (options) => {
2852
2852
  }
2853
2853
  });
2854
2854
  };
2855
+ var getApiV1ProjectsByNameCompetitorAutoAliases = (options) => {
2856
+ return (options.client ?? client).get({
2857
+ security: [
2858
+ {
2859
+ scheme: "bearer",
2860
+ type: "http"
2861
+ }
2862
+ ],
2863
+ url: "/api/v1/projects/{name}/competitor-auto-aliases",
2864
+ ...options
2865
+ });
2866
+ };
2867
+ var postApiV1ProjectsByNameCompetitorAutoAliases = (options) => {
2868
+ return (options.client ?? client).post({
2869
+ security: [
2870
+ {
2871
+ scheme: "bearer",
2872
+ type: "http"
2873
+ }
2874
+ ],
2875
+ url: "/api/v1/projects/{name}/competitor-auto-aliases",
2876
+ ...options
2877
+ });
2878
+ };
2879
+ var postApiV1ProjectsByNameCompetitorsByDomainAliasesBlock = (options) => {
2880
+ return (options.client ?? client).post({
2881
+ security: [
2882
+ {
2883
+ scheme: "bearer",
2884
+ type: "http"
2885
+ }
2886
+ ],
2887
+ url: "/api/v1/projects/{name}/competitors/{domain}/aliases/block",
2888
+ ...options,
2889
+ headers: {
2890
+ "Content-Type": "application/json",
2891
+ ...options.headers
2892
+ }
2893
+ });
2894
+ };
2895
+ var postApiV1ProjectsByNameCompetitorsByDomainAliasesUnblock = (options) => {
2896
+ return (options.client ?? client).post({
2897
+ security: [
2898
+ {
2899
+ scheme: "bearer",
2900
+ type: "http"
2901
+ }
2902
+ ],
2903
+ url: "/api/v1/projects/{name}/competitors/{domain}/aliases/unblock",
2904
+ ...options,
2905
+ headers: {
2906
+ "Content-Type": "application/json",
2907
+ ...options.headers
2908
+ }
2909
+ });
2910
+ };
2855
2911
  var getApiV1ProjectsByNameAnalyticsCompetitors = (options) => {
2856
2912
  return (options.client ?? client).get({
2857
2913
  security: [
@@ -6846,6 +6902,38 @@ var ApiClient = class {
6846
6902
  })
6847
6903
  );
6848
6904
  }
6905
+ /** Dry run of answer-derived competitor alias detection over stored answers. Writes nothing. */
6906
+ async previewCompetitorAutoAliases(project) {
6907
+ return this.invoke(
6908
+ () => getApiV1ProjectsByNameCompetitorAutoAliases({ client: this.heyClient, path: { name: project } })
6909
+ );
6910
+ }
6911
+ /** Run answer-derived competitor alias detection now and store the result. */
6912
+ async applyCompetitorAutoAliases(project) {
6913
+ return this.invoke(
6914
+ () => postApiV1ProjectsByNameCompetitorAutoAliases({ client: this.heyClient, path: { name: project } })
6915
+ );
6916
+ }
6917
+ /** Block names from auto-detection for one competitor (a stored auto name among them is removed). */
6918
+ async blockCompetitorAliases(project, domain, aliases) {
6919
+ return this.invoke(
6920
+ () => postApiV1ProjectsByNameCompetitorsByDomainAliasesBlock({
6921
+ client: this.heyClient,
6922
+ path: { name: project, domain },
6923
+ body: { aliases }
6924
+ })
6925
+ );
6926
+ }
6927
+ /** Release blocked names back to auto-detection for one competitor. */
6928
+ async unblockCompetitorAliases(project, domain, aliases) {
6929
+ return this.invoke(
6930
+ () => postApiV1ProjectsByNameCompetitorsByDomainAliasesUnblock({
6931
+ client: this.heyClient,
6932
+ path: { name: project, domain },
6933
+ body: { aliases }
6934
+ })
6935
+ );
6936
+ }
6849
6937
  async deleteCompetitors(project, competitors) {
6850
6938
  return this.invoke(
6851
6939
  () => deleteApiV1ProjectsByNameCompetitors({
@@ -7372,13 +7460,13 @@ var ApiClient = class {
7372
7460
  })
7373
7461
  );
7374
7462
  }
7375
- async listRuns(project, limit, kind, status) {
7463
+ async listRuns(project, limit, kind, status, excludeKind, excludeKinds) {
7376
7464
  return this.invoke(
7377
7465
  () => getApiV1ProjectsByNameRuns({
7378
7466
  client: this.heyClient,
7379
7467
  path: { name: project },
7380
- // kind/status arrive as free CLI strings; the server validates them against the enums.
7381
- query: { limit, kind, status }
7468
+ // Filters arrive as free CLI strings; the server validates them against the enums.
7469
+ query: { limit, kind, status, excludeKind, excludeKinds }
7382
7470
  })
7383
7471
  );
7384
7472
  }
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-FVMWUT2R.js";
36
+ } from "./chunk-DXI2FTSW.js";
36
37
  import {
37
38
  autoSyncSkills,
38
39
  formatAutoSyncNotice
39
- } from "./chunk-HL3C6Z7T.js";
40
+ } from "./chunk-EPSHIH63.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-IKXVLT4M.js";
57
+ } from "./chunk-SM6ZZAPK.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-5ZBN3EEK.js";
63
+ import "./chunk-7I64KEGM.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-VSZJVBVR.js";
75
+ } from "./chunk-BTHEFJCI.js";
75
76
  import {
76
77
  CLOUDFLARE_WORKER_BINDINGS,
77
78
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -83,10 +84,12 @@ import {
83
84
  backfillProjectAnswerMentions,
84
85
  classifyAiUserFetch,
85
86
  classifyCrawler,
87
+ competitorIdentityColumns,
86
88
  competitors,
87
89
  crawlerEventsHourly,
88
90
  createClient,
89
91
  createRunCompetitorResolver,
92
+ dashboardSessions,
90
93
  dataRepairCompletions,
91
94
  determineCitationState,
92
95
  gaAiReferrals,
@@ -99,8 +102,9 @@ import {
99
102
  queries,
100
103
  querySnapshots,
101
104
  rawEventSamples,
105
+ readMarketCompetitorNames,
102
106
  runs
103
- } from "./chunk-52PHEGHT.js";
107
+ } from "./chunk-GQMSJEMZ.js";
104
108
  import {
105
109
  AdsDeliverySnapshotStatuses,
106
110
  AdsHistoricalCampaignRollupStatuses,
@@ -155,6 +159,7 @@ import {
155
159
  buildModelChangeNotice,
156
160
  canonicalizeGtmAccountId,
157
161
  canonicalizeGtmResourceSelection,
162
+ competitorAutoAliasModeSchema,
158
163
  competitorLandscapeQuerySchema,
159
164
  computeCompetitorOverlap,
160
165
  contentTargetDismissRequestSchema,
@@ -229,7 +234,7 @@ import {
229
234
  visibilityCompareSelectionSchema,
230
235
  visibilityReportRequestSchema,
231
236
  winnabilityClassSchema
232
- } from "./chunk-66DVP4DN.js";
237
+ } from "./chunk-AU7OUDWR.js";
233
238
 
234
239
  // src/cli.ts
235
240
  import { pathToFileURL } from "url";
@@ -779,7 +784,7 @@ function parseDemoTrustedProxies(values) {
779
784
  async function demoCommand(options) {
780
785
  const { host, port } = parseDemoListenOptions(options);
781
786
  const trustProxy = parseDemoTrustedProxies(options.trustProxy);
782
- const { createDemoServer } = await import("./demo-server-WZIB4JKJ.js");
787
+ const { createDemoServer } = await import("./demo-server-WJUA4EIG.js");
783
788
  const app = await createDemoServer({ trustProxy });
784
789
  let stopping = false;
785
790
  const stop = () => {
@@ -857,7 +862,7 @@ async function backfillAnswerVisibilityCommand(opts) {
857
862
  else runIdsByProject.set(run.projectId, [run.id]);
858
863
  }
859
864
  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());
865
+ const competitorsForRun = createRunCompetitorResolver(db, db.select(competitorIdentityColumns).from(competitors).where(eq(competitors.projectId, project2.id)).orderBy(competitors.domain).all(), readMarketCompetitorNames(db, project2.id));
861
866
  const runIds = runIdsByProject.get(project2.id) ?? [];
862
867
  if (runIds.length === 0) continue;
863
868
  const projectDomains = effectiveDomains({
@@ -1193,7 +1198,7 @@ No DB writes performed. Re-run without --dry-run to apply.`);
1193
1198
  }
1194
1199
  }
1195
1200
  async function backfillInsightsCommand(project2, opts) {
1196
- const { IntelligenceService } = await import("./intelligence-service-KC62A4X5.js");
1201
+ const { IntelligenceService } = await import("./intelligence-service-DJRH4Z3D.js");
1197
1202
  const config = loadConfig();
1198
1203
  const db = createClient(config.database);
1199
1204
  migrate(db);
@@ -1509,19 +1514,6 @@ function reparseProviderSnapshot(provider, rawResponse) {
1509
1514
  return null;
1510
1515
  }
1511
1516
  }
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
1517
  function stringifyStoredSnapshotEnvelope(rawResponse, reparsed) {
1526
1518
  const parsed = parseJsonColumn(rawResponse, {});
1527
1519
  const apiResponse = resolveStoredApiResponse(parsed);
@@ -5614,7 +5606,7 @@ async function gaAiReferralDaily(project2, opts) {
5614
5606
  console.log(JSON.stringify(result, null, 2));
5615
5607
  return;
5616
5608
  } else if (opts?.format === "jsonl") {
5617
- emitJsonl(result.days.map((day) => ({ project: project2, window: opts?.window, startDate: opts?.startDate, endDate: opts?.endDate, ...day })));
5609
+ emitJsonl(result.days.map((day2) => ({ project: project2, window: opts?.window, startDate: opts?.startDate, endDate: opts?.endDate, ...day2 })));
5618
5610
  return;
5619
5611
  }
5620
5612
  if (result.days.length === 0) {
@@ -5627,10 +5619,10 @@ async function gaAiReferralDaily(project2, opts) {
5627
5619
  `);
5628
5620
  console.log(` ${"DATE".padEnd(dateWidth)} ${"SOURCE".padEnd(sourceWidth)} ${"SESSIONS".padEnd(10)}${"PAID".padEnd(8)}${"ORGANIC".padEnd(9)}`);
5629
5621
  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) {
5622
+ for (const day2 of result.days) {
5623
+ for (const entry of day2.bySource) {
5632
5624
  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)}`
5625
+ ` ${day2.date.padEnd(dateWidth)} ${entry.source.padEnd(sourceWidth)} ${String(entry.sessions).padEnd(10)}${String(entry.paidSessions).padEnd(8)}${String(entry.organicSessions).padEnd(9)}`
5634
5626
  );
5635
5627
  }
5636
5628
  }
@@ -9139,7 +9131,11 @@ function sameAliasName(a, b) {
9139
9131
  async function competitorAliases(project2, domain, options) {
9140
9132
  const client = getClient10();
9141
9133
  let result;
9142
- if (options.set !== void 0 || options.clear === true) {
9134
+ if (options.block !== void 0) {
9135
+ result = await client.blockCompetitorAliases(project2, domain, options.block);
9136
+ } else if (options.unblock !== void 0) {
9137
+ result = await client.unblockCompetitorAliases(project2, domain, options.unblock);
9138
+ } else if (options.set !== void 0 || options.clear === true) {
9143
9139
  result = await client.setCompetitorAliases(project2, domain, options.clear ? [] : options.set ?? []);
9144
9140
  } else {
9145
9141
  const target = normalizeCompetitorDomain(domain.trim());
@@ -9161,7 +9157,12 @@ async function competitorAliases(project2, domain, options) {
9161
9157
  throw new CliError({
9162
9158
  code: "NOT_FOUND",
9163
9159
  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}`,
9160
+ displayMessage: [
9161
+ `Error: competitor "${target}" is not a project competitor of "${project2}".`,
9162
+ `If an Advanced market pins it, list its plan names and learned names with: canonry competitor aliases detect ${project2}`,
9163
+ `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.`,
9164
+ `To track it in every market instead (it then counts outside its markets): canonry competitor add ${project2} ${target}`
9165
+ ].join("\n"),
9165
9166
  details: { project: project2, domain: target }
9166
9167
  });
9167
9168
  }
@@ -9184,7 +9185,81 @@ async function competitorAliases(project2, domain, options) {
9184
9185
  console.log(JSON.stringify(result, null, 2));
9185
9186
  return;
9186
9187
  }
9187
- console.log(`Aliases for ${result.domain}: ${formatAliases(storedAliases(result))}`);
9188
+ printCompetitorNames(result);
9189
+ }
9190
+ function day(iso) {
9191
+ return iso.slice(0, 10);
9192
+ }
9193
+ function autoAliasEvidence(record) {
9194
+ const precision = record.precision === null ? "n/a" : formatPercent(record.precision, RatioUnits.fraction);
9195
+ const lift = record.lift === void 0 || record.lift === null ? "" : ` (${record.lift}x the rate in answers that do not name it)`;
9196
+ const cased = record.nameCasedAnswers === void 0 ? "" : `; written as a name in ${record.nameCasedAnswers}`;
9197
+ const seen = record.firstSeen ? `; seen ${day(record.firstSeen)} to ${day(record.lastSeen)}` : "";
9198
+ 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}`;
9199
+ }
9200
+ function printCompetitorNames(competitor) {
9201
+ const auto = competitor.autoAliases ?? [];
9202
+ const blocked = competitor.blockedAliases ?? [];
9203
+ if (competitor.marketKeys) {
9204
+ console.log(`Plan names for ${competitor.domain} (Advanced markets: ${competitor.marketKeys.join(", ")}): ${formatAliases(storedAliases(competitor))}`);
9205
+ } else {
9206
+ console.log(`Aliases for ${competitor.domain}: ${formatAliases(storedAliases(competitor))}`);
9207
+ }
9208
+ if (auto.length === 0) {
9209
+ console.log("Auto-detected from stored answers: (none)");
9210
+ } else {
9211
+ console.log("Auto-detected from stored answers:");
9212
+ for (const record of auto) console.log(` ${record.name} (${autoAliasEvidence(record)})`);
9213
+ }
9214
+ console.log(`Blocked from auto-detection: ${formatAliases(blocked)}`);
9215
+ }
9216
+ var CANDIDATE_MARKS = {
9217
+ added: "+",
9218
+ kept: "=",
9219
+ removed: "-",
9220
+ review: "?",
9221
+ rejected: "x"
9222
+ };
9223
+ async function detectCompetitorAutoAliases(project2, options) {
9224
+ const client = getClient10();
9225
+ const result = options.apply ? await client.applyCompetitorAutoAliases(project2) : await client.previewCompetitorAutoAliases(project2);
9226
+ if (options.format === "json") {
9227
+ console.log(JSON.stringify(result, null, 2));
9228
+ return;
9229
+ }
9230
+ if (options.format === "jsonl") {
9231
+ emitJsonl(result.competitors.map((competitor) => ({ project: result.project, applied: result.applied, ...competitor })));
9232
+ return;
9233
+ }
9234
+ const { scan, thresholds } = result;
9235
+ console.log(`Competitor auto-alias detection for "${result.project}" (${result.applied ? "applied" : "dry run, nothing stored"})`);
9236
+ 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"})`);
9237
+ 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`);
9238
+ console.log("Legend: + added = kept - removed ? review only x rejected");
9239
+ if (result.competitors.length === 0) {
9240
+ console.log(`
9241
+ No competitors tracked for "${result.project}".`);
9242
+ return;
9243
+ }
9244
+ for (const competitor of result.competitors) {
9245
+ console.log(competitor.marketKeys ? `
9246
+ ${competitor.domain} (Advanced markets: ${competitor.marketKeys.join(", ")})` : `
9247
+ ${competitor.domain}`);
9248
+ if (competitor.aliases.length) console.log(` ${competitor.marketKeys ? "plan names" : "curated"}: ${competitor.aliases.join(", ")}`);
9249
+ if (competitor.blockedAliases.length) console.log(` blocked: ${competitor.blockedAliases.join(", ")}`);
9250
+ if (competitor.candidates.length === 0) {
9251
+ console.log(" (no candidate names paired with this site)");
9252
+ continue;
9253
+ }
9254
+ for (const candidate of competitor.candidates) {
9255
+ const reason = candidate.reason ? ` [${candidate.reason}${candidate.conflictsWith ? `: ${candidate.conflictsWith}` : ""}]` : "";
9256
+ console.log(` ${CANDIDATE_MARKS[candidate.status]} ${candidate.name}${reason} (${autoAliasEvidence(candidate)})`);
9257
+ }
9258
+ }
9259
+ if (!result.applied && result.changed) {
9260
+ console.log(`
9261
+ Run with --apply to store these changes now (the server also applies them after the next sweep).`);
9262
+ }
9188
9263
  }
9189
9264
  async function removeCompetitors(project2, domains, format) {
9190
9265
  const client = getClient10();
@@ -9233,7 +9308,12 @@ async function listCompetitors(project2, format) {
9233
9308
  `);
9234
9309
  for (const c of comps) {
9235
9310
  const aliases = storedAliases(c);
9236
- console.log(aliases.length > 0 ? ` ${c.domain} (aliases: ${aliases.join(", ")})` : ` ${c.domain}`);
9311
+ const auto = (c.autoAliases ?? []).map((record) => record.name);
9312
+ const names = [
9313
+ ...aliases.length > 0 ? [`aliases: ${aliases.join(", ")}`] : [],
9314
+ ...auto.length > 0 ? [`auto: ${auto.join(", ")}`] : []
9315
+ ];
9316
+ console.log(names.length > 0 ? ` ${c.domain} (${names.join("; ")})` : ` ${c.domain}`);
9237
9317
  }
9238
9318
  }
9239
9319
  async function showCompetitorLandscape(project2, options) {
@@ -9325,7 +9405,8 @@ function printLandscapeRows(heading, rows, showSampleCount = false, context, que
9325
9405
 
9326
9406
  // src/cli-commands/competitor.ts
9327
9407
  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]";
9408
+ var ALIASES_USAGE = "canonry competitor aliases <project> <domain> [--set <name>]... [--add <name>]... [--remove <name>]... [--clear] [--block <name>]... [--unblock <name>]... [--format json]";
9409
+ var DETECT_USAGE = "canonry competitor aliases detect <project> [--apply] [--format json|jsonl]";
9329
9410
  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
9411
  function parseLandscapeScope(value) {
9331
9412
  if (value === void 0 || value === "") return void 0;
@@ -9366,6 +9447,20 @@ Usage: ${ADD_USAGE}`, {
9366
9447
  await addCompetitors(project2, domains, input.format, aliases);
9367
9448
  }
9368
9449
  },
9450
+ {
9451
+ // Longest path wins in dispatch, so `detect` is reserved as the first
9452
+ // positional: a project literally named "detect" reads its aliases with
9453
+ // `competitor list detect`.
9454
+ path: ["competitor", "aliases", "detect"],
9455
+ usage: DETECT_USAGE,
9456
+ options: {
9457
+ apply: { type: "boolean" }
9458
+ },
9459
+ run: async (input) => {
9460
+ const project2 = requireProject(input, "competitor.aliases.detect", DETECT_USAGE);
9461
+ await detectCompetitorAutoAliases(project2, { apply: getBoolean(input.values, "apply"), format: input.format });
9462
+ }
9463
+ },
9369
9464
  {
9370
9465
  path: ["competitor", "aliases"],
9371
9466
  usage: ALIASES_USAGE,
@@ -9373,7 +9468,9 @@ Usage: ${ADD_USAGE}`, {
9373
9468
  set: multiStringOption(),
9374
9469
  add: multiStringOption(),
9375
9470
  remove: multiStringOption(),
9376
- clear: { type: "boolean" }
9471
+ clear: { type: "boolean" },
9472
+ block: multiStringOption(),
9473
+ unblock: multiStringOption()
9377
9474
  },
9378
9475
  run: async (input) => {
9379
9476
  const project2 = requireProject(input, "competitor.aliases", ALIASES_USAGE);
@@ -9386,6 +9483,15 @@ Usage: ${ADD_USAGE}`, {
9386
9483
  const add = getStringArray(input.values, "add");
9387
9484
  const remove = getStringArray(input.values, "remove");
9388
9485
  const clear = getBoolean(input.values, "clear");
9486
+ const block = getStringArray(input.values, "block");
9487
+ const unblock = getStringArray(input.values, "unblock");
9488
+ if ((block !== void 0 || unblock !== void 0) && (block !== void 0 && unblock !== void 0 || set !== void 0 || clear || add !== void 0 || remove !== void 0)) {
9489
+ 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)
9490
+ Usage: ${ALIASES_USAGE}`, {
9491
+ message: "--block and --unblock cannot be combined with each other or with --set/--add/--remove/--clear",
9492
+ details: { command: "competitor.aliases", usage: ALIASES_USAGE }
9493
+ });
9494
+ }
9389
9495
  const exclusive = [set !== void 0, clear].filter(Boolean).length;
9390
9496
  if (exclusive > 1 || exclusive === 1 && (add !== void 0 || remove !== void 0)) {
9391
9497
  throw usageError(`Error: --set and --clear replace the whole list, so they cannot be combined with each other or with --add/--remove
@@ -9394,7 +9500,7 @@ Usage: ${ALIASES_USAGE}`, {
9394
9500
  details: { command: "competitor.aliases", usage: ALIASES_USAGE }
9395
9501
  });
9396
9502
  }
9397
- await competitorAliases(project2, domain, { set, add, remove, clear, format: input.format });
9503
+ await competitorAliases(project2, domain, { set, add, remove, clear, block, unblock, format: input.format });
9398
9504
  }
9399
9505
  },
9400
9506
  {
@@ -13685,12 +13791,20 @@ async function showProject(name, format) {
13685
13791
  if (project2.siteAuditMaxPages !== void 0) {
13686
13792
  console.log(` Site Health page budget: ${formatSiteAuditPageBudget(project2.siteAuditMaxPages)}`);
13687
13793
  }
13794
+ if (project2.competitorAutoAliases !== void 0) {
13795
+ console.log(` Competitor auto aliases: ${formatCompetitorAutoAliasMode(project2.competitorAutoAliases)}`);
13796
+ }
13688
13797
  console.log(` Tags: ${project2.tags.length > 0 ? project2.tags.join(", ") : "(none)"}`);
13689
13798
  const labelEntries = Object.entries(project2.labels);
13690
13799
  console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
13691
13800
  if (project2.createdAt) console.log(` Created: ${project2.createdAt}`);
13692
13801
  if (project2.updatedAt) console.log(` Updated: ${project2.updatedAt}`);
13693
13802
  }
13803
+ function formatCompetitorAutoAliasMode(mode) {
13804
+ if (mode === "apply") return "apply (names detected after each sweep are stored and counted)";
13805
+ if (mode === "off") return "off (no detection after sweeps)";
13806
+ return "preview (detected after each sweep and logged; nothing stored)";
13807
+ }
13694
13808
  async function updateProjectSettings(name, opts) {
13695
13809
  const client = getClient22();
13696
13810
  const project2 = await client.getProject(name);
@@ -13771,7 +13885,9 @@ async function updateProjectSettings(name, opts) {
13771
13885
  ...opts.negativeReviewMaxStars !== void 0 ? { negativeReviewMaxStars: opts.negativeReviewMaxStars } : {},
13772
13886
  // Same for the page budget: never echoed from the read above, so an edit
13773
13887
  // that does not touch it cannot overwrite a newer one or send a stray null.
13774
- ...opts.siteAuditMaxPages !== void 0 ? { siteAuditMaxPages: opts.siteAuditMaxPages } : {}
13888
+ ...opts.siteAuditMaxPages !== void 0 ? { siteAuditMaxPages: opts.siteAuditMaxPages } : {},
13889
+ // Same for the detection mode.
13890
+ ...opts.competitorAutoAliases !== void 0 ? { competitorAutoAliases: opts.competitorAutoAliases } : {}
13775
13891
  });
13776
13892
  warnDroppedDispatchModes(touchesDispatch ? providerDispatchModes : project2.providerDispatchModes ?? {}, result);
13777
13893
  if (isMachineFormat(opts.format)) {
@@ -13925,7 +14041,7 @@ var PROJECT_CLI_COMMANDS = [
13925
14041
  },
13926
14042
  {
13927
14043
  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]",
14044
+ 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
14045
  options: {
13930
14046
  domain: { type: "string", short: "d" },
13931
14047
  "owned-domain": multiStringOption(),
@@ -13946,13 +14062,14 @@ var PROJECT_CLI_COMMANDS = [
13946
14062
  "dispatch-mode": multiStringOption(),
13947
14063
  "clear-dispatch-mode": multiStringOption(),
13948
14064
  "negative-review-max-stars": stringOption(),
13949
- "site-audit-max-pages": stringOption()
14065
+ "site-audit-max-pages": stringOption(),
14066
+ "competitor-auto-aliases": stringOption()
13950
14067
  },
13951
14068
  run: async (input) => {
13952
14069
  const name = requireProject(
13953
14070
  input,
13954
14071
  "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]"
14072
+ "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
14073
  );
13957
14074
  const providers = getStringArray(input.values, "provider");
13958
14075
  const allProviders = getBoolean(input.values, "all-providers");
@@ -13987,6 +14104,7 @@ var PROJECT_CLI_COMMANDS = [
13987
14104
  clearDispatchModes,
13988
14105
  negativeReviewMaxStars: parseNegativeReviewMaxStars(getString(input.values, "negative-review-max-stars")),
13989
14106
  siteAuditMaxPages: parseSiteAuditMaxPages(getString(input.values, "site-audit-max-pages"), "project.update"),
14107
+ competitorAutoAliases: parseCompetitorAutoAliasMode(getString(input.values, "competitor-auto-aliases")),
13990
14108
  format: input.format
13991
14109
  });
13992
14110
  }
@@ -14151,6 +14269,17 @@ function parseSiteAuditMaxPages(raw, command) {
14151
14269
  }
14152
14270
  return parsed.data;
14153
14271
  }
14272
+ function parseCompetitorAutoAliasMode(raw) {
14273
+ if (raw === void 0) return void 0;
14274
+ const parsed = competitorAutoAliasModeSchema.safeParse(raw.trim().toLowerCase());
14275
+ if (!parsed.success) {
14276
+ throw usageError("Error: --competitor-auto-aliases must be off, preview or apply", {
14277
+ message: "--competitor-auto-aliases must be off, preview or apply",
14278
+ details: { command: "project.update", option: "competitor-auto-aliases", value: raw }
14279
+ });
14280
+ }
14281
+ return parsed.data;
14282
+ }
14154
14283
 
14155
14284
  // src/commands/organic-evidence.ts
14156
14285
  async function showOrganicEvidence(project2, opts) {
@@ -14535,7 +14664,7 @@ async function showRun(id, format) {
14535
14664
  }
14536
14665
  async function listRuns(project2, opts) {
14537
14666
  const client = getClient23();
14538
- const runs2 = await client.listRuns(project2, opts?.limit, opts?.kind, opts?.status);
14667
+ const runs2 = await client.listRuns(project2, opts?.limit, opts?.kind, opts?.status, opts?.excludeKind, opts?.excludeKinds);
14539
14668
  if (opts?.format === "json") {
14540
14669
  console.log(JSON.stringify(runs2, null, 2));
14541
14670
  return;
@@ -14746,7 +14875,7 @@ async function fillRun(runId, opts = {}) {
14746
14875
  var RUN_TRIGGER_USAGE = "canonry run trigger <project> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--force] [--wait] [--format json]";
14747
14876
  var RUN_USAGE2 = "canonry run <project|--all> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--force] [--wait] [--format json]";
14748
14877
  var RUN_FILL_USAGE = "canonry run fill <run-id> [--provider <name>[,<name>]] [--dry-run] [--wait] [--format json]";
14749
- var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--status <status>] [--format json]";
14878
+ var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--exclude-kind <kind>] [--exclude-kinds <kind>[,<kind>]]... [--status <status>] [--format json]";
14750
14879
  var RUN_TRIGGER_OPTIONS = {
14751
14880
  provider: stringOption(),
14752
14881
  query: multiStringOption(),
@@ -14933,6 +15062,8 @@ var RUN_CLI_COMMANDS = [
14933
15062
  options: {
14934
15063
  limit: stringOption(),
14935
15064
  kind: stringOption(),
15065
+ "exclude-kind": stringOption(),
15066
+ "exclude-kinds": multiStringOption(),
14936
15067
  status: stringOption()
14937
15068
  },
14938
15069
  run: async (input) => {
@@ -14945,6 +15076,8 @@ var RUN_CLI_COMMANDS = [
14945
15076
  message: "--limit must be an integer"
14946
15077
  }),
14947
15078
  kind: getString(input.values, "kind"),
15079
+ excludeKind: getString(input.values, "exclude-kind"),
15080
+ excludeKinds: getStringArray(input.values, "exclude-kinds"),
14948
15081
  status: getString(input.values, "status")
14949
15082
  });
14950
15083
  }
@@ -16992,6 +17125,9 @@ async function bootstrapCommand(opts) {
16992
17125
  const rotating = existingDefault.keyHash !== keyHash;
16993
17126
  const needsUpdate = rotating || existingDefault.keyPrefix !== keyPrefix || !isDeepStrictEqual(existingDefault.scopes, ["*"]) || existingDefault.projectId !== null || existingDefault.revokedAt !== null;
16994
17127
  if (needsUpdate) {
17128
+ if (rotating || existingDefault.revokedAt !== null) {
17129
+ tx.delete(dashboardSessions).where(eq2(dashboardSessions.apiKeyId, existingDefault.id)).run();
17130
+ }
16995
17131
  tx.update(apiKeys).set({
16996
17132
  keyHash,
16997
17133
  keyPrefix,
@@ -17004,6 +17140,7 @@ async function bootstrapCommand(opts) {
17004
17140
  }
17005
17141
  for (const duplicate of existingDefaults) {
17006
17142
  if (duplicate.id === existingDefault.id) continue;
17143
+ tx.delete(dashboardSessions).where(eq2(dashboardSessions.apiKeyId, duplicate.id)).run();
17007
17144
  if (duplicate.revokedAt === null) {
17008
17145
  tx.update(apiKeys).set({ revokedAt: rotatedAt }).where(eq2(apiKeys.id, duplicate.id)).run();
17009
17146
  changed = true;
@@ -5,11 +5,11 @@ import {
5
5
  computeFactorAverages,
6
6
  layoutSiteCrawlGraphInput,
7
7
  persistSiteCrawlGraphLayout
8
- } from "./chunk-5ZBN3EEK.js";
8
+ } from "./chunk-7I64KEGM.js";
9
9
  import {
10
10
  PACKAGE_VERSION,
11
11
  canonryMcpTools
12
- } from "./chunk-VSZJVBVR.js";
12
+ } from "./chunk-BTHEFJCI.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-52PHEGHT.js";
86
+ } from "./chunk-GQMSJEMZ.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-66DVP4DN.js";
115
+ } from "./chunk-AU7OUDWR.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-FVMWUT2R.js";
6
+ } from "./chunk-DXI2FTSW.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-IKXVLT4M.js";
10
- import "./chunk-5ZBN3EEK.js";
11
- import "./chunk-VSZJVBVR.js";
12
- import "./chunk-52PHEGHT.js";
13
- import "./chunk-66DVP4DN.js";
9
+ } from "./chunk-SM6ZZAPK.js";
10
+ import "./chunk-7I64KEGM.js";
11
+ import "./chunk-BTHEFJCI.js";
12
+ import "./chunk-GQMSJEMZ.js";
13
+ import "./chunk-AU7OUDWR.js";
14
14
  export {
15
15
  GoogleMarketingRuntimeError,
16
16
  createGoogleMarketingCredentialStore,