@canonry/canonry 4.170.0 → 4.171.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.
package/dist/cli.js CHANGED
@@ -26,11 +26,11 @@ import {
26
26
  showFirstRunNotice,
27
27
  trackCliCommandFinished,
28
28
  trackEvent
29
- } from "./chunk-NMCLLN5X.js";
29
+ } from "./chunk-5KGN224W.js";
30
30
  import {
31
31
  autoSyncSkills,
32
32
  formatAutoSyncNotice
33
- } from "./chunk-HTUI6BSK.js";
33
+ } from "./chunk-RR4KSDOD.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-BMW3AURU.js";
58
+ } from "./chunk-IIFDS3K2.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-CCZNCH7Y.js";
72
+ } from "./chunk-O226BLC7.js";
73
73
  import {
74
74
  AdsDeliverySnapshotStatuses,
75
75
  AdsHistoricalCampaignRollupStatuses,
@@ -104,6 +104,9 @@ import {
104
104
  adsPauseRequestSchema,
105
105
  backlinkSourceSchema,
106
106
  buildModelChangeNotice,
107
+ canonicalizeGtmAccountId,
108
+ canonicalizeGtmResourceSelection,
109
+ conversionTrackingContractWriteRequestSchema,
107
110
  describeError,
108
111
  discoveryBucketSchema,
109
112
  discoveryCompetitorTypeSchema,
@@ -3880,9 +3883,9 @@ async function gaConnect(project, opts) {
3880
3883
  propertyId: opts.propertyId
3881
3884
  };
3882
3885
  if (opts.keyFile) {
3883
- const fs19 = await import("fs");
3886
+ const fs20 = await import("fs");
3884
3887
  try {
3885
- const content = fs19.readFileSync(opts.keyFile, "utf-8");
3888
+ const content = fs20.readFileSync(opts.keyFile, "utf-8");
3886
3889
  JSON.parse(content);
3887
3890
  body.keyJson = content;
3888
3891
  } catch (e) {
@@ -5233,6 +5236,581 @@ var GBP_CLI_COMMANDS = [
5233
5236
  }
5234
5237
  ];
5235
5238
 
5239
+ // src/commands/google-marketing.ts
5240
+ import fs4 from "fs";
5241
+ function printMachine(value, format) {
5242
+ if (!isMachineFormat(format)) return false;
5243
+ console.log(JSON.stringify(value, null, 2));
5244
+ return true;
5245
+ }
5246
+ function connectionSummary(status) {
5247
+ if (!status.connected) return "not connected";
5248
+ if (status.status === "selection-required") return "connected; selection required";
5249
+ return status.status === "stale" ? "connected; stored evidence is stale" : "connected";
5250
+ }
5251
+ function snapshotSummary(snapshot) {
5252
+ return `${snapshot.capturedAt} ${snapshot.kind.padEnd(22)} ${snapshot.id} ${snapshot.payloadChecksum.slice(0, 12)}`;
5253
+ }
5254
+ function canonicalGtmReadAccountId(accountId) {
5255
+ const canonical = canonicalizeGtmAccountId(accountId);
5256
+ if (canonical) return canonical;
5257
+ throw new CliError({
5258
+ code: "GTM_RESOURCE_INPUT_INVALID",
5259
+ message: "GTM account must be a safe bare ID or an accounts/{id} resource path.",
5260
+ displayMessage: "Error: GTM account must be a safe bare ID or an accounts/{id} resource path."
5261
+ });
5262
+ }
5263
+ function canonicalGtmReadSelection(accountId, containerId) {
5264
+ const canonical = canonicalizeGtmResourceSelection({ accountId, containerId });
5265
+ if (canonical) return canonical;
5266
+ throw new CliError({
5267
+ code: "GTM_RESOURCE_INPUT_INVALID",
5268
+ message: "GTM account and container must be matching safe IDs or resource paths.",
5269
+ displayMessage: "Error: GTM account and container must be matching safe IDs or resource paths."
5270
+ });
5271
+ }
5272
+ function readConversionTrackingContractInput(inputPath) {
5273
+ if (!inputPath) {
5274
+ throw new CliError({
5275
+ code: "CONVERSION_TRACKING_INPUT_REQUIRED",
5276
+ message: "A conversion-tracking contract JSON input file is required",
5277
+ displayMessage: "Error: --input <json-file> is required (use --input - for stdin)"
5278
+ });
5279
+ }
5280
+ try {
5281
+ const raw = fs4.readFileSync(inputPath === "-" ? 0 : inputPath, "utf8");
5282
+ return conversionTrackingContractWriteRequestSchema.parse(JSON.parse(raw));
5283
+ } catch (err) {
5284
+ const message = describeError(err);
5285
+ throw new CliError({
5286
+ code: "CONVERSION_TRACKING_INPUT_INVALID",
5287
+ message,
5288
+ displayMessage: `Error: invalid conversion-tracking contract input (${message})`,
5289
+ details: { inputPath }
5290
+ });
5291
+ }
5292
+ }
5293
+ async function googleAdsDisconnect(client, project, opts) {
5294
+ const result = await client.disconnectGoogleAds(project);
5295
+ if (printMachine(result, opts.format)) return;
5296
+ console.log(result.disconnected ? `Disconnected Google Ads from project "${project}". Stored redacted evidence was retained.` : `Google Ads was not connected to project "${project}".`);
5297
+ }
5298
+ async function gtmDisconnect(client, project, opts) {
5299
+ const result = await client.disconnectGtm(project);
5300
+ if (printMachine(result, opts.format)) return;
5301
+ console.log(result.disconnected ? `Disconnected Google Tag Manager from project "${project}". Stored redacted evidence was retained.` : `Google Tag Manager was not connected to project "${project}".`);
5302
+ }
5303
+ async function googleAdsStatus(client, project, opts) {
5304
+ const result = await client.getGoogleAdsStatus(project);
5305
+ if (printMachine(result, opts.format)) return;
5306
+ console.log(`Google Ads: ${connectionSummary(result)}`);
5307
+ if (!result.connected) {
5308
+ console.log(`Open project "${project}" in the Canonry dashboard to authorize Google Ads.`);
5309
+ return;
5310
+ }
5311
+ const selection = result.connection.selection;
5312
+ console.log(`Customer: ${selection.customerId ?? "not selected"}`);
5313
+ if (selection.loginCustomerId) console.log(`Login customer: ${selection.loginCustomerId}`);
5314
+ if (result.selectedCustomer) {
5315
+ console.log(`Selected: ${result.selectedCustomer.descriptiveName ?? result.selectedCustomer.customerId} (${result.selectedCustomer.status})`);
5316
+ }
5317
+ console.log(`Last inventory snapshot: ${result.connection.lastInventorySnapshotAt ?? "none"}`);
5318
+ console.log(`Last metrics snapshot: ${result.connection.lastMetricsSnapshotAt ?? "none"}`);
5319
+ }
5320
+ async function gtmStatus(client, project, opts) {
5321
+ const result = await client.getGtmStatus(project);
5322
+ if (printMachine(result, opts.format)) return;
5323
+ console.log(`Google Tag Manager: ${connectionSummary(result)}`);
5324
+ if (!result.connected) {
5325
+ console.log(`Open project "${project}" in the Canonry dashboard to authorize Google Tag Manager.`);
5326
+ return;
5327
+ }
5328
+ console.log(`Account: ${result.selection.accountId ?? "not selected"}`);
5329
+ console.log(`Container: ${result.selection.containerId ?? "not selected"}`);
5330
+ console.log(`Workspace: ${result.selection.workspaceId ?? "none (live only)"}`);
5331
+ console.log(`Last snapshot: ${result.connection.lastSnapshotAt ?? "none"}`);
5332
+ }
5333
+ async function googleAdsCustomers(client, project, opts) {
5334
+ const result = await client.listGoogleAdsCustomers(project);
5335
+ if (opts.format === "jsonl") {
5336
+ emitJsonl(result.customers.map((customer) => ({ project, fetchedAt: result.fetchedAt, ...customer })));
5337
+ return;
5338
+ }
5339
+ if (printMachine(result, opts.format)) return;
5340
+ if (result.customers.length === 0) {
5341
+ console.log("No Google Ads customers are visible to this OAuth connection.");
5342
+ return;
5343
+ }
5344
+ console.log(`${result.totalAccessible} accessible customer(s)${result.truncated ? " (bounded list; truncated)" : ""}:
5345
+ `);
5346
+ for (const customer of result.customers) {
5347
+ const role = customer.manager ? "manager" : "client";
5348
+ const name = customer.descriptiveName ?? "(unnamed)";
5349
+ console.log(` ${customer.customerId.padEnd(16)} ${name} ${role}, ${customer.status}`);
5350
+ }
5351
+ console.log(`
5352
+ Select one with: canonry google-ads select ${project} --customer <customer-id>`);
5353
+ }
5354
+ async function googleAdsSelect(client, project, request, opts) {
5355
+ const result = await client.setGoogleAdsSelection(project, request);
5356
+ if (printMachine(result, opts.format)) return;
5357
+ console.log(`Selected Google Ads customer ${request.customerId}${request.loginCustomerId ? ` through login customer ${request.loginCustomerId}` : ""}.`);
5358
+ console.log(`Run \`canonry google-ads sync ${project}\` to capture read-only conversion and goal evidence.`);
5359
+ }
5360
+ async function googleAdsSync(client, project, opts) {
5361
+ const run = await client.triggerGoogleAdsSync(project);
5362
+ if (printMachine(run, opts.format)) return;
5363
+ console.log(`Google Ads read-only sync queued (run ${run.id}). Use \`canonry run show ${run.id}\` to check it.`);
5364
+ }
5365
+ async function googleAdsSnapshots(client, project, opts) {
5366
+ const result = await client.listGoogleAdsSnapshots(project, { limit: opts.limit, cursor: opts.cursor });
5367
+ if (opts.format === "jsonl") {
5368
+ emitJsonl(result.snapshots.map((snapshot) => ({ project, ...snapshot })));
5369
+ return;
5370
+ }
5371
+ if (printMachine(result, opts.format)) return;
5372
+ if (result.snapshots.length === 0) {
5373
+ console.log(`No stored Google Ads snapshots. Run \`canonry google-ads sync ${project}\` after selecting a customer.`);
5374
+ return;
5375
+ }
5376
+ console.log(`${result.total} stored Google Ads snapshot(s):
5377
+ `);
5378
+ for (const snapshot of result.snapshots) console.log(` ${snapshotSummary(snapshot)}`);
5379
+ if (result.nextCursor) console.log(`
5380
+ Next cursor: ${result.nextCursor}`);
5381
+ }
5382
+ async function googleAdsSnapshot(client, project, snapshotId, opts) {
5383
+ const result = await client.getGoogleAdsSnapshot(project, snapshotId);
5384
+ if (printMachine(result, opts.format)) return;
5385
+ const { metadata, payload } = result.snapshot;
5386
+ console.log(`Google Ads ${metadata.kind} snapshot ${metadata.id}`);
5387
+ console.log(`Captured: ${metadata.capturedAt}`);
5388
+ console.log(`Checksum: ${metadata.payloadChecksum}`);
5389
+ console.log(`Payload kind: ${payload.kind}`);
5390
+ }
5391
+ async function gtmAccounts(client, project, opts) {
5392
+ const result = await client.listGtmAccounts(project);
5393
+ if (opts.format === "jsonl") {
5394
+ emitJsonl(result.accounts.map((account) => ({ project, fetchedAt: result.fetchedAt, ...account })));
5395
+ return;
5396
+ }
5397
+ if (printMachine(result, opts.format)) return;
5398
+ if (result.accounts.length === 0) {
5399
+ console.log("No GTM accounts are visible to this OAuth connection.");
5400
+ return;
5401
+ }
5402
+ console.log(`${result.totalAccessible} accessible GTM account(s)${result.truncated ? " (bounded list; truncated)" : ""}:
5403
+ `);
5404
+ for (const account of result.accounts) console.log(` ${account.id.padEnd(16)} ${account.name}`);
5405
+ console.log(`
5406
+ Discover containers with: canonry gtm containers ${project} --account <account-id>`);
5407
+ }
5408
+ async function gtmContainers(client, project, accountId, opts) {
5409
+ const canonicalAccountId = canonicalGtmReadAccountId(accountId);
5410
+ const result = await client.listGtmContainers(project, canonicalAccountId);
5411
+ if (opts.format === "jsonl") {
5412
+ emitJsonl(result.containers.map((container) => ({ project, fetchedAt: result.fetchedAt, ...container })));
5413
+ return;
5414
+ }
5415
+ if (printMachine(result, opts.format)) return;
5416
+ if (result.containers.length === 0) {
5417
+ console.log(`No GTM containers are visible under account ${accountId}.`);
5418
+ return;
5419
+ }
5420
+ console.log(`${result.totalAccessible} GTM container(s)${result.truncated ? " (bounded list; truncated)" : ""}:
5421
+ `);
5422
+ for (const container of result.containers) {
5423
+ const publicId = container.publicId ? ` (${container.publicId})` : "";
5424
+ console.log(` ${container.id.padEnd(16)} ${container.name}${publicId}`);
5425
+ }
5426
+ console.log(`
5427
+ Discover workspaces with: canonry gtm workspaces ${project} --account ${accountId} --container <container-id>`);
5428
+ }
5429
+ async function gtmWorkspaces(client, project, accountId, containerId, opts) {
5430
+ const selection = canonicalGtmReadSelection(accountId, containerId);
5431
+ const result = await client.listGtmWorkspaces(project, selection.accountId, selection.containerId);
5432
+ if (opts.format === "jsonl") {
5433
+ emitJsonl(result.workspaces.map((workspace) => ({ project, fetchedAt: result.fetchedAt, ...workspace })));
5434
+ return;
5435
+ }
5436
+ if (printMachine(result, opts.format)) return;
5437
+ if (result.workspaces.length === 0) {
5438
+ console.log(`No GTM workspaces are visible for container ${containerId}. A sync can still capture the live container graph.`);
5439
+ return;
5440
+ }
5441
+ console.log(`${result.totalAccessible} GTM workspace(s)${result.truncated ? " (bounded list; truncated)" : ""}:
5442
+ `);
5443
+ for (const workspace of result.workspaces) console.log(` ${workspace.id.padEnd(16)} ${workspace.name}`);
5444
+ }
5445
+ async function gtmSelect(client, project, request, opts) {
5446
+ const result = await client.setGtmSelection(project, request);
5447
+ if (printMachine(result, opts.format)) return;
5448
+ console.log(`Selected GTM account ${request.accountId}, container ${request.containerId}${request.workspaceId ? `, and workspace ${request.workspaceId}` : ""}.`);
5449
+ console.log(`Run \`canonry gtm sync ${project}\` to capture redacted live${request.workspaceId ? " and draft" : ""} evidence.`);
5450
+ }
5451
+ async function gtmSync(client, project, opts) {
5452
+ const run = await client.triggerGtmSync(project);
5453
+ if (printMachine(run, opts.format)) return;
5454
+ console.log(`GTM read-only sync queued (run ${run.id}). Use \`canonry run show ${run.id}\` to check it.`);
5455
+ }
5456
+ async function gtmSnapshots(client, project, opts) {
5457
+ const result = await client.listGtmSnapshots(project, { limit: opts.limit, cursor: opts.cursor });
5458
+ if (opts.format === "jsonl") {
5459
+ emitJsonl(result.snapshots.map((snapshot) => ({ project, ...snapshot })));
5460
+ return;
5461
+ }
5462
+ if (printMachine(result, opts.format)) return;
5463
+ if (result.snapshots.length === 0) {
5464
+ console.log(`No stored GTM snapshots. Run \`canonry gtm sync ${project}\` after selecting a container.`);
5465
+ return;
5466
+ }
5467
+ console.log(`${result.total} stored GTM snapshot(s):
5468
+ `);
5469
+ for (const snapshot of result.snapshots) console.log(` ${snapshotSummary(snapshot)}`);
5470
+ if (result.nextCursor) console.log(`
5471
+ Next cursor: ${result.nextCursor}`);
5472
+ }
5473
+ async function gtmSnapshot(client, project, snapshotId, opts) {
5474
+ const result = await client.getGtmSnapshot(project, snapshotId);
5475
+ if (printMachine(result, opts.format)) return;
5476
+ const { metadata, payload } = result.snapshot;
5477
+ console.log(`GTM ${metadata.kind} snapshot ${metadata.id}`);
5478
+ console.log(`Captured: ${metadata.capturedAt}`);
5479
+ console.log(`Checksum: ${metadata.payloadChecksum}`);
5480
+ console.log(`Payload kind: ${payload.kind}`);
5481
+ }
5482
+ async function conversionTrackingContracts(client, project, opts) {
5483
+ const contracts = await client.listConversionTrackingContracts(project);
5484
+ if (opts.format === "jsonl") {
5485
+ emitJsonl(contracts.map((contract) => ({ project, ...contract })));
5486
+ return;
5487
+ }
5488
+ if (printMachine(contracts, opts.format)) return;
5489
+ if (contracts.length === 0) {
5490
+ console.log("No conversion-tracking contracts are declared for this project.");
5491
+ return;
5492
+ }
5493
+ console.log(`${contracts.length} conversion-tracking contract(s):
5494
+ `);
5495
+ for (const contract of contracts) {
5496
+ console.log(` ${contract.id.padEnd(16)} ${contract.name} event=${contract.eventName}`);
5497
+ }
5498
+ }
5499
+ async function conversionTrackingContract(client, project, contractId, opts) {
5500
+ const contract = await client.getConversionTrackingContract(project, contractId);
5501
+ if (printMachine(contract, opts.format)) return;
5502
+ printConversionTrackingContract(contract);
5503
+ }
5504
+ function printConversionTrackingContract(contract) {
5505
+ console.log(`Conversion contract: ${contract.name} (${contract.id})`);
5506
+ console.log(`Event: ${contract.eventName}`);
5507
+ console.log(`Google Ads: customer ${contract.googleAds.customerId}, conversion action ${contract.googleAds.conversionActionId}`);
5508
+ console.log(`GTM: account ${contract.gtm.accountId}, container ${contract.gtm.containerId}, tag ${contract.gtm.tagId}`);
5509
+ console.log(`Runtime verification: ${contract.runtime.verificationRequired ? "required" : "not required"}`);
5510
+ }
5511
+ async function conversionTrackingCreate(client, project, request, opts) {
5512
+ const contract = await client.createConversionTrackingContract(project, request);
5513
+ if (printMachine(contract, opts.format)) return;
5514
+ console.log(`Created conversion-tracking contract "${contract.name}" (${contract.id}).`);
5515
+ }
5516
+ async function conversionTrackingUpdate(client, project, contractId, request, opts) {
5517
+ const contract = await client.updateConversionTrackingContract(project, contractId, request);
5518
+ if (printMachine(contract, opts.format)) return;
5519
+ console.log(`Updated conversion-tracking contract "${contract.name}" (${contract.id}).`);
5520
+ }
5521
+ async function conversionTrackingDelete(client, project, contractId, opts) {
5522
+ await client.deleteConversionTrackingContract(project, contractId);
5523
+ if (printMachine({ project, contractId, deleted: true }, opts.format)) return;
5524
+ console.log(`Deleted conversion-tracking contract ${contractId}.`);
5525
+ }
5526
+ async function conversionTrackingIntegrity(client, project, contractId, opts) {
5527
+ const result = await client.getConversionTrackingIntegrity(project, contractId);
5528
+ if (opts.format === "jsonl") {
5529
+ emitJsonl(result.assessment.findings.map((finding) => ({
5530
+ project,
5531
+ contractId: result.assessment.contract.id,
5532
+ integrityStatus: result.assessment.status,
5533
+ evaluatedAt: result.assessment.evaluatedAt,
5534
+ ...finding
5535
+ })));
5536
+ return;
5537
+ }
5538
+ if (printMachine(result, opts.format)) return;
5539
+ console.log(`Conversion integrity: ${result.assessment.contract.name} \u2014 ${result.assessment.status}`);
5540
+ console.log(`Evaluated: ${result.assessment.evaluatedAt}`);
5541
+ for (const finding of result.assessment.findings) {
5542
+ console.log(` ${finding.outcome.toUpperCase().padEnd(7)} ${finding.code} \u2014 ${finding.subject}`);
5543
+ }
5544
+ if (result.assessment.status === "runtime-unverified") {
5545
+ console.log("\nStatic configuration is consistent, but a GTM API snapshot cannot prove that the website event fired or Google Ads observed a conversion.");
5546
+ }
5547
+ }
5548
+
5549
+ // src/cli-commands/google-marketing.ts
5550
+ function snapshotLimit(input, command, usage) {
5551
+ const limit = parseIntegerOption(input, "limit", {
5552
+ command,
5553
+ usage,
5554
+ message: "--limit must be an integer from 1 to 100"
5555
+ });
5556
+ if (limit !== void 0 && (limit < 1 || limit > 100)) {
5557
+ throw usageError(`Error: --limit must be an integer from 1 to 100
5558
+ Usage: ${usage}`, {
5559
+ message: "--limit must be an integer from 1 to 100",
5560
+ details: { command, usage, option: "limit", value: limit }
5561
+ });
5562
+ }
5563
+ return limit;
5564
+ }
5565
+ function createGoogleMarketingCliCommands(createClient2) {
5566
+ return [
5567
+ {
5568
+ path: ["google-ads", "disconnect"],
5569
+ usage: "canonry google-ads disconnect <project> [--format json]",
5570
+ run: async (input) => {
5571
+ const usage = "canonry google-ads disconnect <project> [--format json]";
5572
+ await googleAdsDisconnect(createClient2(), requireProject(input, "google-ads.disconnect", usage), { format: input.format });
5573
+ }
5574
+ },
5575
+ {
5576
+ path: ["google-ads", "status"],
5577
+ usage: "canonry google-ads status <project> [--format json]",
5578
+ run: async (input) => {
5579
+ const usage = "canonry google-ads status <project> [--format json]";
5580
+ await googleAdsStatus(createClient2(), requireProject(input, "google-ads.status", usage), { format: input.format });
5581
+ }
5582
+ },
5583
+ {
5584
+ path: ["google-ads", "customers"],
5585
+ usage: "canonry google-ads customers <project> [--format json|jsonl]",
5586
+ run: async (input) => {
5587
+ const usage = "canonry google-ads customers <project> [--format json|jsonl]";
5588
+ await googleAdsCustomers(createClient2(), requireProject(input, "google-ads.customers", usage), { format: input.format });
5589
+ }
5590
+ },
5591
+ {
5592
+ path: ["google-ads", "select"],
5593
+ usage: "canonry google-ads select <project> --customer <customer-id> [--login-customer <manager-id>] [--format json]",
5594
+ options: { customer: stringOption(), "login-customer": stringOption() },
5595
+ run: async (input) => {
5596
+ const usage = "canonry google-ads select <project> --customer <customer-id> [--login-customer <manager-id>] [--format json]";
5597
+ const project = requireProject(input, "google-ads.select", usage);
5598
+ const customerId = requireStringOption(input, "customer", {
5599
+ command: "google-ads.select",
5600
+ usage,
5601
+ message: "--customer is required"
5602
+ });
5603
+ await googleAdsSelect(createClient2(), project, {
5604
+ customerId,
5605
+ ...getString(input.values, "login-customer") ? { loginCustomerId: getString(input.values, "login-customer") } : {}
5606
+ }, { format: input.format });
5607
+ }
5608
+ },
5609
+ {
5610
+ path: ["google-ads", "sync"],
5611
+ usage: "canonry google-ads sync <project> [--format json]",
5612
+ run: async (input) => {
5613
+ const usage = "canonry google-ads sync <project> [--format json]";
5614
+ await googleAdsSync(createClient2(), requireProject(input, "google-ads.sync", usage), { format: input.format });
5615
+ }
5616
+ },
5617
+ {
5618
+ path: ["google-ads", "snapshots"],
5619
+ usage: "canonry google-ads snapshots <project> [--limit <n>] [--cursor <opaque>] [--format json|jsonl]",
5620
+ options: { limit: stringOption(), cursor: stringOption() },
5621
+ run: async (input) => {
5622
+ const usage = "canonry google-ads snapshots <project> [--limit <n>] [--cursor <opaque>] [--format json|jsonl]";
5623
+ await googleAdsSnapshots(createClient2(), requireProject(input, "google-ads.snapshots", usage), {
5624
+ limit: snapshotLimit(input, "google-ads.snapshots", usage),
5625
+ cursor: getString(input.values, "cursor"),
5626
+ format: input.format
5627
+ });
5628
+ }
5629
+ },
5630
+ {
5631
+ path: ["google-ads", "snapshot"],
5632
+ usage: "canonry google-ads snapshot <project> <snapshot-id> [--format json]",
5633
+ run: async (input) => {
5634
+ const usage = "canonry google-ads snapshot <project> <snapshot-id> [--format json]";
5635
+ const project = requireProject(input, "google-ads.snapshot", usage);
5636
+ await googleAdsSnapshot(createClient2(), project, requirePositional(input, 1, {
5637
+ command: "google-ads.snapshot",
5638
+ usage,
5639
+ message: "snapshot id is required"
5640
+ }), { format: input.format });
5641
+ }
5642
+ },
5643
+ {
5644
+ path: ["gtm", "disconnect"],
5645
+ usage: "canonry gtm disconnect <project> [--format json]",
5646
+ run: async (input) => {
5647
+ const usage = "canonry gtm disconnect <project> [--format json]";
5648
+ await gtmDisconnect(createClient2(), requireProject(input, "gtm.disconnect", usage), { format: input.format });
5649
+ }
5650
+ },
5651
+ {
5652
+ path: ["gtm", "status"],
5653
+ usage: "canonry gtm status <project> [--format json]",
5654
+ run: async (input) => {
5655
+ const usage = "canonry gtm status <project> [--format json]";
5656
+ await gtmStatus(createClient2(), requireProject(input, "gtm.status", usage), { format: input.format });
5657
+ }
5658
+ },
5659
+ {
5660
+ path: ["gtm", "accounts"],
5661
+ usage: "canonry gtm accounts <project> [--format json|jsonl]",
5662
+ run: async (input) => {
5663
+ const usage = "canonry gtm accounts <project> [--format json|jsonl]";
5664
+ await gtmAccounts(createClient2(), requireProject(input, "gtm.accounts", usage), { format: input.format });
5665
+ }
5666
+ },
5667
+ {
5668
+ path: ["gtm", "containers"],
5669
+ usage: "canonry gtm containers <project> --account <account-id> [--format json|jsonl]",
5670
+ options: { account: stringOption() },
5671
+ run: async (input) => {
5672
+ const usage = "canonry gtm containers <project> --account <account-id> [--format json|jsonl]";
5673
+ const project = requireProject(input, "gtm.containers", usage);
5674
+ await gtmContainers(createClient2(), project, requireStringOption(input, "account", {
5675
+ command: "gtm.containers",
5676
+ usage,
5677
+ message: "--account is required"
5678
+ }), { format: input.format });
5679
+ }
5680
+ },
5681
+ {
5682
+ path: ["gtm", "workspaces"],
5683
+ usage: "canonry gtm workspaces <project> --account <account-id> --container <container-id> [--format json|jsonl]",
5684
+ options: { account: stringOption(), container: stringOption() },
5685
+ run: async (input) => {
5686
+ const usage = "canonry gtm workspaces <project> --account <account-id> --container <container-id> [--format json|jsonl]";
5687
+ const project = requireProject(input, "gtm.workspaces", usage);
5688
+ const accountId = requireStringOption(input, "account", { command: "gtm.workspaces", usage, message: "--account is required" });
5689
+ const containerId = requireStringOption(input, "container", { command: "gtm.workspaces", usage, message: "--container is required" });
5690
+ await gtmWorkspaces(createClient2(), project, accountId, containerId, { format: input.format });
5691
+ }
5692
+ },
5693
+ {
5694
+ path: ["gtm", "select"],
5695
+ usage: "canonry gtm select <project> --account <account-id> --container <container-id> [--workspace <workspace-id>] [--format json]",
5696
+ options: { account: stringOption(), container: stringOption(), workspace: stringOption() },
5697
+ run: async (input) => {
5698
+ const usage = "canonry gtm select <project> --account <account-id> --container <container-id> [--workspace <workspace-id>] [--format json]";
5699
+ const project = requireProject(input, "gtm.select", usage);
5700
+ const accountId = requireStringOption(input, "account", { command: "gtm.select", usage, message: "--account is required" });
5701
+ const containerId = requireStringOption(input, "container", { command: "gtm.select", usage, message: "--container is required" });
5702
+ const workspaceId = getString(input.values, "workspace");
5703
+ await gtmSelect(createClient2(), project, {
5704
+ accountId,
5705
+ containerId,
5706
+ ...workspaceId ? { workspaceId } : {}
5707
+ }, { format: input.format });
5708
+ }
5709
+ },
5710
+ {
5711
+ path: ["gtm", "sync"],
5712
+ usage: "canonry gtm sync <project> [--format json]",
5713
+ run: async (input) => {
5714
+ const usage = "canonry gtm sync <project> [--format json]";
5715
+ await gtmSync(createClient2(), requireProject(input, "gtm.sync", usage), { format: input.format });
5716
+ }
5717
+ },
5718
+ {
5719
+ path: ["gtm", "snapshots"],
5720
+ usage: "canonry gtm snapshots <project> [--limit <n>] [--cursor <opaque>] [--format json|jsonl]",
5721
+ options: { limit: stringOption(), cursor: stringOption() },
5722
+ run: async (input) => {
5723
+ const usage = "canonry gtm snapshots <project> [--limit <n>] [--cursor <opaque>] [--format json|jsonl]";
5724
+ await gtmSnapshots(createClient2(), requireProject(input, "gtm.snapshots", usage), {
5725
+ limit: snapshotLimit(input, "gtm.snapshots", usage),
5726
+ cursor: getString(input.values, "cursor"),
5727
+ format: input.format
5728
+ });
5729
+ }
5730
+ },
5731
+ {
5732
+ path: ["gtm", "snapshot"],
5733
+ usage: "canonry gtm snapshot <project> <snapshot-id> [--format json]",
5734
+ run: async (input) => {
5735
+ const usage = "canonry gtm snapshot <project> <snapshot-id> [--format json]";
5736
+ const project = requireProject(input, "gtm.snapshot", usage);
5737
+ await gtmSnapshot(createClient2(), project, requirePositional(input, 1, {
5738
+ command: "gtm.snapshot",
5739
+ usage,
5740
+ message: "snapshot id is required"
5741
+ }), { format: input.format });
5742
+ }
5743
+ },
5744
+ {
5745
+ path: ["conversion-tracking", "contracts"],
5746
+ usage: "canonry conversion-tracking contracts <project> [--format json|jsonl]",
5747
+ run: async (input) => {
5748
+ const usage = "canonry conversion-tracking contracts <project> [--format json|jsonl]";
5749
+ await conversionTrackingContracts(createClient2(), requireProject(input, "conversion-tracking.contracts", usage), { format: input.format });
5750
+ }
5751
+ },
5752
+ {
5753
+ path: ["conversion-tracking", "contracts", "get"],
5754
+ usage: "canonry conversion-tracking contracts get <project> <contract-id> [--format json]",
5755
+ run: async (input) => {
5756
+ const usage = "canonry conversion-tracking contracts get <project> <contract-id> [--format json]";
5757
+ const project = requireProject(input, "conversion-tracking.contracts.get", usage);
5758
+ await conversionTrackingContract(createClient2(), project, requirePositional(input, 1, {
5759
+ command: "conversion-tracking.contracts.get",
5760
+ usage,
5761
+ message: "contract id is required"
5762
+ }), { format: input.format });
5763
+ }
5764
+ },
5765
+ {
5766
+ path: ["conversion-tracking", "contracts", "create"],
5767
+ usage: "canonry conversion-tracking contracts create <project> --input <json-file|-> [--format json]",
5768
+ options: { input: stringOption() },
5769
+ run: async (input) => {
5770
+ const usage = "canonry conversion-tracking contracts create <project> --input <json-file|-> [--format json]";
5771
+ await conversionTrackingCreate(createClient2(), requireProject(input, "conversion-tracking.contracts.create", usage), readConversionTrackingContractInput(getString(input.values, "input")), { format: input.format });
5772
+ }
5773
+ },
5774
+ {
5775
+ path: ["conversion-tracking", "contracts", "update"],
5776
+ usage: "canonry conversion-tracking contracts update <project> <contract-id> --input <json-file|-> [--format json]",
5777
+ options: { input: stringOption() },
5778
+ run: async (input) => {
5779
+ const usage = "canonry conversion-tracking contracts update <project> <contract-id> --input <json-file|-> [--format json]";
5780
+ const project = requireProject(input, "conversion-tracking.contracts.update", usage);
5781
+ const contractId = requirePositional(input, 1, { command: "conversion-tracking.contracts.update", usage, message: "contract id is required" });
5782
+ await conversionTrackingUpdate(createClient2(), project, contractId, readConversionTrackingContractInput(getString(input.values, "input")), { format: input.format });
5783
+ }
5784
+ },
5785
+ {
5786
+ path: ["conversion-tracking", "contracts", "delete"],
5787
+ usage: "canonry conversion-tracking contracts delete <project> <contract-id> [--format json]",
5788
+ run: async (input) => {
5789
+ const usage = "canonry conversion-tracking contracts delete <project> <contract-id> [--format json]";
5790
+ const project = requireProject(input, "conversion-tracking.contracts.delete", usage);
5791
+ await conversionTrackingDelete(createClient2(), project, requirePositional(input, 1, {
5792
+ command: "conversion-tracking.contracts.delete",
5793
+ usage,
5794
+ message: "contract id is required"
5795
+ }), { format: input.format });
5796
+ }
5797
+ },
5798
+ {
5799
+ path: ["conversion-tracking", "contracts", "integrity"],
5800
+ usage: "canonry conversion-tracking contracts integrity <project> <contract-id> [--format json|jsonl]",
5801
+ run: async (input) => {
5802
+ const usage = "canonry conversion-tracking contracts integrity <project> <contract-id> [--format json|jsonl]";
5803
+ const project = requireProject(input, "conversion-tracking.contracts.integrity", usage);
5804
+ await conversionTrackingIntegrity(createClient2(), project, requirePositional(input, 1, {
5805
+ command: "conversion-tracking.contracts.integrity",
5806
+ usage,
5807
+ message: "contract id is required"
5808
+ }), { format: input.format });
5809
+ }
5810
+ }
5811
+ ];
5812
+ }
5813
+
5236
5814
  // src/commands/get.ts
5237
5815
  var SOURCE_FETCHERS = {
5238
5816
  overview: async (project) => createApiClient().getProjectOverview(project),
@@ -5336,12 +5914,12 @@ var GET_CLI_COMMANDS = [
5336
5914
  ];
5337
5915
 
5338
5916
  // src/commands/traffic.ts
5339
- import fs5 from "fs";
5917
+ import fs6 from "fs";
5340
5918
 
5341
5919
  // src/cloudflare-worker-deploy.ts
5342
5920
  import { spawn } from "child_process";
5343
5921
  import { randomUUID } from "crypto";
5344
- import fs4 from "fs";
5922
+ import fs5 from "fs";
5345
5923
  import os from "os";
5346
5924
  import path2 from "path";
5347
5925
  import { stripVTControlCharacters } from "util";
@@ -5359,7 +5937,7 @@ function projectSlug(project) {
5359
5937
  }
5360
5938
  function lstatIfPresent(filePath) {
5361
5939
  try {
5362
- return fs4.lstatSync(filePath);
5940
+ return fs5.lstatSync(filePath);
5363
5941
  } catch (error) {
5364
5942
  if (error.code === "ENOENT") return null;
5365
5943
  throw error;
@@ -5377,7 +5955,7 @@ function prepareCloudflareWorkerOutputDirectory(outputDirectory) {
5377
5955
  throw new Error(`output directory is not a regular directory: ${resolved}`);
5378
5956
  }
5379
5957
  } else {
5380
- fs4.mkdirSync(resolved, { recursive: true, mode: 448 });
5958
+ fs5.mkdirSync(resolved, { recursive: true, mode: 448 });
5381
5959
  }
5382
5960
  const artifacts = {
5383
5961
  outputDirectory: resolved,
@@ -5390,7 +5968,7 @@ function prepareCloudflareWorkerOutputDirectory(outputDirectory) {
5390
5968
  if (!stat.isFile() || stat.isSymbolicLink()) {
5391
5969
  throw new Error(`artifact path is not a regular file: ${artifactPath}`);
5392
5970
  }
5393
- const contents = fs4.readFileSync(artifactPath, "utf-8");
5971
+ const contents = fs5.readFileSync(artifactPath, "utf-8");
5394
5972
  if (!isRecognizablyCanonryGenerated(artifactPath, contents)) {
5395
5973
  throw new Error(`refusing to overwrite an operator-owned artifact: ${artifactPath}`);
5396
5974
  }
@@ -5403,7 +5981,7 @@ function readRegularArtifact(filePath) {
5403
5981
  if (!stat.isFile() || stat.isSymbolicLink()) {
5404
5982
  throw new Error(`artifact path is not a regular file: ${filePath}`);
5405
5983
  }
5406
- return fs4.readFileSync(filePath, "utf-8");
5984
+ return fs5.readFileSync(filePath, "utf-8");
5407
5985
  }
5408
5986
  function isRecognizablyCanonryGenerated(filePath, contents) {
5409
5987
  if (path2.basename(filePath) === "worker.js") {
@@ -5420,22 +5998,22 @@ function writePrivateTemporaryFile(filePath, contents) {
5420
5998
  path2.dirname(filePath),
5421
5999
  `.${path2.basename(filePath)}.${randomUUID()}.tmp`
5422
6000
  );
5423
- const fd = fs4.openSync(temporaryPath, "wx", 384);
6001
+ const fd = fs5.openSync(temporaryPath, "wx", 384);
5424
6002
  let closed = false;
5425
6003
  try {
5426
- fs4.writeFileSync(fd, contents, "utf-8");
5427
- fs4.fsyncSync(fd);
5428
- fs4.closeSync(fd);
6004
+ fs5.writeFileSync(fd, contents, "utf-8");
6005
+ fs5.fsyncSync(fd);
6006
+ fs5.closeSync(fd);
5429
6007
  closed = true;
5430
6008
  } catch (error) {
5431
6009
  if (!closed) {
5432
6010
  try {
5433
- fs4.closeSync(fd);
6011
+ fs5.closeSync(fd);
5434
6012
  } catch {
5435
6013
  }
5436
6014
  }
5437
6015
  try {
5438
- fs4.unlinkSync(temporaryPath);
6016
+ fs5.unlinkSync(temporaryPath);
5439
6017
  } catch {
5440
6018
  }
5441
6019
  throw error;
@@ -5469,10 +6047,10 @@ function writeCloudflareWorkerArtifacts(artifacts, contents) {
5469
6047
  throw new Error(`artifact changed during update: ${change.filePath}`);
5470
6048
  }
5471
6049
  if (change.previous === null) {
5472
- fs4.linkSync(change.temporaryPath, change.filePath);
5473
- fs4.unlinkSync(change.temporaryPath);
6050
+ fs5.linkSync(change.temporaryPath, change.filePath);
6051
+ fs5.unlinkSync(change.temporaryPath);
5474
6052
  } else {
5475
- fs4.renameSync(change.temporaryPath, change.filePath);
6053
+ fs5.renameSync(change.temporaryPath, change.filePath);
5476
6054
  }
5477
6055
  applied.push(change);
5478
6056
  }
@@ -5481,10 +6059,10 @@ function writeCloudflareWorkerArtifacts(artifacts, contents) {
5481
6059
  try {
5482
6060
  if (readRegularArtifact(change.filePath) !== change.contents) continue;
5483
6061
  if (change.previous === null) {
5484
- fs4.unlinkSync(change.filePath);
6062
+ fs5.unlinkSync(change.filePath);
5485
6063
  } else {
5486
6064
  const restorePath = writePrivateTemporaryFile(change.filePath, change.previous);
5487
- fs4.renameSync(restorePath, change.filePath);
6065
+ fs5.renameSync(restorePath, change.filePath);
5488
6066
  }
5489
6067
  } catch {
5490
6068
  }
@@ -5493,7 +6071,7 @@ function writeCloudflareWorkerArtifacts(artifacts, contents) {
5493
6071
  } finally {
5494
6072
  for (const change of pending) {
5495
6073
  try {
5496
- if (fs4.existsSync(change.temporaryPath)) fs4.unlinkSync(change.temporaryPath);
6074
+ if (fs5.existsSync(change.temporaryPath)) fs5.unlinkSync(change.temporaryPath);
5497
6075
  } catch {
5498
6076
  }
5499
6077
  }
@@ -5579,7 +6157,7 @@ async function preflightCloudflareWrangler(opts = {}) {
5579
6157
  if (missing.length > 0) {
5580
6158
  throw new Error(`Wrangler deploy does not support required flags: ${missing.join(", ")}`);
5581
6159
  }
5582
- const tempDirectory = fs4.mkdtempSync(path2.join(
6160
+ const tempDirectory = fs5.mkdtempSync(path2.join(
5583
6161
  opts.tempRoot ?? os.tmpdir(),
5584
6162
  "canonry-cloudflare-wrangler-preflight-"
5585
6163
  ));
@@ -5587,13 +6165,13 @@ async function preflightCloudflareWrangler(opts = {}) {
5587
6165
  const wranglerTomlPath = path2.join(tempDirectory, "wrangler.toml");
5588
6166
  const secretsPath = path2.join(tempDirectory, "secrets.json");
5589
6167
  try {
5590
- fs4.chmodSync(tempDirectory, 448);
5591
- fs4.writeFileSync(workerScriptPath, generateWorkerScript({
6168
+ fs5.chmodSync(tempDirectory, 448);
6169
+ fs5.writeFileSync(workerScriptPath, generateWorkerScript({
5592
6170
  deliveryMode,
5593
6171
  workerVersion: "preflight",
5594
6172
  botList: DEFAULT_BOT_LIST
5595
6173
  }), { encoding: "utf-8", flag: "wx", mode: 384 });
5596
- fs4.writeFileSync(wranglerTomlPath, generateWranglerToml(
6174
+ fs5.writeFileSync(wranglerTomlPath, generateWranglerToml(
5597
6175
  deliveryMode === "queue-pull" ? {
5598
6176
  deliveryMode,
5599
6177
  sourceId: "preflight",
@@ -5609,7 +6187,7 @@ async function preflightCloudflareWrangler(opts = {}) {
5609
6187
  }
5610
6188
  ), { encoding: "utf-8", flag: "wx", mode: 384 });
5611
6189
  if (deliveryMode === "direct-push") {
5612
- fs4.writeFileSync(secretsPath, JSON.stringify({
6190
+ fs5.writeFileSync(secretsPath, JSON.stringify({
5613
6191
  [CLOUDFLARE_WORKER_BINDINGS.bearerToken]: "preflight-bearer",
5614
6192
  [CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: "preflight-hmac"
5615
6193
  }), { encoding: "utf-8", flag: "wx", mode: 384 });
@@ -5637,7 +6215,7 @@ ${wranglerDiagnostic(validationOutput)}`
5637
6215
  );
5638
6216
  }
5639
6217
  } finally {
5640
- fs4.rmSync(tempDirectory, { recursive: true, force: true });
6218
+ fs5.rmSync(tempDirectory, { recursive: true, force: true });
5641
6219
  }
5642
6220
  }
5643
6221
  async function deployCloudflareWorker(opts) {
@@ -5653,11 +6231,11 @@ async function deployCloudflareWorker(opts) {
5653
6231
  }
5654
6232
  if (!opts.secrets) throw new Error("Direct-push Worker deploy requires bearer and HMAC secrets");
5655
6233
  const secrets = opts.secrets;
5656
- const tempDirectory = fs4.mkdtempSync(path2.join(opts.tempRoot ?? os.tmpdir(), "canonry-cloudflare-secrets-"));
6234
+ const tempDirectory = fs5.mkdtempSync(path2.join(opts.tempRoot ?? os.tmpdir(), "canonry-cloudflare-secrets-"));
5657
6235
  const secretsPath = path2.join(tempDirectory, "secrets.json");
5658
6236
  try {
5659
- fs4.chmodSync(tempDirectory, 448);
5660
- fs4.writeFileSync(
6237
+ fs5.chmodSync(tempDirectory, 448);
6238
+ fs5.writeFileSync(
5661
6239
  secretsPath,
5662
6240
  JSON.stringify({
5663
6241
  [CLOUDFLARE_WORKER_BINDINGS.bearerToken]: secrets.bearerToken,
@@ -5665,7 +6243,7 @@ async function deployCloudflareWorker(opts) {
5665
6243
  }),
5666
6244
  { encoding: "utf-8", flag: "wx", mode: 384 }
5667
6245
  );
5668
- fs4.chmodSync(secretsPath, 384);
6246
+ fs5.chmodSync(secretsPath, 384);
5669
6247
  const runner = opts.run ?? runWrangler;
5670
6248
  await runner(
5671
6249
  "wrangler",
@@ -5681,9 +6259,9 @@ async function deployCloudflareWorker(opts) {
5681
6259
  );
5682
6260
  } finally {
5683
6261
  try {
5684
- if (fs4.existsSync(secretsPath)) fs4.unlinkSync(secretsPath);
6262
+ if (fs5.existsSync(secretsPath)) fs5.unlinkSync(secretsPath);
5685
6263
  } finally {
5686
- fs4.rmSync(tempDirectory, { recursive: true, force: true });
6264
+ fs5.rmSync(tempDirectory, { recursive: true, force: true });
5687
6265
  }
5688
6266
  }
5689
6267
  }
@@ -5713,11 +6291,11 @@ function requireCloudflareQueueOption(project, value, option) {
5713
6291
  }
5714
6292
  function readCloudflareQueueApiToken(project, tokenFile) {
5715
6293
  try {
5716
- const stat = fs5.lstatSync(tokenFile);
6294
+ const stat = fs6.lstatSync(tokenFile);
5717
6295
  if (!stat.isFile() || stat.isSymbolicLink()) {
5718
6296
  throw new Error("not a regular file");
5719
6297
  }
5720
- const apiToken = fs5.readFileSync(tokenFile, "utf-8").trim();
6298
+ const apiToken = fs6.readFileSync(tokenFile, "utf-8").trim();
5721
6299
  if (!apiToken) throw new Error("file is empty");
5722
6300
  return apiToken;
5723
6301
  } catch (error) {
@@ -5999,9 +6577,9 @@ async function trafficConnectWordpress(project, opts) {
5999
6577
  }
6000
6578
  let applicationPassword = opts.appPassword?.trim() ?? "";
6001
6579
  if (!applicationPassword && opts.appPasswordFile) {
6002
- const fs19 = await import("fs");
6580
+ const fs20 = await import("fs");
6003
6581
  try {
6004
- applicationPassword = fs19.readFileSync(opts.appPasswordFile, "utf-8").trim();
6582
+ applicationPassword = fs20.readFileSync(opts.appPasswordFile, "utf-8").trim();
6005
6583
  } catch (e) {
6006
6584
  const msg = describeError(e);
6007
6585
  throw new CliError({
@@ -6057,10 +6635,10 @@ async function trafficConnectCloudRun(project, opts) {
6057
6635
  details: { project }
6058
6636
  });
6059
6637
  }
6060
- const fs19 = await import("fs");
6638
+ const fs20 = await import("fs");
6061
6639
  let keyJson;
6062
6640
  try {
6063
- keyJson = fs19.readFileSync(opts.serviceAccountKey, "utf-8");
6641
+ keyJson = fs20.readFileSync(opts.serviceAccountKey, "utf-8");
6064
6642
  JSON.parse(keyJson);
6065
6643
  } catch (e) {
6066
6644
  const msg = describeError(e);
@@ -6120,9 +6698,9 @@ async function trafficConnectVercel(project, opts) {
6120
6698
  }
6121
6699
  let token = opts.token?.trim() ?? "";
6122
6700
  if (!token && opts.tokenFile) {
6123
- const fs19 = await import("fs");
6701
+ const fs20 = await import("fs");
6124
6702
  try {
6125
- token = fs19.readFileSync(opts.tokenFile, "utf-8").trim();
6703
+ token = fs20.readFileSync(opts.tokenFile, "utf-8").trim();
6126
6704
  } catch (e) {
6127
6705
  const msg = describeError(e);
6128
6706
  throw new CliError({
@@ -8570,7 +9148,7 @@ var USERS_CLI_COMMANDS = [
8570
9148
  ];
8571
9149
 
8572
9150
  // src/commands/keyword.ts
8573
- import fs6 from "fs";
9151
+ import fs7 from "fs";
8574
9152
  function getClient14() {
8575
9153
  return createApiClient();
8576
9154
  }
@@ -8638,7 +9216,7 @@ async function listKeywords(project, format) {
8638
9216
  }
8639
9217
  }
8640
9218
  async function importKeywords(project, filePath, format) {
8641
- if (!fs6.existsSync(filePath)) {
9219
+ if (!fs7.existsSync(filePath)) {
8642
9220
  throw new CliError({
8643
9221
  code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
8644
9222
  message: `File not found: ${filePath}`,
@@ -8649,7 +9227,7 @@ async function importKeywords(project, filePath, format) {
8649
9227
  }
8650
9228
  });
8651
9229
  }
8652
- const content = fs6.readFileSync(filePath, "utf-8");
9230
+ const content = fs7.readFileSync(filePath, "utf-8");
8653
9231
  const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
8654
9232
  if (keywords.length === 0) {
8655
9233
  if (isMachineFormat(format)) {
@@ -8850,7 +9428,7 @@ var KEYWORD_CLI_COMMANDS = [
8850
9428
  ];
8851
9429
 
8852
9430
  // src/commands/query.ts
8853
- import fs7 from "fs";
9431
+ import fs8 from "fs";
8854
9432
  function getClient15() {
8855
9433
  return createApiClient();
8856
9434
  }
@@ -8941,7 +9519,7 @@ async function listQueries(project, format) {
8941
9519
  }
8942
9520
  }
8943
9521
  async function importQueries(project, filePath, format) {
8944
- if (!fs7.existsSync(filePath)) {
9522
+ if (!fs8.existsSync(filePath)) {
8945
9523
  throw new CliError({
8946
9524
  code: "QUERY_IMPORT_FILE_NOT_FOUND",
8947
9525
  message: `File not found: ${filePath}`,
@@ -8952,7 +9530,7 @@ async function importQueries(project, filePath, format) {
8952
9530
  }
8953
9531
  });
8954
9532
  }
8955
- const content = fs7.readFileSync(filePath, "utf-8");
9533
+ const content = fs8.readFileSync(filePath, "utf-8");
8956
9534
  const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
8957
9535
  if (queries2.length === 0) {
8958
9536
  if (isMachineFormat(format)) {
@@ -9154,7 +9732,7 @@ var QUERY_CLI_COMMANDS = [
9154
9732
  ];
9155
9733
 
9156
9734
  // src/commands/mcp.ts
9157
- import fs8 from "fs";
9735
+ import fs9 from "fs";
9158
9736
  import path4 from "path";
9159
9737
  import { createRequire } from "module";
9160
9738
 
@@ -9270,8 +9848,8 @@ function renderClientSnippet(client, serverName, entry) {
9270
9848
  return renderJsonSnippet(serverName, entry, client.format);
9271
9849
  }
9272
9850
  function readJsonConfig(configPath) {
9273
- if (!fs8.existsSync(configPath)) return {};
9274
- const raw = fs8.readFileSync(configPath, "utf-8").trim();
9851
+ if (!fs9.existsSync(configPath)) return {};
9852
+ const raw = fs9.readFileSync(configPath, "utf-8").trim();
9275
9853
  if (!raw) return {};
9276
9854
  try {
9277
9855
  const parsed = JSON.parse(raw);
@@ -9289,14 +9867,14 @@ function readJsonConfig(configPath) {
9289
9867
  }
9290
9868
  }
9291
9869
  function writeJsonConfig(configPath, value) {
9292
- fs8.mkdirSync(path4.dirname(configPath), { recursive: true });
9293
- fs8.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
9870
+ fs9.mkdirSync(path4.dirname(configPath), { recursive: true });
9871
+ fs9.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
9294
9872
  `, "utf-8");
9295
9873
  }
9296
9874
  function backupConfigIfPresent(configPath) {
9297
- if (!fs8.existsSync(configPath)) return void 0;
9875
+ if (!fs9.existsSync(configPath)) return void 0;
9298
9876
  const backupPath = `${configPath}.canonry.bak`;
9299
- fs8.copyFileSync(configPath, backupPath);
9877
+ fs9.copyFileSync(configPath, backupPath);
9300
9878
  return backupPath;
9301
9879
  }
9302
9880
  function findClientOrThrow(id) {
@@ -9652,13 +10230,13 @@ var NOTIFY_CLI_COMMANDS = [
9652
10230
  ];
9653
10231
 
9654
10232
  // src/commands/apply.ts
9655
- import fs9 from "fs";
10233
+ import fs10 from "fs";
9656
10234
  import { parseAllDocuments } from "yaml";
9657
10235
  async function applyConfigFile(filePath) {
9658
- if (!fs9.existsSync(filePath)) {
10236
+ if (!fs10.existsSync(filePath)) {
9659
10237
  throw new Error(`File not found: ${filePath}`);
9660
10238
  }
9661
- const content = fs9.readFileSync(filePath, "utf-8");
10239
+ const content = fs10.readFileSync(filePath, "utf-8");
9662
10240
  const docs = parseAllDocuments(content);
9663
10241
  const client = createApiClient();
9664
10242
  const errors = [];
@@ -10061,7 +10639,7 @@ async function loadLatestRunForExport(client, project) {
10061
10639
  }
10062
10640
 
10063
10641
  // src/commands/results-export.ts
10064
- import fs10 from "fs";
10642
+ import fs11 from "fs";
10065
10643
  import path5 from "path";
10066
10644
  async function exportResults(project, opts) {
10067
10645
  const { output, ...request } = opts;
@@ -10071,8 +10649,8 @@ async function exportResults(project, opts) {
10071
10649
  return;
10072
10650
  }
10073
10651
  const target = output ? path5.resolve(output) : path5.resolve(process.cwd(), path5.basename(artifact.filename));
10074
- fs10.mkdirSync(path5.dirname(target), { recursive: true });
10075
- fs10.writeFileSync(target, artifact.content, "utf8");
10652
+ fs11.mkdirSync(path5.dirname(target), { recursive: true });
10653
+ fs11.writeFileSync(target, artifact.content, "utf8");
10076
10654
  console.log(`Results export written to ${target}`);
10077
10655
  }
10078
10656
 
@@ -10788,7 +11366,7 @@ function parseProviderModelAssignments(assignments) {
10788
11366
  }
10789
11367
 
10790
11368
  // src/commands/report.ts
10791
- import fs11 from "fs";
11369
+ import fs12 from "fs";
10792
11370
  import path6 from "path";
10793
11371
  function defaultOutputPath(project, audience) {
10794
11372
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -10805,10 +11383,10 @@ async function runReportCommand(project, opts = {}) {
10805
11383
  const html = renderReportHtml(report, { audience });
10806
11384
  const targetPath = opts.output ? path6.resolve(opts.output) : defaultOutputPath(project, audience);
10807
11385
  const dir = path6.dirname(targetPath);
10808
- if (!fs11.existsSync(dir)) {
10809
- fs11.mkdirSync(dir, { recursive: true });
11386
+ if (!fs12.existsSync(dir)) {
11387
+ fs12.mkdirSync(dir, { recursive: true });
10810
11388
  }
10811
- fs11.writeFileSync(targetPath, html, "utf-8");
11389
+ fs12.writeFileSync(targetPath, html, "utf-8");
10812
11390
  console.log(`Report written to ${targetPath}`);
10813
11391
  }
10814
11392
 
@@ -11783,11 +12361,11 @@ var SKILLS_CLI_COMMANDS = [
11783
12361
  ];
11784
12362
 
11785
12363
  // src/commands/snapshot.ts
11786
- import fs13 from "fs";
12364
+ import fs14 from "fs";
11787
12365
  import path8 from "path";
11788
12366
 
11789
12367
  // src/snapshot-pdf.ts
11790
- import fs12 from "fs";
12368
+ import fs13 from "fs";
11791
12369
  import path7 from "path";
11792
12370
  import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
11793
12371
  var PAGE_WIDTH = 612;
@@ -12001,8 +12579,8 @@ async function writeSnapshotPdf(report, outputPath) {
12001
12579
  renderQueries(pdf, report);
12002
12580
  const bytes = await doc.save();
12003
12581
  const resolvedPath = path7.resolve(outputPath);
12004
- fs12.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
12005
- fs12.writeFileSync(resolvedPath, bytes);
12582
+ fs13.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
12583
+ fs13.writeFileSync(resolvedPath, bytes);
12006
12584
  return resolvedPath;
12007
12585
  }
12008
12586
  function renderCover(pdf, report) {
@@ -12161,8 +12739,8 @@ PDF saved: ${savedPdfPath}`);
12161
12739
  }
12162
12740
  function writeSnapshotMarkdown(report, outputPath) {
12163
12741
  const resolvedPath = path8.resolve(outputPath);
12164
- fs13.mkdirSync(path8.dirname(resolvedPath), { recursive: true });
12165
- fs13.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
12742
+ fs14.mkdirSync(path8.dirname(resolvedPath), { recursive: true });
12743
+ fs14.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
12166
12744
  return resolvedPath;
12167
12745
  }
12168
12746
  function formatSnapshotMarkdown(report) {
@@ -13390,7 +13968,7 @@ async function bootstrapCommand(_opts) {
13390
13968
 
13391
13969
  // src/commands/daemon.ts
13392
13970
  import { spawn as spawn2 } from "child_process";
13393
- import fs14 from "fs";
13971
+ import fs15 from "fs";
13394
13972
  import path10 from "path";
13395
13973
  function getPidPath() {
13396
13974
  return path10.join(getConfigDir(), "canonry.pid");
@@ -13433,8 +14011,8 @@ function buildServeForwardArgs(opts) {
13433
14011
  async function startDaemon(opts) {
13434
14012
  const pidPath = getPidPath();
13435
14013
  const format = opts.format ?? "text";
13436
- if (fs14.existsSync(pidPath)) {
13437
- const existingPid = parseInt(fs14.readFileSync(pidPath, "utf-8").trim(), 10);
14014
+ if (fs15.existsSync(pidPath)) {
14015
+ const existingPid = parseInt(fs15.readFileSync(pidPath, "utf-8").trim(), 10);
13438
14016
  if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
13439
14017
  throw new CliError({
13440
14018
  code: "DAEMON_ALREADY_RUNNING",
@@ -13445,7 +14023,7 @@ async function startDaemon(opts) {
13445
14023
  }
13446
14024
  });
13447
14025
  }
13448
- fs14.unlinkSync(pidPath);
14026
+ fs15.unlinkSync(pidPath);
13449
14027
  }
13450
14028
  const cliPath = path10.resolve(new URL(import.meta.url).pathname);
13451
14029
  const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
@@ -13464,10 +14042,10 @@ async function startDaemon(opts) {
13464
14042
  });
13465
14043
  }
13466
14044
  const configDir = getConfigDir();
13467
- if (!fs14.existsSync(configDir)) {
13468
- fs14.mkdirSync(configDir, { recursive: true });
14045
+ if (!fs15.existsSync(configDir)) {
14046
+ fs15.mkdirSync(configDir, { recursive: true });
13469
14047
  }
13470
- fs14.writeFileSync(pidPath, String(child.pid), "utf-8");
14048
+ fs15.writeFileSync(pidPath, String(child.pid), "utf-8");
13471
14049
  const port = opts.port ?? "4100";
13472
14050
  const host = opts.host ?? "127.0.0.1";
13473
14051
  if (!isMachineFormat(format)) {
@@ -13476,7 +14054,7 @@ async function startDaemon(opts) {
13476
14054
  const ready = await waitForReady(host, port);
13477
14055
  if (!ready) {
13478
14056
  try {
13479
- fs14.unlinkSync(pidPath);
14057
+ fs15.unlinkSync(pidPath);
13480
14058
  } catch {
13481
14059
  }
13482
14060
  throw new CliError({
@@ -13508,7 +14086,7 @@ async function startDaemon(opts) {
13508
14086
  }
13509
14087
  function stopDaemon(format = "text") {
13510
14088
  const pidPath = getPidPath();
13511
- if (!fs14.existsSync(pidPath)) {
14089
+ if (!fs15.existsSync(pidPath)) {
13512
14090
  if (isMachineFormat(format)) {
13513
14091
  console.log(JSON.stringify({
13514
14092
  stopped: false,
@@ -13519,7 +14097,7 @@ function stopDaemon(format = "text") {
13519
14097
  console.log("Canonry is not running (no PID file found)");
13520
14098
  return;
13521
14099
  }
13522
- const pid = parseInt(fs14.readFileSync(pidPath, "utf-8").trim(), 10);
14100
+ const pid = parseInt(fs15.readFileSync(pidPath, "utf-8").trim(), 10);
13523
14101
  if (isNaN(pid)) {
13524
14102
  if (isMachineFormat(format)) {
13525
14103
  console.log(JSON.stringify({
@@ -13530,7 +14108,7 @@ function stopDaemon(format = "text") {
13530
14108
  } else {
13531
14109
  console.error("Invalid PID file. Removing it.");
13532
14110
  }
13533
- fs14.unlinkSync(pidPath);
14111
+ fs15.unlinkSync(pidPath);
13534
14112
  return;
13535
14113
  }
13536
14114
  if (!isProcessAlive(pid)) {
@@ -13544,12 +14122,12 @@ function stopDaemon(format = "text") {
13544
14122
  } else {
13545
14123
  console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
13546
14124
  }
13547
- fs14.unlinkSync(pidPath);
14125
+ fs15.unlinkSync(pidPath);
13548
14126
  return;
13549
14127
  }
13550
14128
  try {
13551
14129
  process.kill(pid, "SIGTERM");
13552
- fs14.unlinkSync(pidPath);
14130
+ fs15.unlinkSync(pidPath);
13553
14131
  if (isMachineFormat(format)) {
13554
14132
  console.log(JSON.stringify({
13555
14133
  stopped: true,
@@ -13573,7 +14151,7 @@ function stopDaemon(format = "text") {
13573
14151
 
13574
14152
  // src/commands/init.ts
13575
14153
  import crypto2 from "crypto";
13576
- import fs15 from "fs";
14154
+ import fs16 from "fs";
13577
14155
  import readline2 from "readline";
13578
14156
  import path11 from "path";
13579
14157
  var pendingServeHandoff = false;
@@ -13611,7 +14189,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
13611
14189
  function cwdLooksLikeProject(dir) {
13612
14190
  const home = process.env.HOME ?? "";
13613
14191
  if (home && path11.resolve(dir) === path11.resolve(home)) return false;
13614
- return PROJECT_MARKERS.some((marker) => fs15.existsSync(path11.join(dir, marker)));
14192
+ return PROJECT_MARKERS.some((marker) => fs16.existsSync(path11.join(dir, marker)));
13615
14193
  }
13616
14194
  var DEFAULT_AGENT_MODELS = {
13617
14195
  anthropic: "anthropic/claude-sonnet-4-6",
@@ -13642,8 +14220,8 @@ async function initCommand(opts) {
13642
14220
  return void 0;
13643
14221
  }
13644
14222
  const configDir = getConfigDir();
13645
- if (!fs15.existsSync(configDir)) {
13646
- fs15.mkdirSync(configDir, { recursive: true });
14223
+ if (!fs16.existsSync(configDir)) {
14224
+ fs16.mkdirSync(configDir, { recursive: true });
13647
14225
  }
13648
14226
  const bootstrapEnv = getBootstrapEnv(process.env, {
13649
14227
  GEMINI_API_KEY: opts?.geminiKey,
@@ -13944,7 +14522,7 @@ function encodeLegacySetupState(state) {
13944
14522
  }
13945
14523
 
13946
14524
  // src/agent-plugin.ts
13947
- import fs16 from "fs";
14525
+ import fs17 from "fs";
13948
14526
  import path12 from "path";
13949
14527
  var CANONRY_PLUGIN_ID = "canonry@canonry";
13950
14528
  var REQUIRED_SKILLS = ["canonry", "aero"];
@@ -13989,7 +14567,7 @@ function isCanonryPluginId(value) {
13989
14567
  }
13990
14568
  function readJson(filePath) {
13991
14569
  try {
13992
- return JSON.parse(fs16.readFileSync(filePath, "utf8"));
14570
+ return JSON.parse(fs17.readFileSync(filePath, "utf8"));
13993
14571
  } catch {
13994
14572
  return null;
13995
14573
  }
@@ -14008,7 +14586,7 @@ function readCodexPluginConfigured(filePath) {
14008
14586
  try {
14009
14587
  let currentPlugin = null;
14010
14588
  let inPluginsTable = false;
14011
- for (const line of fs16.readFileSync(filePath, "utf8").split(/\r?\n/)) {
14589
+ for (const line of fs17.readFileSync(filePath, "utf8").split(/\r?\n/)) {
14012
14590
  const section = /^\s*\[plugins\.(?:"([^"]+)"|'([^']+)'|(\S+))\]\s*(?:#.*)?$/.exec(line);
14013
14591
  if (section) {
14014
14592
  const captures = section.slice(1, 4);
@@ -14041,7 +14619,7 @@ function readCodexPluginConfigured(filePath) {
14041
14619
  }
14042
14620
  }
14043
14621
  function verifiedPluginVersion(root, client) {
14044
- if (client === "codex" && fs16.existsSync(path12.join(root, "plugin.json"))) {
14622
+ if (client === "codex" && fs17.existsSync(path12.join(root, "plugin.json"))) {
14045
14623
  const manifest2 = readJson(path12.join(root, "plugin.json"));
14046
14624
  if (manifest2 && typeof manifest2 === "object") {
14047
14625
  const plugin2 = manifest2;
@@ -14055,7 +14633,7 @@ function verifiedPluginVersion(root, client) {
14055
14633
  if (portableMcp.$schema !== AGENT_PLUGINS_MCP_SCHEMA || portableMcp.mcpServers?.canonry?.type !== "stdio" || portableMcp.mcpServers.canonry.command !== "canonry-mcp") {
14056
14634
  return null;
14057
14635
  }
14058
- return REQUIRED_SKILLS.every((name) => fs16.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
14636
+ return REQUIRED_SKILLS.every((name) => fs17.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
14059
14637
  }
14060
14638
  if (typeof plugin2.$schema === "string" && plugin2.$schema.startsWith("https://agent-plugins.org/schemas/")) {
14061
14639
  return null;
@@ -14073,7 +14651,7 @@ function verifiedPluginVersion(root, client) {
14073
14651
  if (!mcp || typeof mcp !== "object") return null;
14074
14652
  const canonryServer = mcp.mcpServers?.canonry;
14075
14653
  if (canonryServer?.command !== "canonry-mcp") return null;
14076
- return REQUIRED_SKILLS.every((name) => fs16.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
14654
+ return REQUIRED_SKILLS.every((name) => fs17.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
14077
14655
  }
14078
14656
  function readClaudeUserInstallPaths(claudeConfigDir) {
14079
14657
  const parsed = readJson(path12.join(claudeConfigDir, "plugins", "installed_plugins.json"));
@@ -14090,7 +14668,7 @@ function readClaudeUserInstallPaths(claudeConfigDir) {
14090
14668
  }
14091
14669
  function listDirectories(dir) {
14092
14670
  try {
14093
- return fs16.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path12.join(dir, entry.name));
14671
+ return fs17.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path12.join(dir, entry.name));
14094
14672
  } catch {
14095
14673
  return [];
14096
14674
  }
@@ -15749,7 +16327,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
15749
16327
  ];
15750
16328
 
15751
16329
  // src/cli-commands/wordpress.ts
15752
- import fs17 from "fs";
16330
+ import fs18 from "fs";
15753
16331
 
15754
16332
  // src/commands/wordpress.ts
15755
16333
  function getClient28() {
@@ -15988,12 +16566,12 @@ async function wordpressSetMeta(project, body) {
15988
16566
  printPageDetail(result);
15989
16567
  }
15990
16568
  async function wordpressBulkSetMeta(project, opts) {
15991
- const fs19 = await import("fs/promises");
16569
+ const fs20 = await import("fs/promises");
15992
16570
  const path13 = await import("path");
15993
16571
  const filePath = path13.resolve(opts.from);
15994
16572
  let raw;
15995
16573
  try {
15996
- raw = await fs19.readFile(filePath, "utf8");
16574
+ raw = await fs20.readFile(filePath, "utf8");
15997
16575
  } catch {
15998
16576
  throw new CliError({
15999
16577
  code: "FILE_READ_ERROR",
@@ -16090,13 +16668,13 @@ async function wordpressSetSchema(project, body) {
16090
16668
  printManualAssist(`Schema update for "${body.slug}"`, result);
16091
16669
  }
16092
16670
  async function wordpressSchemaDeploy(project, opts) {
16093
- const fs19 = await import("fs/promises");
16671
+ const fs20 = await import("fs/promises");
16094
16672
  const path13 = await import("path");
16095
16673
  const yaml = await loadYamlModule();
16096
16674
  const filePath = path13.resolve(opts.profile);
16097
16675
  let raw;
16098
16676
  try {
16099
- raw = await fs19.readFile(filePath, "utf8");
16677
+ raw = await fs20.readFile(filePath, "utf8");
16100
16678
  } catch {
16101
16679
  throw new CliError({
16102
16680
  code: "FILE_READ_ERROR",
@@ -16201,13 +16779,13 @@ async function wordpressOnboard(project, opts) {
16201
16779
  }
16202
16780
  let profileData;
16203
16781
  if (opts.profile) {
16204
- const fs19 = await import("fs/promises");
16782
+ const fs20 = await import("fs/promises");
16205
16783
  const path13 = await import("path");
16206
16784
  const yaml = await loadYamlModule();
16207
16785
  const filePath = path13.resolve(opts.profile);
16208
16786
  let raw;
16209
16787
  try {
16210
- raw = await fs19.readFile(filePath, "utf8");
16788
+ raw = await fs20.readFile(filePath, "utf8");
16211
16789
  } catch {
16212
16790
  throw new CliError({
16213
16791
  code: "FILE_READ_ERROR",
@@ -16356,7 +16934,7 @@ function resolveContent(input, command, usage, options) {
16356
16934
  }
16357
16935
  if (contentFile) {
16358
16936
  try {
16359
- return fs17.readFileSync(contentFile, "utf-8");
16937
+ return fs18.readFileSync(contentFile, "utf-8");
16360
16938
  } catch (error) {
16361
16939
  const message = describeError(error);
16362
16940
  throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
@@ -17323,16 +17901,16 @@ Usage: ${usage}`, {
17323
17901
 
17324
17902
  // src/commands/measurement-plan.ts
17325
17903
  import crypto3 from "crypto";
17326
- import fs18 from "fs";
17904
+ import fs19 from "fs";
17327
17905
  import { parse } from "yaml";
17328
17906
  import { z as z2 } from "zod";
17329
17907
  function readPlan(source) {
17330
- const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
17908
+ const content = source === "-" ? fs19.readFileSync(0, "utf8") : fs19.readFileSync(source, "utf8");
17331
17909
  const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
17332
17910
  return measurementPlanInputSchema.parse(parsed);
17333
17911
  }
17334
17912
  function readDiscoveryRule(source) {
17335
- const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
17913
+ const content = source === "-" ? fs19.readFileSync(0, "utf8") : fs19.readFileSync(source, "utf8");
17336
17914
  const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
17337
17915
  return measurementDiscoveryRuleSchema.parse(parsed);
17338
17916
  }
@@ -17381,7 +17959,7 @@ var advancedDeactivateInputSchema = z2.object({
17381
17959
  }).strict();
17382
17960
  function readAdvancedMeasurementInput(source) {
17383
17961
  if (source === void 0) return {};
17384
- const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
17962
+ const content = source === "-" ? fs19.readFileSync(0, "utf8") : fs19.readFileSync(source, "utf8");
17385
17963
  return JSON.parse(content);
17386
17964
  }
17387
17965
  var advancedMeasurementCollectionKeys = {
@@ -17563,7 +18141,7 @@ async function replaceMeasurementPlanAssignments(project, opts) {
17563
18141
  console.log(JSON.stringify({ preview, result }, null, 2));
17564
18142
  }
17565
18143
  function readGroupMembershipCsv(source) {
17566
- return source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
18144
+ return source === "-" ? fs19.readFileSync(0, "utf8") : fs19.readFileSync(source, "utf8");
17567
18145
  }
17568
18146
  async function previewMeasurementPlanGroups(project, source) {
17569
18147
  const preview = await createApiClient().previewMeasurementDraftGroupMembership(project, {
@@ -17994,6 +18572,7 @@ var REGISTERED_CLI_COMMANDS = [
17994
18572
  ...ADS_CLI_COMMANDS,
17995
18573
  ...GA_CLI_COMMANDS,
17996
18574
  ...GBP_CLI_COMMANDS,
18575
+ ...createGoogleMarketingCliCommands(createApiClient),
17997
18576
  ...TRAFFIC_CLI_COMMANDS,
17998
18577
  ...INTELLIGENCE_CLI_COMMANDS,
17999
18578
  ...VISIBILITY_STATS_CLI_COMMANDS,