@canonry/canonry 4.145.0 → 4.147.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/assets/{AuditHistoryPanel-CV9g5mLR.js → AuditHistoryPanel-DiQ0Ynw5.js} +1 -1
  2. package/assets/assets/{BacklinksPage-RTcYKA5I.js → BacklinksPage-DLUQuPE_.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-BMR0Dqnf.js → ChartPrimitives-BoEy1qo7.js} +1 -1
  4. package/assets/assets/{HistoryPage-DAN9QO6x.js → HistoryPage-mYR2vk8n.js} +1 -1
  5. package/assets/assets/MeasurementPropertyPage-BocyGHJN.js +1 -0
  6. package/assets/assets/ProjectPage-Q5wQ7nSW.js +12 -0
  7. package/assets/assets/{RunRow-WDCIS9MJ.js → RunRow-Ct-A6H9e.js} +1 -1
  8. package/assets/assets/{RunsPage-D3Zxd6oc.js → RunsPage-CsDtq0Qw.js} +1 -1
  9. package/assets/assets/{SettingsPage-DcCFVeA7.js → SettingsPage-BXwFGc1P.js} +1 -1
  10. package/assets/assets/{TrafficPage-D0tPBu5O.js → TrafficPage-B2omyoI8.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-CtyEE9eJ.js → TrafficSourceDetailPage-CoEqc6OJ.js} +1 -1
  12. package/assets/assets/{arrow-left-hBxXIGS1.js → arrow-left-Dh0vsQxe.js} +1 -1
  13. package/assets/assets/{extract-error-message-DRu89RHb.js → extract-error-message-DvWJ0xRn.js} +1 -1
  14. package/assets/assets/index-CXe6S1ey.css +1 -0
  15. package/assets/assets/index-FE8JPHmT.js +213 -0
  16. package/assets/assets/{trash-2-DP0ukgbl.js → trash-2-D1_8ZseY.js} +1 -1
  17. package/assets/assets/v2-overview-adapter-owDeFdUc.js +1 -0
  18. package/assets/assets/{vendor-markdown-6dwjPOKK.js → vendor-markdown-CpDJch4G.js} +1 -1
  19. package/assets/assets/{vendor-radix-CQ77EO2y.js → vendor-radix-DnzzbllM.js} +1 -1
  20. package/assets/assets/{vendor-recharts-DSxTZhaH.js → vendor-recharts-33ACLxsD.js} +1 -1
  21. package/assets/assets/vendor-tanstack-BjS7iY3g.js +1 -0
  22. package/assets/index.html +6 -6
  23. package/dist/{chunk-MBLATP5L.js → chunk-4BL24FQA.js} +485 -8
  24. package/dist/{chunk-EFBTZ6ZT.js → chunk-BVOU4UJM.js} +3753 -3018
  25. package/dist/{chunk-GTBCGJNT.js → chunk-DRDNHYIF.js} +4397 -1501
  26. package/dist/{chunk-XNAJXSZ2.js → chunk-ZDYS24LW.js} +4 -4
  27. package/dist/cli.js +364 -4
  28. package/dist/index.js +4 -4
  29. package/dist/{intelligence-service-DVMEVHAG.js → intelligence-service-7CGZ537D.js} +2 -2
  30. package/dist/mcp.js +5 -5
  31. package/package.json +11 -10
  32. package/assets/assets/ProjectPage-aFV2nddp.js +0 -9
  33. package/assets/assets/index-ANFzZ3nU.css +0 -1
  34. package/assets/assets/index-DSOfEiTH.js +0 -213
  35. package/assets/assets/vendor-tanstack-Be8JdV5M.js +0 -1
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-MBLATP5L.js";
13
+ } from "./chunk-4BL24FQA.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -126,7 +126,7 @@ import {
126
126
  siteAuditSnapshots,
127
127
  toAlertView,
128
128
  usageCounters
129
- } from "./chunk-GTBCGJNT.js";
129
+ } from "./chunk-DRDNHYIF.js";
130
130
  import {
131
131
  AGENT_MEMORY_VALUE_MAX_BYTES,
132
132
  AGENT_PROVIDER_IDS,
@@ -217,7 +217,7 @@ import {
217
217
  validationError,
218
218
  winnabilityClassLabel,
219
219
  withRetry
220
- } from "./chunk-EFBTZ6ZT.js";
220
+ } from "./chunk-BVOU4UJM.js";
221
221
 
