@canonry/canonry 4.178.1 → 4.178.3

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.
@@ -332,7 +332,7 @@ cnry measurement-plan discover <project> --sitemap-url https://example.com/sitem
332
332
  cnry measurement-plan show <project> # active immutable revision
333
333
  cnry measurement-plan show <project> --revision 2 # one historical revision
334
334
  cnry measurement-plan versions <project>
335
- cnry measurement-plan publish <project> plan.yaml
335
+ cnry measurement-plan publish <project> plan.yaml # legacy schema v1 only; refuses over an active v2 plan (use: measurement-plan advanced <project> draft-action)
336
336
  cnry measurement-plan report <project> --revision 2 # stored evidence only; never starts provider work
337
337
  cnry measurement-plan retire <project> <stable-key>
338
338
  ```
@@ -92,7 +92,7 @@ import {
92
92
  trafficConnectWordpressRequestSchema,
93
93
  trafficEventKindSchema,
94
94
  trafficSeriesGranularitySchema
95
- } from "./chunk-OYDIND7Q.js";
95
+ } from "./chunk-BNYDRPKC.js";
96
96
 
97
97
  // src/config.ts
98
98
  import fs from "fs";
@@ -10528,8 +10528,8 @@ var canonryMcpTools = [
10528
10528
  }),
10529
10529
  defineTool({
10530
10530
  name: "canonry_measurement_plan_publish",
10531
- title: "Publish measurement plan",
10532
- description: "Publish only when the active revision still matches the revision you reviewed. Use null only when you reviewed a planless project.",
10531
+ title: "Publish measurement plan (legacy v1)",
10532
+ description: "Legacy schema-v1 publish. For Advanced Measurement use canonry_measurement_draft_action instead: this tool compiles only schema v1 and refuses when the active revision is schema v2 rather than downgrade it. Publish only when the active revision still matches the revision you reviewed. Use null only when you reviewed a planless project.",
10533
10533
  access: "write",
10534
10534
  tier: "setup",
10535
10535
  inputSchema: measurementPlanPublishInputSchema,
@@ -202,6 +202,7 @@ import {
202
202
  calendarMonthBounds,
203
203
  canonicalMeasurementExecutionIdentityJson,
204
204
  canonicalMeasurementPlanJson,
205
+ canonicalMeasurementPlanV2,
205
206
  canonicalMeasurementPlanV2Json,
206
207
  canonicalizeAdsActivationManifest,
207
208
  canonicalizeGoogleAdsCustomerSelection,
@@ -637,7 +638,7 @@ import {
637
638
  wordpressSchemaDeployResultDtoSchema,
638
639
  wordpressSchemaStatusResultDtoSchema,
639
640
  wordpressStatusDtoSchema
640
- } from "./chunk-OYDIND7Q.js";
641
+ } from "./chunk-BNYDRPKC.js";
641
642
 
642
643
  // src/intelligence-service.ts
643
644
  import { eq as eq62, desc as desc27, asc as asc11, and as and51, ne as ne8, or as or14, inArray as inArray21, gte as gte14, lte as lte12, isNull as isNull9, sql as sql25, exists } from "drizzle-orm";
@@ -826,6 +827,17 @@ var measurementPlanVersions = sqliteTable("measurement_plan_versions", {
826
827
  * never happened.
827
828
  */
828
829
  compiledChecksum: text("compiled_checksum"),
830
+ /**
831
+ * Continuity link written at publish time: the superseded active version this
832
+ * revision is measurement-comparable with. Set ONLY when the publish changed
833
+ * nothing about execution — the frozen execution nodes of both revisions are
834
+ * canonically identical — so a run pinned to the linked version answered
835
+ * exactly the questions this revision would ask. A label-only republish
836
+ * therefore keeps serving the previous run instead of blanking the dashboard
837
+ * until the next sweep. Null for the first revision, for every
838
+ * execution-changing publish, and on every historic row.
839
+ */
840
+ comparableToVersionId: text("comparable_to_version_id"),
829
841
  publishedBy: text("published_by"),
830
842
  sourceDraftId: text("source_draft_id"),
831
843
  createdAt: text("created_at").notNull()
@@ -7039,6 +7051,21 @@ var MIGRATION_VERSIONS = [
7039
7051
  `CREATE INDEX IF NOT EXISTS insight_notify_state_project