222
222
  // src/telemetry.ts
223
223
  import crypto from "crypto";
@@ -7420,7 +7420,7 @@ function readStoredGroundingSources(rawResponse) {
7420
7420
  return result;
7421
7421
  }
7422
7422
  async function backfillInsightsCommand(project, opts) {
7423
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-DVMEVHAG.js");
7423
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-7CGZ537D.js");
7424
7424
  const config = loadConfig();
7425
7425
  const db = createClient(config.database);
7426
7426
  migrate(db);
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  showFirstRunNotice,
31
31
  trackCliCommandFinished,
32
32
  trackEvent
33
- } from "./chunk-XNAJXSZ2.js";
33
+ } from "./chunk-ZDYS24LW.js";
34
34
  import {
35
35
  CliError,
36
36
  EXIT_SYSTEM_ERROR,
@@ -46,8 +46,9 @@ import {
46
46
  printCliError,
47
47
  saveConfig,
48
48
  saveConfigPatch,
49
+ systemError,
49
50
  usageError
50
- } from "./chunk-MBLATP5L.js";
51
+ } from "./chunk-4BL24FQA.js";
51
52
  import {
52
53
  apiKeys,
53
54
  createClient,
@@ -55,7 +56,7 @@ import {
55
56
  projects,
56
57
  queries,
57
58
  renderReportHtml
58
- } from "./chunk-GTBCGJNT.js";
59
+ } from "./chunk-DRDNHYIF.js";
59
60
  import {
60
61
  AdsDeliverySnapshotStatuses,
61
62
  AdsHistoricalCampaignRollupStatuses,
@@ -68,6 +69,7 @@ import {
68
69
  CheckScopes,
69
70
  CheckStatuses,
70
71
  CitationStates,
72
+ MeasurementEvidenceShapes,
71
73
  READ_ONLY_SCOPE,
72
74
  REPORT_PERIOD_OPTIONS,
73
75
  ResearchRunStatuses,
@@ -106,7 +108,7 @@ import {
106
108
  providerQuotaPolicySchema,
107
109
  resolveProviderInput,
108
110
  winnabilityClassSchema
109
- } from "./chunk-EFBTZ6ZT.js";
111
+ } from "./chunk-BVOU4UJM.js";
110
112
 
111
113
  // src/cli.ts
112
114
  import { pathToFileURL } from "url";
@@ -15714,6 +15716,7 @@ Usage: ${usage}`, {
15714
15716
  ];
15715
15717
 
15716
15718
  // src/commands/measurement-plan.ts
15719
+ import crypto3 from "crypto";
15717
15720
  import fs16 from "fs";
15718
15721
  import { parse } from "yaml";
15719
15722
  function readPlan(source) {
@@ -15745,6 +15748,76 @@ async function publishMeasurementPlan(project, source) {
15745
15748
  async function retireMeasurementPlanSegment(project, stableKey) {
15746
15749
  console.log(JSON.stringify(await createApiClient().retireMeasurementPlanSegment(project, stableKey), null, 2));
15747
15750
  }
15751
+ async function measurementAssignmentRequest(project, opts) {
15752
+ const client = createApiClient();
15753
+ const current = await client.getMeasurementPlanDraft(project);
15754
+ if (!current.draft || !current.etag) {
15755
+ throw new Error(`Project "${project}" has no Advanced Measurement draft. Start setup in the dashboard or create a draft first.`);
15756
+ }
15757
+ const targetKeys = opts.allProperties ? current.draft.authoring.targets.filter((target) => target.status === "included").map((target) => target.stableKey) : opts.targetKeys;
15758
+ return {
15759
+ client,
15760
+ request: {
15761
+ ...targetKeys?.length ? { targetKeys } : {},
15762
+ ...opts.groupKeys?.length ? { groupKeys: opts.groupKeys } : {},
15763
+ queryIds: opts.queryIds
15764
+ }
15765
+ };
15766
+ }
15767
+ async function previewMeasurementPlanAssignments(project, opts) {
15768
+ const { client, request } = await measurementAssignmentRequest(project, opts);
15769
+ console.log(JSON.stringify(await client.previewMeasurementDraftAssignments(project, request), null, 2));
15770
+ }
15771
+ async function applyMeasurementPlanAssignments(project, opts) {
15772
+ const { client, request } = await measurementAssignmentRequest(project, opts);
15773
+ const preview = await client.previewMeasurementDraftAssignments(project, request);
15774
+ const result = await client.applyMeasurementDraftAssignments(
15775
+ project,
15776
+ request,
15777
+ crypto3.randomUUID(),
15778
+ preview.draftEtag
15779
+ );
15780
+ console.log(JSON.stringify({ preview, result }, null, 2));
15781
+ }
15782
+ async function replaceMeasurementPlanAssignments(project, opts) {
15783
+ const { client, request } = await measurementAssignmentRequest(project, opts);
15784
+ const preview = await client.previewMeasurementDraftAssignments(project, request);
15785
+ const result = await client.replaceMeasurementDraftAssignments(
15786
+ project,
15787
+ request,
15788
+ crypto3.randomUUID(),
15789
+ preview.draftEtag
15790
+ );
15791
+ console.log(JSON.stringify({ preview, result }, null, 2));
15792
+ }
15793
+ function readGroupMembershipCsv(source) {
15794
+ return source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
15795
+ }
15796
+ async function previewMeasurementPlanGroups(project, source) {
15797
+ const preview = await createApiClient().previewMeasurementDraftGroupMembership(project, {
15798
+ csv: readGroupMembershipCsv(source)
15799
+ });
15800
+ console.log(JSON.stringify(preview, null, 2));
15801
+ }
15802
+ async function applyMeasurementPlanGroups(project, source, opts) {
15803
+ const client = createApiClient();
15804
+ const csv = readGroupMembershipCsv(source);
15805
+ const preview = await client.previewMeasurementDraftGroupMembership(project, { csv });
15806
+ if (preview.counts.needsAttention > 0 && !opts.acknowledgeSkipped) {
15807
+ throw new Error(
15808
+ `${preview.counts.needsAttention} CSV rows need attention. Correct them or add --acknowledge-skipped to apply only the selected matched rows.`
15809
+ );
15810
+ }
15811
+ const acceptedRows = opts.acceptAllMatched ? preview.rows.filter((row) => row.status === "matched").map((row) => row.dataRow) : opts.acceptedRows ?? [];
15812
+ if (acceptedRows.length === 0) throw new Error("No matched CSV rows were selected for apply.");
15813
+ const result = await client.applyMeasurementDraftGroupMembership(project, {
15814
+ csv,
15815
+ sourceChecksum: preview.sourceChecksum,
15816
+ previewChecksum: preview.previewChecksum,
15817
+ acceptedRows
15818
+ }, crypto3.randomUUID(), preview.draftEtag);
15819
+ console.log(JSON.stringify({ preview, result }, null, 2));
15820
+ }
15748
15821
  async function discoverMeasurementTargets(project, sitemapUrl, ruleSource, maxUrls) {
15749
15822
  const request = measurementDiscoveryRequestSchema.parse({
15750
15823
  sitemapUrl,
@@ -15756,8 +15829,185 @@ async function discoverMeasurementTargets(project, sitemapUrl, ruleSource, maxUr
15756
15829
  async function showMeasurementReport(project, revision) {
15757
15830
  console.log(JSON.stringify(await createApiClient().getMeasurementReport(project, revision), null, 2));
15758
15831
  }
15832
+ var METRIC_REASONS = {
15833
+ plan_v1: "not measured (setup update required)",
15834
+ no_completed_run: "not measured (no completed run)",
15835
+ no_population: "not measured (no questions of this type)",
15836
+ evidence_incomplete: "not measured (evidence incomplete)",
15837
+ not_applicable: "not measured (not applicable)"
15838
+ };
15839
+ function metricText(metric) {
15840
+ if (metric.state === "unavailable") return METRIC_REASONS[metric.reason] ?? `not measured (${metric.reason})`;
15841
+ const percent = `${Math.round(metric.value * 100)}%`;
15842
+ return metric.numerator === void 0 || metric.denominator === void 0 ? percent : `${metric.numerator} of ${metric.denominator} (${percent})`;
15843
+ }
15844
+ async function showMeasurementProperty(project, opts) {
15845
+ const response = await createApiClient().getMeasurementOverview(project, {
15846
+ scope: "property",
15847
+ targetKey: opts.targetKey,
15848
+ ...opts.queryClass === void 0 ? {} : { queryClass: opts.queryClass },
15849
+ ...opts.provider === void 0 ? {} : { provider: opts.provider },
15850
+ ...opts.location === void 0 ? {} : { location: opts.location },
15851
+ ...opts.runId === void 0 ? {} : { runId: opts.runId }
15852
+ });
15853
+ if (isMachineFormat(opts.format)) {
15854
+ console.log(JSON.stringify(response, null, 2));
15855
+ return;
15856
+ }
15857
+ printMeasurementProperty(response);
15858
+ }
15859
+ function printMeasurementProperty(response) {
15860
+ const row = response.properties.items.at(0);
15861
+ const lines = [];
15862
+ lines.push(`${response.scope.label} \u2014 ${response.queryClass} questions`);
15863
+ lines.push(`Measurement: ${response.measurement.state}${response.measurement.displayedRunId ? ` \xB7 run ${response.measurement.displayedRunId}` : ""}`);
15864
+ lines.push("");
15865
+ lines.push(`Mentioned ${metricText(row ? row.mentionCoverage : response.metrics.mentionCoverage)}`);
15866
+ lines.push(`Cited ${metricText(row ? row.citationCoverage : response.metrics.citationCoverage)}`);
15867
+ if (row && row.flags > 0) lines.push(`Flagged ${row.flags} ${row.flags === 1 ? "result needs" : "results need"} review`);
15868
+ if (row && row.providers.length > 0) {
15869
+ lines.push("");
15870
+ lines.push(`${"Engine".padEnd(14)}${"Mentioned".padEnd(34)}Cited`);
15871
+ for (const provider of row.providers) {
15872
+ lines.push(`${provider.provider.padEnd(14)}${metricText(provider.mentionCoverage).padEnd(34)}${metricText(provider.citationCoverage)}`);
15873
+ }
15874
+ }
15875
+ console.log(lines.join("\n"));
15876
+ }
15877
+ function shapedPage(response) {
15878
+ if (response.answers !== void 0) return { shape: MeasurementEvidenceShapes.answers, ...response.answers };
15879
+ if (response.evidence !== void 0) return { shape: MeasurementEvidenceShapes.sources, ...response.evidence };
15880
+ throw systemError("The measurement property evidence response carried neither an evidence nor an answers page.");
15881
+ }
15882
+ async function showMeasurementPropertyEvidence(project, opts) {
15883
+ const response = await createApiClient().getMeasurementPropertyEvidence(project, {
15884
+ targetKey: opts.targetKey,
15885
+ ...opts.queryClass === void 0 ? {} : { queryClass: opts.queryClass },
15886
+ ...opts.provider === void 0 ? {} : { provider: opts.provider },
15887
+ ...opts.location === void 0 ? {} : { location: opts.location },
15888
+ ...opts.runId === void 0 ? {} : { runId: opts.runId },
15889
+ ...opts.shape === void 0 ? {} : { shape: opts.shape },
15890
+ ...opts.cursor === void 0 ? {} : { cursor: opts.cursor },
15891
+ ...opts.limit === void 0 ? {} : { limit: opts.limit }
15892
+ });
15893
+ if (opts.format === "jsonl") {
15894
+ const page = shapedPage(response);
15895
+ emitJsonl([{
15896
+ kind: "measurement-property-evidence-header",
15897
+ shape: page.shape,
15898
+ property: response.property,
15899
+ queryClass: response.queryClass,
15900
+ measurement: response.measurement,
15901
+ totalEstimate: page.totalEstimate ?? null,
15902
+ nextCursor: page.nextCursor ?? null
15903
+ }]);
15904
+ emitJsonl(page.items);
15905
+ return;
15906
+ }
15907
+ if (opts.format === "json") {
15908
+ console.log(JSON.stringify(response, null, 2));
15909
+ return;
15910
+ }
15911
+ printMeasurementPropertyEvidence(response);
15912
+ }
15913
+ function signalText(signal) {
15914
+ if (signal === null) return "not measured";
15915
+ return signal ? "yes" : "no";
15916
+ }
15917
+ function printMeasurementPropertyEvidence(response) {
15918
+ const lines = [];
15919
+ lines.push(`${response.property.label} \u2014 ${response.queryClass} questions`);
15920
+ if (response.measurement.state === "not_measured") {
15921
+ lines.push("Not measured yet. Run a measurement to collect source evidence.");
15922
+ console.log(lines.join("\n"));
15923
+ return;
15924
+ }
15925
+ const page = shapedPage(response);
15926
+ const answerShape = page.shape === MeasurementEvidenceShapes.answers;
15927
+ lines.push(`Measurement: ${response.measurement.state}${response.measurement.displayedRunId ? ` \xB7 run ${response.measurement.displayedRunId}` : ""}`);
15928
+ if (page.items.length === 0) {
15929
+ lines.push(answerShape ? "No answers matched this Property in the displayed run." : "No source evidence matched this Property in the displayed run.");
15930
+ console.log(lines.join("\n"));
15931
+ return;
15932
+ }
15933
+ lines.push(`${page.items.length} of ${page.totalEstimate ?? page.items.length} ${answerShape ? "answers" : "evidence rows"}`);
15934
+ lines.push("");
15935
+ if (page.shape === MeasurementEvidenceShapes.answers) {
15936
+ lines.push(`${"Engine".padEnd(12)}${"Question".padEnd(40)}${"Mentioned".padEnd(15)}${"Cited".padEnd(8)}Sources`);
15937
+ for (const item of page.items) {
15938
+ lines.push(
15939
+ `${item.provider.padEnd(12)}${item.queryText.slice(0, 39).padEnd(40)}${signalText(item.mentioned).padEnd(15)}${signalText(item.cited).padEnd(8)}${item.sources.length}`
15940
+ );
15941
+ }
15942
+ } else {
15943
+ lines.push(`${"Match".padEnd(14)}${"Engine".padEnd(12)}${"Question".padEnd(40)}URL`);
15944
+ for (const item of page.items) {
15945
+ lines.push(`${item.classification.padEnd(14)}${item.provider.padEnd(12)}${item.queryText.slice(0, 39).padEnd(40)}${item.sourceUrl}`);
15946
+ }
15947
+ }
15948
+ if (page.nextCursor) lines.push(`
15949
+ More rows: --cursor ${page.nextCursor}`);
15950
+ console.log(lines.join("\n"));
15951
+ }
15759
15952
 
15760
15953
  // src/cli-commands/measurement-plan.ts
15954
+ var QUERY_CLASSES = ["all", "branded", "non-brand"];
15955
+ var EVIDENCE_SHAPES = ["sources", "answers"];
15956
+ function queryClassOption(input) {
15957
+ const value = getString(input.values, "query-class");
15958
+ if (value === void 0) return void 0;
15959
+ const match = QUERY_CLASSES.find((candidate) => candidate === value);
15960
+ if (!match) throw usageError(`--query-class must be one of ${QUERY_CLASSES.join(", ")}`);
15961
+ return match;
15962
+ }
15963
+ function shapeOption(input) {
15964
+ const value = getString(input.values, "shape");
15965
+ if (value === void 0) return void 0;
15966
+ const match = EVIDENCE_SHAPES.find((candidate) => candidate === value);
15967
+ if (!match) throw usageError(`--shape must be one of ${EVIDENCE_SHAPES.join(", ")}`);
15968
+ return match;
15969
+ }
15970
+ function propertyScope(input, command, usage) {
15971
+ return {
15972
+ targetKey: requireStringOption(input, "target-key", { command, usage, message: "--target-key is required" }),
15973
+ queryClass: queryClassOption(input),
15974
+ provider: getString(input.values, "provider"),
15975
+ location: getString(input.values, "location"),
15976
+ runId: getString(input.values, "run-id"),
15977
+ format: input.format
15978
+ };
15979
+ }
15980
+ var PROPERTY_SCOPE_OPTIONS = {
15981
+ "target-key": stringOption(),
15982
+ "query-class": stringOption(),
15983
+ provider: stringOption(),
15984
+ location: stringOption(),
15985
+ "run-id": stringOption()
15986
+ };
15987
+ var ASSIGNMENT_AUDIENCE_OPTIONS = {
15988
+ group: multiStringOption(),
15989
+ "target-key": multiStringOption(),
15990
+ "all-properties": { type: "boolean" },
15991
+ "query-id": multiStringOption()
15992
+ };
15993
+ function assignmentAudience(input, command, usage) {
15994
+ const project = requireProject(input, command, usage);
15995
+ const groupKeys = getStringArray(input.values, "group")?.map((value) => value.trim()).filter(Boolean);
15996
+ const targetKeys = getStringArray(input.values, "target-key")?.map((value) => value.trim()).filter(Boolean);
15997
+ const queryIds = getStringArray(input.values, "query-id")?.map((value) => value.trim()).filter(Boolean) ?? [];
15998
+ const allProperties = getBoolean(input.values, "all-properties");
15999
+ if (queryIds.length === 0) throw usageError(`Error: at least one --query-id is required
16000
+ Usage: ${usage}`);
16001
+ if (allProperties && ((groupKeys?.length ?? 0) > 0 || (targetKeys?.length ?? 0) > 0)) {
16002
+ throw usageError(`Error: --all-properties cannot be combined with --group or --target-key
16003
+ Usage: ${usage}`);
16004
+ }
16005
+ if (!allProperties && (groupKeys?.length ?? 0) === 0 && (targetKeys?.length ?? 0) === 0) {
16006
+ throw usageError(`Error: select at least one --group, --target-key, or --all-properties
16007
+ Usage: ${usage}`);
16008
+ }
16009
+ return { project, options: { groupKeys, targetKeys, queryIds, allProperties } };
16010
+ }
15761
16011
  var MEASUREMENT_PLAN_CLI_COMMANDS = [
15762
16012
  { path: ["measurement-plan", "show"], usage: "canonry measurement-plan show <project> [--revision N] [--format json]", options: { revision: stringOption() }, run: async (input) => {
15763
16013
  const value = getString(input.values, "revision");
@@ -15778,6 +16028,85 @@ var MEASUREMENT_PLAN_CLI_COMMANDS = [
15778
16028
  if (!stableKey) throw usageError("stable segment key is required");
15779
16029
  return retireMeasurementPlanSegment(project, stableKey);
15780
16030
  } },
16031
+ {
16032
+ path: ["measurement-plan", "assignments", "preview"],
16033
+ usage: "canonry measurement-plan assignments preview <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...] [--format json]",
16034
+ options: ASSIGNMENT_AUDIENCE_OPTIONS,
16035
+ run: (input) => {
16036
+ const usage = "canonry measurement-plan assignments preview <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...]";
16037
+ const { project, options } = assignmentAudience(input, "measurement-plan.assignments.preview", usage);
16038
+ return previewMeasurementPlanAssignments(project, options);
16039
+ }
16040
+ },
16041
+ {
16042
+ path: ["measurement-plan", "assignments", "apply"],
16043
+ usage: "canonry measurement-plan assignments apply <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...] [--format json]",
16044
+ options: ASSIGNMENT_AUDIENCE_OPTIONS,
16045
+ run: (input) => {
16046
+ const usage = "canonry measurement-plan assignments apply <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...]";
16047
+ const { project, options } = assignmentAudience(input, "measurement-plan.assignments.apply", usage);
16048
+ return applyMeasurementPlanAssignments(project, options);
16049
+ }
16050
+ },
16051
+ {
16052
+ path: ["measurement-plan", "assignments", "replace"],
16053
+ usage: "canonry measurement-plan assignments replace <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...] --confirm [--format json]",
16054
+ options: { ...ASSIGNMENT_AUDIENCE_OPTIONS, confirm: { type: "boolean" } },
16055
+ run: (input) => {
16056
+ const usage = "canonry measurement-plan assignments replace <project> [--group KEY ...] [--target-key KEY ... | --all-properties] --query-id ID [--query-id ID ...] --confirm";
16057
+ if (!getBoolean(input.values, "confirm")) throw usageError(`Error: --confirm is required
16058
+ Usage: ${usage}`);
16059
+ const { project, options } = assignmentAudience(input, "measurement-plan.assignments.replace", usage);
16060
+ return replaceMeasurementPlanAssignments(project, options);
16061
+ }
16062
+ },
16063
+ {
16064
+ path: ["measurement-plan", "groups", "preview"],
16065
+ usage: "canonry measurement-plan groups preview <project> <csv|-> [--format json]",
16066
+ run: (input) => {
16067
+ const usage = "canonry measurement-plan groups preview <project> <csv|->";
16068
+ const project = requireProject(input, "measurement-plan.groups.preview", usage);
16069
+ const source = input.positionals[1];
16070
+ if (!source) throw usageError(`Error: CSV file path or - is required
16071
+ Usage: ${usage}`);
16072
+ return previewMeasurementPlanGroups(project, source);
16073
+ }
16074
+ },
16075
+ {
16076
+ path: ["measurement-plan", "groups", "apply"],
16077
+ usage: "canonry measurement-plan groups apply <project> <csv|-> --confirm [--accept-row N ... | --accept-all-matched] [--acknowledge-skipped] [--format json]",
16078
+ options: {
16079
+ confirm: { type: "boolean" },
16080
+ "accept-row": multiStringOption(),
16081
+ "accept-all-matched": { type: "boolean" },
16082
+ "acknowledge-skipped": { type: "boolean" }
16083
+ },
16084
+ run: (input) => {
16085
+ const usage = "canonry measurement-plan groups apply <project> <csv|-> --confirm [--accept-row N ... | --accept-all-matched] [--acknowledge-skipped]";
16086
+ const project = requireProject(input, "measurement-plan.groups.apply", usage);
16087
+ const source = input.positionals[1];
16088
+ if (!source) throw usageError(`Error: CSV file path or - is required
16089
+ Usage: ${usage}`);
16090
+ if (!getBoolean(input.values, "confirm")) throw usageError(`Error: --confirm is required
16091
+ Usage: ${usage}`);
16092
+ const acceptAllMatched = getBoolean(input.values, "accept-all-matched");
16093
+ const rowValues = getStringArray(input.values, "accept-row") ?? [];
16094
+ if (acceptAllMatched === rowValues.length > 0) {
16095
+ throw usageError(`Error: choose exactly one of --accept-row or --accept-all-matched
16096
+ Usage: ${usage}`);
16097
+ }
16098
+ const acceptedRows = rowValues.map((value) => Number(value));
16099
+ if (acceptedRows.some((row) => !Number.isSafeInteger(row) || row < 1)) {
16100
+ throw usageError(`Error: --accept-row must be a positive integer
16101
+ Usage: ${usage}`);
16102
+ }
16103
+ return applyMeasurementPlanGroups(project, source, {
16104
+ acceptedRows,
16105
+ acceptAllMatched,
16106
+ acknowledgeSkipped: getBoolean(input.values, "acknowledge-skipped")
16107
+ });
16108
+ }
16109
+ },
15781
16110
  {
15782
16111
  path: ["measurement-plan", "discover"],
15783
16112
  usage: "canonry measurement-plan discover <project> --sitemap-url <url> --rule <yaml|json|-> [--max-urls N] [--format json]",
@@ -15816,6 +16145,37 @@ var MEASUREMENT_PLAN_CLI_COMMANDS = [
15816
16145
  }
15817
16146
  return showMeasurementReport(project, revision);
15818
16147
  }
16148
+ },
16149
+ {
16150
+ path: ["measurement-plan", "property"],
16151
+ usage: "canonry measurement-plan property <project> --target-key <key> [--query-class all|branded|non-brand] [--provider <p>] [--location <l>] [--run-id <id>] [--format json]",
16152
+ options: PROPERTY_SCOPE_OPTIONS,
16153
+ run: (input) => {
16154
+ const usage = "canonry measurement-plan property <project> --target-key <key>";
16155
+ const project = requireProject(input, "measurement-plan.property", usage);
16156
+ return showMeasurementProperty(project, propertyScope(input, "measurement-plan.property", usage));
16157
+ }
16158
+ },
16159
+ {
16160
+ path: ["measurement-plan", "property-evidence"],
16161
+ usage: "canonry measurement-plan property-evidence <project> --target-key <key> [--query-class all|branded|non-brand] [--provider <p>] [--location <l>] [--run-id <id>] [--shape sources|answers] [--cursor <c>] [--limit N] [--format json|jsonl]",
16162
+ options: { ...PROPERTY_SCOPE_OPTIONS, shape: stringOption(), cursor: stringOption(), limit: stringOption() },
16163
+ run: (input) => {
16164
+ const usage = "canonry measurement-plan property-evidence <project> --target-key <key>";
16165
+ const project = requireProject(input, "measurement-plan.property-evidence", usage);
16166
+ const limitValue = getString(input.values, "limit");
16167
+ const limit = limitValue === void 0 ? void 0 : Number(limitValue);
16168
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 100)) {
16169
+ throw usageError("--limit must be an integer from 1 to 100");
16170
+ }
16171
+ const shape = shapeOption(input);
16172
+ return showMeasurementPropertyEvidence(project, {
16173
+ ...propertyScope(input, "measurement-plan.property-evidence", usage),
16174
+ ...shape === void 0 ? {} : { shape },
16175
+ cursor: getString(input.values, "cursor"),
16176
+ ...limit === void 0 ? {} : { limit }
16177
+ });
16178
+ }
15819
16179
  }
15820
16180
  ];
15821
16181
 
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-XNAJXSZ2.js";
3
+ } from "./chunk-ZDYS24LW.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-MBLATP5L.js";
7
- import "./chunk-GTBCGJNT.js";
8
- import "./chunk-EFBTZ6ZT.js";
6
+ } from "./chunk-4BL24FQA.js";
7
+ import "./chunk-DRDNHYIF.js";
8
+ import "./chunk-BVOU4UJM.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-GTBCGJNT.js";
4
- import "./chunk-EFBTZ6ZT.js";
3
+ } from "./chunk-DRDNHYIF.js";
4
+ import "./chunk-BVOU4UJM.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-MBLATP5L.js";
6
+ } from "./chunk-4BL24FQA.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-EFBTZ6ZT.js";
9
+ } from "./chunk-BVOU4UJM.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -110,9 +110,9 @@ var CANONRY_MCP_TOOLKIT_NAMES = ["monitoring", "setup", "gsc", "ga", "gbp", "ads
110
110
  var CANONRY_MCP_TOOLKITS = [
111
111
  {
112
112
  name: "monitoring",
113
- title: "Runs, snapshots, insights, health",
114
- description: "Inspect run history, query snapshots, intelligence insights, and health timelines.",
115
- whenToLoad: "Load when investigating regressions, comparing runs, or reviewing insights and health history."
113
+ title: "Measurement, runs, insights, health",
114
+ description: "Inspect portfolio and Property measurement, question results, run history, intelligence insights, and health timelines.",
115
+ whenToLoad: "Load when finding weak Properties, reviewing answers and competitors, checking data quality, comparing runs, or investigating regressions."
116
116
  },
117
117
  {
118
118
  name: "setup",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.145.0",
3
+ "version": "4.147.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -41,6 +41,7 @@
41
41
  "dependencies": {
42
42
  "@ainyc/aeo-audit": "4.0.1",
43
43
  "@anthropic-ai/sdk": "^0.91.1",
44
+ "@fastify/rate-limit": "^11.2.0",
44
45
  "@fastify/static": "^9.1.1",
45
46
  "@google/genai": "^1.46.0",
46
47
  "@mariozechner/pi-agent-core": "0.67.6",
@@ -66,26 +67,26 @@
66
67
  "tsup": "^8.5.1",
67
68
  "tsx": "^4.19.0",
68
69
  "@ainyc/canonry-api-client": "0.0.0",
69
- "@ainyc/canonry-api-routes": "0.0.0",
70
70
  "@ainyc/canonry-config": "0.0.0",
71
- "@ainyc/canonry-contracts": "0.0.0",
71
+ "@ainyc/canonry-api-routes": "0.0.0",
72
72
  "@ainyc/canonry-db": "0.0.0",
73
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
73
+ "@ainyc/canonry-contracts": "0.0.0",
74
74
  "@ainyc/canonry-integration-bing": "0.0.0",
75
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
75
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
76
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
76
77
  "@ainyc/canonry-integration-google": "0.0.0",
78
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
77
79
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
78
80
  "@ainyc/canonry-integration-google-places": "0.0.0",
79
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
81
+ "@ainyc/canonry-integration-traffic": "0.0.0",
80
82
  "@ainyc/canonry-integration-wordpress": "0.0.0",
81
83
  "@ainyc/canonry-intelligence": "0.0.0",
82
- "@ainyc/canonry-integration-traffic": "0.0.0",
83
84
  "@ainyc/canonry-provider-cdp": "0.0.0",
84
85
  "@ainyc/canonry-provider-claude": "0.0.0",
85
- "@ainyc/canonry-provider-local": "0.0.0",
86
86
  "@ainyc/canonry-provider-gemini": "0.0.0",
87
- "@ainyc/canonry-provider-perplexity": "0.0.0",
88
- "@ainyc/canonry-provider-openai": "0.0.0"
87
+ "@ainyc/canonry-provider-local": "0.0.0",
88
+ "@ainyc/canonry-provider-openai": "0.0.0",
89
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
89
90
  },
90
91
  "scripts": {
91
92
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",