7040
7052
  ON insight_notify_state(project_id)`
7041
7053
  ]
7054
+ },
7055
+ {
7056
+ version: 149,
7057
+ name: "measurement-plan-version-continuity",
7058
+ // Every measurement read pins to the active plan version row id, but a
7059
+ // publish mints a new row for ANY compiled-checksum change — labels
7060
+ // included — so renaming a group blanked the dashboard until the next full
7061
+ // sweep. This column records, at publish time, the superseded version a
7062
+ // cosmetic (execution-identical) revision stays comparable with, so reads
7063
+ // can keep serving the previous run. Historic rows stay NULL on purpose:
7064
+ // their execution equality was never verified at publish time, and
7065
+ // backfilling one would claim a comparison nobody made.
7066
+ statements: [
7067
+ `ALTER TABLE measurement_plan_versions ADD COLUMN comparable_to_version_id TEXT`
7068
+ ]
7042
7069
  }
7043
7070
  ];
7044
7071
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -13258,10 +13285,37 @@ function responseFromReport(revision, report, run) {
13258
13285
  diagnostics: report.diagnostics
13259
13286
  };
13260
13287
  }
13261
- function latestMeasurementRun(db, projectId, versionId, statuses, window = {}) {
13288
+ var MEASUREMENT_COMPARABLE_VERSION_WALK_LIMIT = 32;
13289
+ function comparableMeasurementVersionIds(db, projectId, versionId) {
13290
+ const ids = [versionId];
13291
+ const seen = new Set(ids);
13292
+ let cursor = versionId;
13293
+ for (let step = 0; step < MEASUREMENT_COMPARABLE_VERSION_WALK_LIMIT; step++) {
13294
+ const row = db.select({ comparableToVersionId: measurementPlanVersions.comparableToVersionId }).from(measurementPlanVersions).where(and4(
13295
+ eq9(measurementPlanVersions.projectId, projectId),
13296
+ eq9(measurementPlanVersions.id, cursor)
13297
+ )).get();
13298
+ const next = row?.comparableToVersionId ?? null;
13299
+ if (next === null || seen.has(next)) break;
13300
+ ids.push(next);
13301
+ seen.add(next);
13302
+ cursor = next;
13303
+ }
13304
+ return ids;
13305
+ }
13306
+ function runVersionServesActiveVersion(db, projectId, activeVersionId, runVersionId) {
13307
+ if (runVersionId === null) return false;
13308
+ if (runVersionId === activeVersionId) return true;
13309
+ return comparableMeasurementVersionIds(db, projectId, activeVersionId).includes(runVersionId);
13310
+ }
13311
+ function latestMeasurementRun(db, projectId, versionId, statuses, window = {}, opts = {}) {
13262
13312
  const conditions = [
13263
13313
  eq9(runs.projectId, projectId),
13264
- eq9(runs.measurementPlanVersionId, versionId),
13314
+ // The revision-addressed report surface promises the revision AS-WAS and
13315
+ // must never borrow a predecessor's run through the comparable chain; the
13316
+ // active-dashboard surfaces want the chain so a label-only republish does
13317
+ // not blank them. exactVersion selects the contract.
13318
+ opts.exactVersion ? eq9(runs.measurementPlanVersionId, versionId) : inArray3(runs.measurementPlanVersionId, comparableMeasurementVersionIds(db, projectId, versionId)),
13265
13319
  eq9(runs.kind, RunKinds["answer-visibility"]),
13266
13320
  inArray3(runs.status, [...statuses]),
13267
13321
  ne2(runs.trigger, RunTriggers.probe),
@@ -13271,11 +13325,11 @@ function latestMeasurementRun(db, projectId, versionId, statuses, window = {}) {
13271
13325
  if (window.to !== void 0) conditions.push(lte2(runs.createdAt, window.to));
13272
13326
  return db.select().from(runs).where(and4(...conditions)).orderBy(desc(runs.createdAt), desc(runs.id)).get();
13273
13327
  }
13274
- function pinnedMeasurementRun(db, projectId, versionId, runId) {
13328
+ function pinnedMeasurementRun(db, projectId, versionId, runId, opts = {}) {
13275
13329
  return db.select().from(runs).where(and4(
13276
13330
  eq9(runs.id, runId),
13277
13331
  eq9(runs.projectId, projectId),
13278
- eq9(runs.measurementPlanVersionId, versionId),
13332
+ opts.exactVersion ? eq9(runs.measurementPlanVersionId, versionId) : inArray3(runs.measurementPlanVersionId, comparableMeasurementVersionIds(db, projectId, versionId)),
13279
13333
  eq9(runs.kind, RunKinds["answer-visibility"]),
13280
13334
  inArray3(runs.status, [RunStatuses.completed, RunStatuses.partial]),
13281
13335
  ne2(runs.trigger, RunTriggers.probe),
@@ -13287,7 +13341,7 @@ function measurementRunExpectedSlots(run, plan) {
13287
13341
  return parseManifest(run.measurementManifest, frozenExecutionNodes(plan), run.measurementScope === null);
13288
13342
  }
13289
13343
  function storedMeasurementPlanV2Report(db, projectId, version, plan, runId) {
13290
- const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId) : latestMeasurementRun(db, projectId, version.id, [RunStatuses.completed, RunStatuses.partial]);
13344
+ const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId, { exactVersion: true }) : latestMeasurementRun(db, projectId, version.id, [RunStatuses.completed, RunStatuses.partial], {}, { exactVersion: true });
13291
13345
  if (!run) {
13292
13346
  const empty = buildMeasurementPlanV2ReportInput(version.revision, plan, { schemaVersion: 1, expectedSlots: [] }, []);
13293
13347
  return {
@@ -13321,7 +13375,7 @@ function buildStoredMeasurementReport(db, projectId, revision, runId) {
13321
13375
  return storedMeasurementPlanV2Report(db, projectId, version, stored, runId);
13322
13376
  }
13323
13377
  const plan = stored;
13324
- const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId) : db.select().from(runs).where(and4(
13378
+ const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId, { exactVersion: true }) : db.select().from(runs).where(and4(
13325
13379
  eq9(runs.projectId, projectId),
13326
13380
  eq9(runs.measurementPlanVersionId, version.id),
13327
13381
  eq9(runs.kind, RunKinds["answer-visibility"]),
@@ -14527,6 +14581,11 @@ async function measurementPlanRoutes(app, opts) {
14527
14581
  if (activePlan && !activeVersion) {
14528
14582
  throw new Error(`Measurement plan ${project.id} points to missing version ${activePlan.activeVersionId}`);
14529
14583
  }
14584
+ if (activeVersion && activeVersion.schemaVersion !== 1) {
14585
+ throw validationError(
14586
+ `The active measurement plan is schema v${activeVersion.schemaVersion}; this legacy endpoint publishes only schema v1 and would downgrade it. Publish through the draft flow instead: POST /projects/:name/measurement-plan/draft/actions/publish (CLI: canonry measurement-plan advanced <project> draft-action; MCP: canonry_measurement_draft_action).`
14587
+ );
14588
+ }
14530
14589
  if (activeVersion && activeVersion.checksum === checksum) return { kind: "existing", version: activeVersion };
14531
14590
  const actualActiveRevision = activeVersion?.revision ?? null;
14532
14591
  if (actualActiveRevision !== expectedActiveRevision) {
@@ -15903,6 +15962,26 @@ function diffCompiledPlans(active, candidate, activeRevision) {
15903
15962
  }
15904
15963
  };
15905
15964
  }
15965
+ function plansAreLabelOnlyVariants(active, candidate) {
15966
+ const surface = (plan) => {
15967
+ const doc = canonicalMeasurementPlanV2(plan);
15968
+ const stripped = {
15969
+ ...doc,
15970
+ // compiledChecksum is DERIVED over the full document, labels included,
15971
+ // so keeping it would smuggle the stripped labels back into the
15972
+ // comparison. Everything else in the doc is semantic and stays.
15973
+ compiledChecksum: "",
15974
+ targets: doc.targets.map((target) => ({ ...target, label: "" })),
15975
+ groups: doc.groups.map((group) => ({
15976
+ ...group,
15977
+ label: "",
15978
+ competitors: group.competitors.map((competitor) => ({ ...competitor, label: "" }))
15979
+ }))
15980
+ };
15981
+ return JSON.stringify(canonicalJsonValue(stripped));
15982
+ };
15983
+ return surface(active) === surface(candidate);
15984
+ }
15906
15985
 
15907
15986
  // ../api-routes/src/measurement-draft-actions.ts
15908
15987
  var MEASUREMENT_DRAFT_ACTIONS = [
@@ -17361,7 +17440,10 @@ async function measurementDraftRoutes(app, opts = {}) {
17361
17440
  const activeSchemaVersion = active ? active.schemaVersion === 2 ? 2 : 1 : null;
17362
17441
  const completedRun = active ? app.db.select({ id: runs.id }).from(runs).where(and10(
17363
17442
  eq15(runs.projectId, project.id),
17364
- eq15(runs.measurementPlanVersionId, active.id),
17443
+ // The comparable chain, not the bare active id: a label-only
17444
+ // republish must not flip setup back to awaiting_first_run while
17445
+ // the overview keeps serving the prior run.
17446
+ inArray5(runs.measurementPlanVersionId, comparableMeasurementVersionIds(app.db, project.id, active.id)),
17365
17447
  eq15(runs.status, RunStatuses.completed)
17366
17448
  )).get() : void 0;
17367
17449
  const state = activeSchemaVersion === 1 ? "republish_required" : draft ? "setup_in_progress" : active ? completedRun ? "operational" : "awaiting_first_run" : "simple";
@@ -17769,6 +17851,7 @@ async function measurementDraftRoutes(app, opts = {}) {
17769
17851
  createdAt: now.toISOString()
17770
17852
  }).run();
17771
17853
  }
17854
+ const comparableToVersionId = active !== null && active.schemaVersion === 2 && plansAreLabelOnlyVariants(parseV2Plan(active), compiled.plan) ? active.id : null;
17772
17855
  tx.insert(measurementPlanVersions).values({
17773
17856
  id: versionId,
17774
17857
  projectId: gate.project.id,
@@ -17777,6 +17860,7 @@ async function measurementDraftRoutes(app, opts = {}) {
17777
17860
  checksum: sha256Hex(canonicalJson3),
17778
17861
  schemaVersion: 2,
17779
17862
  compiledChecksum: compiled.plan.compiledChecksum,
17863
+ comparableToVersionId,
17780
17864
  publishedBy: serializeActor(gate.actor),
17781
17865
  sourceDraftId: row.id,
17782
17866
  createdAt: now.toISOString()
@@ -18942,7 +19026,7 @@ function selectDisplayedRun(db, projectId, active, query) {
18942
19026
  }
18943
19027
  const run = db.select().from(runs).where(and12(eq18(runs.projectId, projectId), eq18(runs.id, selectedRunId))).get();
18944
19028
  if (!run) throw notFound("Run", selectedRunId);
18945
- if (run.measurementPlanVersionId === active.version.id) return run;
19029
+ if (runVersionServesActiveVersion(db, projectId, active.version.id, run.measurementPlanVersionId)) return run;
18946
19030
  const pinned = run.measurementPlanVersionId === null ? null : db.select({ revision: measurementPlanVersions.revision }).from(measurementPlanVersions).where(eq18(measurementPlanVersions.id, run.measurementPlanVersionId)).get()?.revision ?? null;
18947
19031
  throw runRevisionMismatch(run.id, pinned, active.version.revision);
18948
19032
  }
@@ -19283,7 +19367,7 @@ function selectDisplayedRun2(db, projectId, active, runId) {
19283
19367
  }
19284
19368
  const run = db.select().from(runs).where(and13(eq19(runs.projectId, projectId), eq19(runs.id, runId))).get();
19285
19369
  if (!run) throw notFound("Run", runId);
19286
- if (run.measurementPlanVersionId === active.version.id) return run;
19370
+ if (runVersionServesActiveVersion(db, projectId, active.version.id, run.measurementPlanVersionId)) return run;
19287
19371
  const pinned = run.measurementPlanVersionId === null ? null : db.select({ revision: measurementPlanVersions.revision }).from(measurementPlanVersions).where(eq19(measurementPlanVersions.id, run.measurementPlanVersionId)).get()?.revision ?? null;
19288
19372
  throw runRevisionMismatch(run.id, pinned, active.version.revision);
19289
19373
  }
@@ -19482,7 +19566,7 @@ function selectMeasurementQuestionRun(db, projectId, active, runId) {
19482
19566
  eq20(runs.projectId, projectId)
19483
19567
  )).get();
19484
19568
  if (!run) throw notFound("Run", runId);
19485
- if (run.measurementPlanVersionId !== active.version.id) {
19569
+ if (!runVersionServesActiveVersion(db, projectId, active.version.id, run.measurementPlanVersionId)) {
19486
19570
  const pinned = run.measurementPlanVersionId === null ? null : db.select({ revision: measurementPlanVersions.revision }).from(measurementPlanVersions).where(eq20(measurementPlanVersions.id, run.measurementPlanVersionId)).get()?.revision ?? null;
19487
19571
  throw runRevisionMismatch(run.id, pinned, active.version.revision);
19488
19572
  }
@@ -20174,7 +20258,11 @@ function previousComparableRun(db, active, current) {
20174
20258
  measurementExecutionIdentity: runs.measurementExecutionIdentity
20175
20259
  }).from(runs).where(and15(
20176
20260
  eq21(runs.projectId, current.projectId),
20177
- eq21(runs.measurementPlanVersionId, active.version.id),
20261
+ // The comparable chain keeps period-over-period alive across a label-only
20262
+ // republish: a run pinned to a comparable prior revision measured exactly
20263
+ // the questions the active revision asks. The execution-identity and scope
20264
+ // checks below still gate what actually compares.
20265
+ inArray7(runs.measurementPlanVersionId, comparableMeasurementVersionIds(db, current.projectId, active.version.id)),
20178
20266
  eq21(runs.kind, RunKinds["answer-visibility"]),
20179
20267
  inArray7(runs.status, [RunStatuses.completed, RunStatuses.partial]),
20180
20268
  or3(
@@ -30849,8 +30937,8 @@ var routeCatalog = [
30849
30937
  {
30850
30938
  method: "put",
30851
30939
  path: "/api/v1/projects/{name}/measurement-plan",
30852
- summary: "Publish a measurement-plan revision",
30853
- description: "Compares the caller-observed active revision, then validates and canonicalizes the plan against current project domains, locations, and tracked queries. Identical active content is idempotent; restoring older content creates a new immutable revision.",
30940
+ summary: "Publish a measurement-plan revision (legacy schema v1)",
30941
+ description: "Legacy schema-v1 publish. Compares the caller-observed active revision, then validates and canonicalizes the plan against current project domains, locations, and tracked queries. Identical active content is idempotent; restoring older content creates a new immutable revision. Refuses when the active revision is schema v2 rather than downgrade it; publish v2 plans through the draft flow (POST .../measurement-plan/draft/actions/publish).",
30854
30942
  tags: ["measurement-plans"],
30855
30943
  parameters: [nameParameter],
30856
30944
  requestBody: {
@@ -16409,6 +16409,7 @@ export {
16409
16409
  compileQueryClassifier,
16410
16410
  MEASUREMENT_PLAN_V2_SCHEMA_VERSION,
16411
16411
  measurementPlanV2Schema,
16412
+ canonicalMeasurementPlanV2,
16412
16413
  canonicalMeasurementPlanV2Json,
16413
16414
  measurementPlanV2ChecksumJson,
16414
16415
  MEASUREMENT_PAGE_DEFAULT_LIMIT,
@@ -11,7 +11,7 @@ import {
11
11
  loadConfig,
12
12
  loadConfigRaw,
13
13
  saveConfigPatch
14
- } from "./chunk-UTW6QOGP.js";
14
+ } from "./chunk-2UKCBT7D.js";
15
15
  import {
16
16
  CC_CACHE_DIR,
17
17
  DUCKDB_SPEC,
@@ -157,7 +157,7 @@ import {
157
157
  siteCrawlSnapshots,
158
158
  toAlertView,
159
159
  usageCounters
160
- } from "./chunk-JKKHGDDC.js";
160
+ } from "./chunk-2ZWW62T3.js";
161
161
  import {
162
162
  AGENT_MEMORY_VALUE_MAX_BYTES,
163
163
  AGENT_PROVIDER_IDS,
@@ -296,7 +296,7 @@ import {
296
296
  validationError,
297
297
  winnabilityClassLabel,
298
298
  withRetry
299
- } from "./chunk-OYDIND7Q.js";
299
+ } from "./chunk-BNYDRPKC.js";
300
300
 
301
301
  // src/telemetry.ts
302
302
  import crypto from "crypto";
@@ -11896,7 +11896,7 @@ function readStoredGroundingSources(rawResponse) {
11896
11896
  return result;
11897
11897
  }
11898
11898
  async function backfillInsightsCommand(project, opts) {
11899
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-YSRPTRKP.js");
11899
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-LSQGK34A.js");
11900
11900
  const config = loadConfig();
11901
11901
  const db = createClient(config.database);
11902
11902
  migrate(db);
@@ -5,11 +5,11 @@ import {
5
5
  installSkills,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-UTW6QOGP.js";
8
+ } from "./chunk-2UKCBT7D.js";
9
9
  import {
10
10
  SKILL_MANIFEST_FILENAME,
11
11
  SkillsClients
12
- } from "./chunk-OYDIND7Q.js";
12
+ } from "./chunk-BNYDRPKC.js";
13
13
 
14
14
  // src/skills-autosync.ts
15
15
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -26,11 +26,11 @@ import {
26
26
  showFirstRunNotice,
27
27
  trackCliCommandFinished,
28
28
  trackEvent
29
- } from "./chunk-WINVGL5B.js";
29
+ } from "./chunk-GHO4574A.js";
30
30
  import {
31
31
  autoSyncSkills,
32
32
  formatAutoSyncNotice
33
- } from "./chunk-VIVLYBFX.js";
33
+ } from "./chunk-XLVNLY2I.js";
34
34
  import {
35
35
  CliError,
36
36
  EXIT_SYSTEM_ERROR,
@@ -55,7 +55,7 @@ import {
55
55
  saveConfigPatch,
56
56
  systemError,
57
57
  usageError
58
- } from "./chunk-UTW6QOGP.js";
58
+ } from "./chunk-2UKCBT7D.js";
59
59
  import {
60
60
  CLOUDFLARE_WORKER_BINDINGS,
61
61
  CLOUDFLARE_WORKER_GENERATED_MARKER,
@@ -69,7 +69,7 @@ import {
69
69
  projects,
70
70
  queries,
71
71
  renderReportHtml
72
- } from "./chunk-JKKHGDDC.js";
72
+ } from "./chunk-2ZWW62T3.js";
73
73
  import {
74
74
  AdsDeliverySnapshotStatuses,
75
75
  AdsHistoricalCampaignRollupStatuses,
@@ -138,7 +138,7 @@ import {
138
138
  providerQuotaPolicySchema,
139
139
  resolveProviderInput,
140
140
  winnabilityClassSchema
141
- } from "./chunk-OYDIND7Q.js";
141
+ } from "./chunk-BNYDRPKC.js";
142
142
 
143
143
  // src/cli.ts
144
144
  import { pathToFileURL } from "url";
@@ -18537,7 +18537,7 @@ var MEASUREMENT_PLAN_CLI_COMMANDS = [
18537
18537
  await showMeasurementPlan(requireProject(input, "measurement-plan.show", "canonry measurement-plan show <project> [--revision N]"), revision);
18538
18538
  } },
18539
18539
  { path: ["measurement-plan", "versions"], usage: "canonry measurement-plan versions <project> [--format json]", run: (input) => listMeasurementPlanVersions(requireProject(input, "measurement-plan.versions", "canonry measurement-plan versions <project>")) },
18540
- { path: ["measurement-plan", "publish"], usage: "canonry measurement-plan publish <project> <yaml|json|-> [--format json]", run: (input) => {
18540
+ { path: ["measurement-plan", "publish"], usage: "canonry measurement-plan publish <project> <yaml|json|-> [--format json] (legacy schema v1 only; refuses over an active v2 plan; for Advanced Measurement use: canonry measurement-plan advanced <project> draft-action)", run: (input) => {
18541
18541
  const project = requireProject(input, "measurement-plan.publish", "canonry measurement-plan publish <project> <yaml|json|->");
18542
18542
  const source = input.positionals[1];
18543
18543
  if (!source) throw usageError("plan file path or - is required");
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-WINVGL5B.js";
6
+ } from "./chunk-GHO4574A.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-UTW6QOGP.js";
10
- import "./chunk-JKKHGDDC.js";
11
- import "./chunk-OYDIND7Q.js";
9
+ } from "./chunk-2UKCBT7D.js";
10
+ import "./chunk-2ZWW62T3.js";
11
+ import "./chunk-BNYDRPKC.js";
12
12
  export {
13
13
  GoogleMarketingRuntimeError,
14
14
  createGoogleMarketingCredentialStore,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-JKKHGDDC.js";
4
- import "./chunk-OYDIND7Q.js";
3
+ } from "./chunk-2ZWW62T3.js";
4
+ import "./chunk-BNYDRPKC.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  autoSyncSkills
3
- } from "./chunk-VIVLYBFX.js";
3
+ } from "./chunk-XLVNLY2I.js";
4
4
  import {
5
5
  createApiClient,
6
6
  createCanonryMcpServer
7
- } from "./chunk-UTW6QOGP.js";
7
+ } from "./chunk-2UKCBT7D.js";
8
8
  import {
9
9
  isReadOnlyKey
10
- } from "./chunk-OYDIND7Q.js";
10
+ } from "./chunk-BNYDRPKC.js";
11
11
 
12
12
  // src/mcp/cli.ts
13
13
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.178.1",
3
+ "version": "4.178.3",
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
  "license": "FSL-1.1-ALv2",
@@ -70,28 +70,28 @@
70
70
  "@types/node-cron": "^3.0.11",
71
71
  "tsup": "^8.5.1",
72
72
  "tsx": "^4.19.0",
73
- "@ainyc/canonry-api-client": "0.0.0",
74
- "@ainyc/canonry-api-routes": "0.0.0",
75
73
  "@ainyc/canonry-config": "0.0.0",
76
- "@ainyc/canonry-contracts": "0.0.0",
74
+ "@ainyc/canonry-api-routes": "0.0.0",
77
75
  "@ainyc/canonry-db": "0.0.0",
78
- "@ainyc/canonry-integration-bing": "0.0.0",
79
- "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
76
+ "@ainyc/canonry-contracts": "0.0.0",
77
+ "@ainyc/canonry-api-client": "0.0.0",
80
78
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
79
+ "@ainyc/canonry-integration-bing": "0.0.0",
81
80
  "@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
82
- "@ainyc/canonry-integration-google": "0.0.0",
83
81
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
84
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
82
+ "@ainyc/canonry-integration-google": "0.0.0",
83
+ "@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
85
84
  "@ainyc/canonry-integration-google-ads": "0.0.0",
86
85
  "@ainyc/canonry-integration-google-places": "0.0.0",
87
- "@ainyc/canonry-integration-openai-ads": "0.0.0",
88
86
  "@ainyc/canonry-integration-google-tag-manager": "0.0.0",
87
+ "@ainyc/canonry-integration-openai-ads": "0.0.0",
88
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
89
89
  "@ainyc/canonry-integration-traffic": "0.0.0",
90
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
90
91
  "@ainyc/canonry-intelligence": "0.0.0",
91
- "@ainyc/canonry-provider-claude": "0.0.0",
92
92
  "@ainyc/canonry-provider-cdp": "0.0.0",
93
93
  "@ainyc/canonry-provider-gemini": "0.0.0",
94
- "@ainyc/canonry-integration-wordpress": "0.0.0",
94
+ "@ainyc/canonry-provider-claude": "0.0.0",
95
95
  "@ainyc/canonry-provider-local": "0.0.0",
96
96
  "@ainyc/canonry-provider-openai": "0.0.0",
97
97
  "@ainyc/canonry-provider-perplexity": "0.0.0"