@canonry/canonry 4.156.0 → 4.158.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +2 -2
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +8 -3
  3. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +454 -21
  4. package/assets/assets/{AuditHistoryPanel-gmJMVlXX.js → AuditHistoryPanel-McQfjoU9.js} +1 -1
  5. package/assets/assets/{BacklinksPage-Bv2sTCyN.js → BacklinksPage-Da8y1Wv8.js} +1 -1
  6. package/assets/assets/{HistoryPage-13q7GkiI.js → HistoryPage-9MJfhRyT.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-BgBqXT1p.js → MeasurementPropertyPage-BiNAHEJW.js} +1 -1
  8. package/assets/assets/ProjectPage-mWSMjXrR.js +15 -0
  9. package/assets/assets/{RunRow-bPzapIGJ.js → RunRow-DCG2LBlS.js} +1 -1
  10. package/assets/assets/{RunsPage-Dqfz18lH.js → RunsPage-ByWA_FSk.js} +1 -1
  11. package/assets/assets/{SettingsPage-_wMecn-V.js → SettingsPage-DdSeKAKF.js} +1 -1
  12. package/assets/assets/{TrafficPage-RYBdsOLB.js → TrafficPage-5u8rPcBt.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DpeMMyKU.js → TrafficSourceDetailPage-Dm-fK87u.js} +1 -1
  14. package/assets/assets/{extract-error-message-DUarAlyD.js → extract-error-message-CtxkAd6E.js} +1 -1
  15. package/assets/assets/{index-DP-fT2uI.js → index-DUfg50Pl.js} +28 -28
  16. package/assets/assets/index-cXb3mfeQ.css +1 -0
  17. package/assets/assets/{react-sigma_core.esm.min-DnypJl5w.js → react-sigma_core.esm.min-ComIyDKi.js} +1 -1
  18. package/assets/index.html +2 -2
  19. package/dist/{chunk-ZWESOFA5.js → chunk-4VZP64GZ.js} +273 -15
  20. package/dist/{chunk-AMPT2EP7.js → chunk-DEVAEC2S.js} +121 -1
  21. package/dist/{chunk-CPASKGUE.js → chunk-LK6YFHG4.js} +35 -1
  22. package/dist/{chunk-ABNN2ZVR.js → chunk-YP4K2KXH.js} +1933 -451
  23. package/dist/cli.js +667 -142
  24. package/dist/index.d.ts +32 -0
  25. package/dist/index.js +4 -4
  26. package/dist/{intelligence-service-WHC7B32V.js → intelligence-service-LQ6ZRWMF.js} +2 -2
  27. package/dist/mcp.js +2 -2
  28. package/package.json +12 -10
  29. package/assets/assets/ProjectPage-EsGwKhot.js +0 -15
  30. package/assets/assets/index-BxegEwic.css +0 -1
package/dist/cli.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  detectAndTrackUpgrade,
17
17
  emitInstallSummary,
18
18
  formatAuditFactorScore,
19
+ getCloudflareTrafficConnection,
19
20
  getMissingUserSkillsNudge,
20
21
  getOrCreateAnonymousId,
21
22
  installSkills,
@@ -30,7 +31,7 @@ import {
30
31
  showFirstRunNotice,
31
32
  trackCliCommandFinished,
32
33
  trackEvent
33
- } from "./chunk-ZWESOFA5.js";
34
+ } from "./chunk-4VZP64GZ.js";
34
35
  import {
35
36
  CliError,
36
37
  EXIT_SYSTEM_ERROR,
@@ -50,15 +51,21 @@ import {
50
51
  saveConfigPatch,
51
52
  systemError,
52
53
  usageError
53
- } from "./chunk-CPASKGUE.js";
54
+ } from "./chunk-LK6YFHG4.js";
54
55
  import {
56
+ CLOUDFLARE_WORKER_BINDINGS,
57
+ CLOUDFLARE_WORKER_GENERATED_MARKER,
58
+ CLOUDFLARE_WRANGLER_GENERATED_MARKER,
59
+ DEFAULT_BOT_LIST,
55
60
  apiKeys,
56
61
  createClient,
62
+ generateWorkerScript,
63
+ generateWranglerToml,
57
64
  migrate,
58
65
  projects,
59
66
  queries,
60
67
  renderReportHtml
61
- } from "./chunk-ABNN2ZVR.js";
68
+ } from "./chunk-YP4K2KXH.js";
62
69
  import {
63
70
  AdsDeliverySnapshotStatuses,
64
71
  AdsHistoricalCampaignRollupStatuses,
@@ -122,7 +129,7 @@ import {
122
129
  providerQuotaPolicySchema,
123
130
  resolveProviderInput,
124
131
  winnabilityClassSchema
125
- } from "./chunk-AMPT2EP7.js";
132
+ } from "./chunk-DEVAEC2S.js";
126
133
 
127
134
  // src/cli.ts
128
135
  import { pathToFileURL } from "url";
@@ -168,9 +175,9 @@ import { parseArgs } from "util";
168
175
  function commandId(spec) {
169
176
  return spec.path.join(".");
170
177
  }
171
- function matchesPath(args, path12) {
172
- if (args.length < path12.length) return false;
173
- return path12.every((segment, index) => args[index] === segment);
178
+ function matchesPath(args, path13) {
179
+ if (args.length < path13.length) return false;
180
+ return path13.every((segment, index) => args[index] === segment);
174
181
  }
175
182
  function withGlobalOptions(options) {
176
183
  const base = options ? { ...options } : {};
@@ -3868,9 +3875,9 @@ async function gaConnect(project, opts) {
3868
3875
  propertyId: opts.propertyId
3869
3876
  };
3870
3877
  if (opts.keyFile) {
3871
- const fs17 = await import("fs");
3878
+ const fs18 = await import("fs");
3872
3879
  try {
3873
- const content = fs17.readFileSync(opts.keyFile, "utf-8");
3880
+ const content = fs18.readFileSync(opts.keyFile, "utf-8");
3874
3881
  JSON.parse(content);
3875
3882
  body.keyJson = content;
3876
3883
  } catch (e) {
@@ -5224,9 +5231,9 @@ async function getCommand(opts) {
5224
5231
  console.log(JSON.stringify(leaf, null, 2));
5225
5232
  }
5226
5233
  }
5227
- function walkPath(value, path12) {
5228
- if (!path12 || path12 === ".") return value;
5229
- const segments = path12.split(".").flatMap((part) => {
5234
+ function walkPath(value, path13) {
5235
+ if (!path13 || path13 === ".") return value;
5236
+ const segments = path13.split(".").flatMap((part) => {
5230
5237
  const tokens = [];
5231
5238
  let i = 0;
5232
5239
  const bracketStart = part.indexOf("[");
@@ -5274,17 +5281,342 @@ var GET_CLI_COMMANDS = [
5274
5281
  },
5275
5282
  run: async (input) => {
5276
5283
  const project = requireProject(input, "get", USAGE2);
5277
- const path12 = requirePositional(input, 1, {
5284
+ const path13 = requirePositional(input, 1, {
5278
5285
  command: "get",
5279
5286
  usage: USAGE2,
5280
5287
  message: 'path is required (e.g. "scores.mentionShare.value")'
5281
5288
  });
5282
5289
  const from = getString(input.values, "from");
5283
- await getCommand({ project, path: path12, from, format: input.format });
5290
+ await getCommand({ project, path: path13, from, format: input.format });
5284
5291
  }
5285
5292
  }
5286
5293
  ];
5287
5294
 
5295
+ // src/cloudflare-worker-deploy.ts
5296
+ import { spawn } from "child_process";
5297
+ import { randomUUID } from "crypto";
5298
+ import fs4 from "fs";
5299
+ import os from "os";
5300
+ import path2 from "path";
5301
+ import { stripVTControlCharacters } from "util";
5302
+ var MAX_WRANGLER_DIAGNOSTIC_LENGTH = 2e3;
5303
+ function wranglerDiagnostic(output) {
5304
+ const plain = stripVTControlCharacters(output).trim();
5305
+ if (plain.length <= MAX_WRANGLER_DIAGNOSTIC_LENGTH) return plain;
5306
+ const half = Math.floor(MAX_WRANGLER_DIAGNOSTIC_LENGTH / 2);
5307
+ return `${plain.slice(0, half)}
5308
+ ...
5309
+ ${plain.slice(-half)}`;
5310
+ }
5311
+ function projectSlug(project) {
5312
+ return project.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
5313
+ }
5314
+ function lstatIfPresent(filePath) {
5315
+ try {
5316
+ return fs4.lstatSync(filePath);
5317
+ } catch (error) {
5318
+ if (error.code === "ENOENT") return null;
5319
+ throw error;
5320
+ }
5321
+ }
5322
+ function resolveCloudflareWorkerOutputDirectory(project, outputDirectory, cwd = process.cwd()) {
5323
+ return path2.resolve(cwd, outputDirectory ?? `canonry-cloudflare-${projectSlug(project)}`);
5324
+ }
5325
+ function prepareCloudflareWorkerOutputDirectory(outputDirectory) {
5326
+ const resolved = path2.resolve(outputDirectory);
5327
+ const outputStat = lstatIfPresent(resolved);
5328
+ if (outputStat) {
5329
+ const stat = outputStat;
5330
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
5331
+ throw new Error(`output directory is not a regular directory: ${resolved}`);
5332
+ }
5333
+ } else {
5334
+ fs4.mkdirSync(resolved, { recursive: true, mode: 448 });
5335
+ }
5336
+ const artifacts = {
5337
+ outputDirectory: resolved,
5338
+ workerScriptPath: path2.join(resolved, "worker.js"),
5339
+ wranglerTomlPath: path2.join(resolved, "wrangler.toml")
5340
+ };
5341
+ for (const artifactPath of [artifacts.workerScriptPath, artifacts.wranglerTomlPath]) {
5342
+ const stat = lstatIfPresent(artifactPath);
5343
+ if (!stat) continue;
5344
+ if (!stat.isFile() || stat.isSymbolicLink()) {
5345
+ throw new Error(`artifact path is not a regular file: ${artifactPath}`);
5346
+ }
5347
+ const contents = fs4.readFileSync(artifactPath, "utf-8");
5348
+ if (!isRecognizablyCanonryGenerated(artifactPath, contents)) {
5349
+ throw new Error(`refusing to overwrite an operator-owned artifact: ${artifactPath}`);
5350
+ }
5351
+ }
5352
+ return artifacts;
5353
+ }
5354
+ function readRegularArtifact(filePath) {
5355
+ const stat = lstatIfPresent(filePath);
5356
+ if (!stat) return null;
5357
+ if (!stat.isFile() || stat.isSymbolicLink()) {
5358
+ throw new Error(`artifact path is not a regular file: ${filePath}`);
5359
+ }
5360
+ return fs4.readFileSync(filePath, "utf-8");
5361
+ }
5362
+ function isRecognizablyCanonryGenerated(filePath, contents) {
5363
+ if (path2.basename(filePath) === "worker.js") {
5364
+ return contents.startsWith(`${CLOUDFLARE_WORKER_GENERATED_MARKER}
5365
+ `);
5366
+ }
5367
+ if (path2.basename(filePath) !== "wrangler.toml") return false;
5368
+ if (contents.startsWith(`${CLOUDFLARE_WRANGLER_GENERATED_MARKER}
5369
+ `)) return true;
5370
+ return /^name = "canonry-traffic-[^"]+"$/m.test(contents) && /^main = "worker\.js"$/m.test(contents) && /^CANONRY_DELIVERY_MODE = "direct-push"$/m.test(contents) && /^CANONRY_SOURCE_ID = /m.test(contents) && /^CANONRY_INGEST_URL = /m.test(contents) && /^CANONRY_WORKER_VERSION = /m.test(contents) && contents.includes("[secrets]") && contents.includes(CLOUDFLARE_WORKER_BINDINGS.bearerToken) && contents.includes(CLOUDFLARE_WORKER_BINDINGS.hmacSecret);
5371
+ }
5372
+ function writePrivateTemporaryFile(filePath, contents) {
5373
+ const temporaryPath = path2.join(
5374
+ path2.dirname(filePath),
5375
+ `.${path2.basename(filePath)}.${randomUUID()}.tmp`
5376
+ );
5377
+ const fd = fs4.openSync(temporaryPath, "wx", 384);
5378
+ let closed = false;
5379
+ try {
5380
+ fs4.writeFileSync(fd, contents, "utf-8");
5381
+ fs4.fsyncSync(fd);
5382
+ fs4.closeSync(fd);
5383
+ closed = true;
5384
+ } catch (error) {
5385
+ if (!closed) {
5386
+ try {
5387
+ fs4.closeSync(fd);
5388
+ } catch {
5389
+ }
5390
+ }
5391
+ try {
5392
+ fs4.unlinkSync(temporaryPath);
5393
+ } catch {
5394
+ }
5395
+ throw error;
5396
+ }
5397
+ return temporaryPath;
5398
+ }
5399
+ function writeCloudflareWorkerArtifacts(artifacts, contents) {
5400
+ const requested = [
5401
+ { filePath: artifacts.workerScriptPath, contents: contents.workerScript },
5402
+ { filePath: artifacts.wranglerTomlPath, contents: contents.wranglerToml }
5403
+ ];
5404
+ const changes = requested.flatMap((artifact) => {
5405
+ const previous = readRegularArtifact(artifact.filePath);
5406
+ if (previous === artifact.contents) return [];
5407
+ if (previous !== null && !isRecognizablyCanonryGenerated(artifact.filePath, previous)) {
5408
+ throw new Error(`refusing to overwrite an operator-owned artifact: ${artifact.filePath}`);
5409
+ }
5410
+ return [{ ...artifact, previous }];
5411
+ });
5412
+ const pending = [];
5413
+ const applied = [];
5414
+ try {
5415
+ for (const change of changes) {
5416
+ pending.push({
5417
+ ...change,
5418
+ temporaryPath: writePrivateTemporaryFile(change.filePath, change.contents)
5419
+ });
5420
+ }
5421
+ for (const change of pending) {
5422
+ if (readRegularArtifact(change.filePath) !== change.previous) {
5423
+ throw new Error(`artifact changed during update: ${change.filePath}`);
5424
+ }
5425
+ if (change.previous === null) {
5426
+ fs4.linkSync(change.temporaryPath, change.filePath);
5427
+ fs4.unlinkSync(change.temporaryPath);
5428
+ } else {
5429
+ fs4.renameSync(change.temporaryPath, change.filePath);
5430
+ }
5431
+ applied.push(change);
5432
+ }
5433
+ } catch (error) {
5434
+ for (const change of applied.reverse()) {
5435
+ try {
5436
+ if (readRegularArtifact(change.filePath) !== change.contents) continue;
5437
+ if (change.previous === null) {
5438
+ fs4.unlinkSync(change.filePath);
5439
+ } else {
5440
+ const restorePath = writePrivateTemporaryFile(change.filePath, change.previous);
5441
+ fs4.renameSync(restorePath, change.filePath);
5442
+ }
5443
+ } catch {
5444
+ }
5445
+ }
5446
+ throw error;
5447
+ } finally {
5448
+ for (const change of pending) {
5449
+ try {
5450
+ if (fs4.existsSync(change.temporaryPath)) fs4.unlinkSync(change.temporaryPath);
5451
+ } catch {
5452
+ }
5453
+ }
5454
+ }
5455
+ }
5456
+ function assertCloudflareArtifactsDoNotContainSecrets(contents, secrets) {
5457
+ const artifactText = [contents.workerScript, contents.wranglerToml, contents.instructions ?? ""];
5458
+ for (const secret of [secrets.bearerToken, secrets.hmacSecret]) {
5459
+ if (secret && artifactText.some((value) => value.includes(secret))) {
5460
+ throw new Error("Cloudflare setup response unexpectedly contained a cleartext secret");
5461
+ }
5462
+ }
5463
+ }
5464
+ function redactCloudflareSecrets(message, secrets) {
5465
+ let redacted = message;
5466
+ for (const secret of [secrets.bearerToken, secrets.hmacSecret]) {
5467
+ if (secret) redacted = redacted.replaceAll(secret, "[REDACTED]");
5468
+ }
5469
+ return redacted;
5470
+ }
5471
+ async function runWrangler(command, args, opts) {
5472
+ await new Promise((resolve, reject) => {
5473
+ const child = spawn(command, [...args], {
5474
+ cwd: opts.cwd,
5475
+ shell: false,
5476
+ stdio: "inherit"
5477
+ });
5478
+ child.once("error", reject);
5479
+ child.once("exit", (code, signal) => {
5480
+ if (code === 0) {
5481
+ resolve();
5482
+ return;
5483
+ }
5484
+ reject(new Error(
5485
+ signal ? `Wrangler exited after signal ${signal}` : `Wrangler exited with code ${code ?? "unknown"}`
5486
+ ));
5487
+ });
5488
+ });
5489
+ }
5490
+ async function runWranglerCapture(command, args, opts) {
5491
+ return new Promise((resolve, reject) => {
5492
+ const child = spawn(command, [...args], {
5493
+ cwd: opts.cwd,
5494
+ shell: false,
5495
+ stdio: ["ignore", "pipe", "pipe"]
5496
+ });
5497
+ let output = "";
5498
+ child.stdout.setEncoding("utf-8");
5499
+ child.stderr.setEncoding("utf-8");
5500
+ child.stdout.on("data", (chunk) => {
5501
+ output += String(chunk);
5502
+ });
5503
+ child.stderr.on("data", (chunk) => {
5504
+ output += String(chunk);
5505
+ });
5506
+ child.once("error", reject);
5507
+ child.once("exit", (code, signal) => {
5508
+ if (code === 0) {
5509
+ resolve(output);
5510
+ return;
5511
+ }
5512
+ const diagnostic = wranglerDiagnostic(output);
5513
+ const suffix = diagnostic ? `
5514
+ ${diagnostic}` : "";
5515
+ reject(new Error(
5516
+ signal ? `Wrangler exited after signal ${signal}${suffix}` : `Wrangler exited with code ${code ?? "unknown"}${suffix}`
5517
+ ));
5518
+ });
5519
+ });
5520
+ }
5521
+ async function preflightCloudflareWrangler(opts = {}) {
5522
+ const runner = opts.run ?? runWranglerCapture;
5523
+ const help = await runner(
5524
+ "wrangler",
5525
+ ["deploy", "--help"],
5526
+ { cwd: path2.resolve(opts.cwd ?? process.cwd()) }
5527
+ );
5528
+ const missing = ["--dry-run", "--secrets-file", "--strict"].filter((flag) => !help.includes(flag));
5529
+ if (missing.length > 0) {
5530
+ throw new Error(`Wrangler deploy does not support required flags: ${missing.join(", ")}`);
5531
+ }
5532
+ const tempDirectory = fs4.mkdtempSync(path2.join(
5533
+ opts.tempRoot ?? os.tmpdir(),
5534
+ "canonry-cloudflare-wrangler-preflight-"
5535
+ ));
5536
+ const workerScriptPath = path2.join(tempDirectory, "worker.js");
5537
+ const wranglerTomlPath = path2.join(tempDirectory, "wrangler.toml");
5538
+ const secretsPath = path2.join(tempDirectory, "secrets.json");
5539
+ try {
5540
+ fs4.chmodSync(tempDirectory, 448);
5541
+ fs4.writeFileSync(workerScriptPath, generateWorkerScript({
5542
+ deliveryMode: "direct-push",
5543
+ workerVersion: "preflight",
5544
+ botList: DEFAULT_BOT_LIST
5545
+ }), { encoding: "utf-8", flag: "wx", mode: 384 });
5546
+ fs4.writeFileSync(wranglerTomlPath, generateWranglerToml({
5547
+ deliveryMode: "direct-push",
5548
+ sourceId: "preflight",
5549
+ hostname: "example.invalid",
5550
+ ingestUrl: "https://canonry.example.invalid/api/v1/projects/preflight/traffic/cloudflare/ingest",
5551
+ workerVersion: "preflight"
5552
+ }), { encoding: "utf-8", flag: "wx", mode: 384 });
5553
+ fs4.writeFileSync(secretsPath, JSON.stringify({
5554
+ [CLOUDFLARE_WORKER_BINDINGS.bearerToken]: "preflight-bearer",
5555
+ [CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: "preflight-hmac"
5556
+ }), { encoding: "utf-8", flag: "wx", mode: 384 });
5557
+ const validationOutput = await runner(
5558
+ "wrangler",
5559
+ [
5560
+ "deploy",
5561
+ "--config",
5562
+ wranglerTomlPath,
5563
+ "--secrets-file",
5564
+ secretsPath,
5565
+ "--strict",
5566
+ "--dry-run",
5567
+ "--outdir",
5568
+ path2.join(tempDirectory, "dist")
5569
+ ],
5570
+ { cwd: tempDirectory }
5571
+ );
5572
+ const hasUnknownFields = /Unexpected fields found/i.test(validationOutput);
5573
+ const hasExperimentalSecrets = /\bexperimental\b/i.test(validationOutput) && /\bsecrets?\b/i.test(validationOutput);
5574
+ if (hasUnknownFields || hasExperimentalSecrets) {
5575
+ throw new Error(
5576
+ `Wrangler reported an incompatible generated configuration:
5577
+ ${wranglerDiagnostic(validationOutput)}`
5578
+ );
5579
+ }
5580
+ } finally {
5581
+ fs4.rmSync(tempDirectory, { recursive: true, force: true });
5582
+ }
5583
+ }
5584
+ async function deployCloudflareWorker(opts) {
5585
+ const tempDirectory = fs4.mkdtempSync(path2.join(opts.tempRoot ?? os.tmpdir(), "canonry-cloudflare-secrets-"));
5586
+ const secretsPath = path2.join(tempDirectory, "secrets.json");
5587
+ try {
5588
+ fs4.chmodSync(tempDirectory, 448);
5589
+ fs4.writeFileSync(
5590
+ secretsPath,
5591
+ JSON.stringify({
5592
+ [CLOUDFLARE_WORKER_BINDINGS.bearerToken]: opts.secrets.bearerToken,
5593
+ [CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: opts.secrets.hmacSecret
5594
+ }),
5595
+ { encoding: "utf-8", flag: "wx", mode: 384 }
5596
+ );
5597
+ fs4.chmodSync(secretsPath, 384);
5598
+ const runner = opts.run ?? runWrangler;
5599
+ await runner(
5600
+ "wrangler",
5601
+ [
5602
+ "deploy",
5603
+ "--config",
5604
+ path2.resolve(opts.wranglerTomlPath),
5605
+ "--secrets-file",
5606
+ secretsPath,
5607
+ "--strict"
5608
+ ],
5609
+ { cwd: path2.dirname(path2.resolve(opts.wranglerTomlPath)) }
5610
+ );
5611
+ } finally {
5612
+ try {
5613
+ if (fs4.existsSync(secretsPath)) fs4.unlinkSync(secretsPath);
5614
+ } finally {
5615
+ fs4.rmSync(tempDirectory, { recursive: true, force: true });
5616
+ }
5617
+ }
5618
+ }
5619
+
5288
5620
  // src/commands/traffic.ts
5289
5621
  function getClient9() {
5290
5622
  return createApiClient();
@@ -5292,6 +5624,169 @@ function getClient9() {
5292
5624
  function configString(value, fallback = "(unset)") {
5293
5625
  return typeof value === "string" ? value : fallback;
5294
5626
  }
5627
+ function errorMessage2(error) {
5628
+ return error instanceof Error ? error.message : String(error);
5629
+ }
5630
+ async function trafficConnectCloudflare(project, opts, deps = {}) {
5631
+ if (opts.deploy && !opts.zoneId) {
5632
+ throw new CliError({
5633
+ code: "TRAFFIC_CLOUDFLARE_DEPLOY_ZONE_REQUIRED",
5634
+ message: "--zone-id is required with --deploy",
5635
+ displayMessage: "Error: --zone-id <id> is required with --deploy",
5636
+ details: { project }
5637
+ });
5638
+ }
5639
+ if (opts.deploy && !opts.confirmRoute) {
5640
+ throw new CliError({
5641
+ code: "TRAFFIC_CLOUDFLARE_ROUTE_CONFIRMATION_REQUIRED",
5642
+ message: "--confirm-route is required with --deploy",
5643
+ displayMessage: "Error: inspect existing Worker routes first. Pass --confirm-route to acknowledge the required manual route attachment after Worker deployment.",
5644
+ details: { project, zoneId: opts.zoneId }
5645
+ });
5646
+ }
5647
+ if (opts.deploy && !opts.confirmFailOpen) {
5648
+ throw new CliError({
5649
+ code: "TRAFFIC_CLOUDFLARE_FAIL_OPEN_CONFIRMATION_REQUIRED",
5650
+ message: "--confirm-fail-open is required with --deploy",
5651
+ displayMessage: "Error: pass --confirm-fail-open to acknowledge the required Fail open setting during manual route attachment after Worker deployment.",
5652
+ details: { project, zoneId: opts.zoneId }
5653
+ });
5654
+ }
5655
+ if (opts.deploy) {
5656
+ try {
5657
+ await (deps.preflightWrangler ?? preflightCloudflareWrangler)();
5658
+ } catch (error) {
5659
+ const message = errorMessage2(error);
5660
+ throw new CliError({
5661
+ code: "TRAFFIC_CLOUDFLARE_WRANGLER_UNSUPPORTED",
5662
+ message,
5663
+ displayMessage: `Error: Wrangler preflight failed before Canonry changed state.
5664
+ ${message}`,
5665
+ details: { project }
5666
+ });
5667
+ }
5668
+ }
5669
+ const outputDirectory = resolveCloudflareWorkerOutputDirectory(project, opts.outputDirectory);
5670
+ let artifacts;
5671
+ try {
5672
+ artifacts = prepareCloudflareWorkerOutputDirectory(outputDirectory);
5673
+ } catch (error) {
5674
+ const message = errorMessage2(error);
5675
+ throw new CliError({
5676
+ code: "TRAFFIC_CLOUDFLARE_OUTPUT_INVALID",
5677
+ message,
5678
+ displayMessage: `Error: cannot use Cloudflare Worker output directory: ${message}`,
5679
+ details: { project, outputDirectory }
5680
+ });
5681
+ }
5682
+ const client = deps.client ?? getClient9();
5683
+ const result = await client.trafficConnectCloudflare(project, {
5684
+ deliveryMode: "direct-push",
5685
+ displayName: opts.displayName,
5686
+ zoneId: opts.zoneId,
5687
+ accountId: opts.accountId
5688
+ });
5689
+ const localConfig = (deps.loadLocalConfig ?? loadConfigRaw)();
5690
+ const localCredential = localConfig ? getCloudflareTrafficConnection(localConfig, project) : void 0;
5691
+ const deploymentCredential = localCredential?.sourceId === result.sourceId ? localCredential : void 0;
5692
+ if (deploymentCredential) {
5693
+ try {
5694
+ assertCloudflareArtifactsDoNotContainSecrets(result, deploymentCredential);
5695
+ } catch {
5696
+ throw new CliError({
5697
+ code: "TRAFFIC_CLOUDFLARE_SECRET_EXPOSURE",
5698
+ message: "Cloudflare setup response unexpectedly contained a cleartext secret",
5699
+ displayMessage: "Error: refused to write Cloudflare artifacts because the response contained a cleartext secret",
5700
+ details: { project, sourceId: result.sourceId }
5701
+ });
5702
+ }
5703
+ }
5704
+ try {
5705
+ writeCloudflareWorkerArtifacts(artifacts, result);
5706
+ } catch (error) {
5707
+ const message = errorMessage2(error);
5708
+ throw new CliError({
5709
+ code: "TRAFFIC_CLOUDFLARE_ARTIFACT_WRITE_FAILED",
5710
+ message,
5711
+ displayMessage: `Error: Cloudflare source connected, but artifact writing failed: ${message}`,
5712
+ details: { project, sourceId: result.sourceId, outputDirectory }
5713
+ });
5714
+ }
5715
+ if (opts.deploy && !deploymentCredential) {
5716
+ throw new CliError({
5717
+ code: "TRAFFIC_CLOUDFLARE_DEPLOY_CONFIG_UNSHARED",
5718
+ message: "Cloudflare auto-deploy requires the CLI and Canonry server to share the same local config",
5719
+ displayMessage: "Error: source connected and artifacts written, but auto-deploy is unavailable because this CLI cannot read the server-stored Worker credentials. Ask the Canonry server operator to deploy the Worker and install its secrets.",
5720
+ details: {
5721
+ project,
5722
+ sourceId: result.sourceId,
5723
+ outputDirectory
5724
+ }
5725
+ });
5726
+ }
5727
+ if (opts.deploy && deploymentCredential) {
5728
+ try {
5729
+ await (deps.deployWorker ?? deployCloudflareWorker)({
5730
+ wranglerTomlPath: artifacts.wranglerTomlPath,
5731
+ secrets: deploymentCredential
5732
+ });
5733
+ } catch (error) {
5734
+ const message = redactCloudflareSecrets(errorMessage2(error), deploymentCredential);
5735
+ throw new CliError({
5736
+ code: "TRAFFIC_CLOUDFLARE_DEPLOY_FAILED",
5737
+ message,
5738
+ displayMessage: `Error: Cloudflare source connected and artifacts written, but Wrangler deploy failed: ${message}`,
5739
+ details: { project, sourceId: result.sourceId, outputDirectory }
5740
+ });
5741
+ }
5742
+ }
5743
+ const summary = {
5744
+ sourceId: result.sourceId,
5745
+ deliveryMode: result.deliveryMode,
5746
+ workerVersion: result.workerVersion,
5747
+ outputDirectory: artifacts.outputDirectory,
5748
+ files: {
5749
+ workerScript: artifacts.workerScriptPath,
5750
+ wranglerConfig: artifacts.wranglerTomlPath
5751
+ },
5752
+ autoDeployAvailable: Boolean(deploymentCredential),
5753
+ deployment: opts.deploy ? "worker-deployed-route-unattached" : "not-requested",
5754
+ routeAttachment: "required-manual",
5755
+ requestLimitFailureMode: {
5756
+ required: "fail-open",
5757
+ status: "required-during-manual-route-attachment",
5758
+ configuredBy: "cloudflare-dashboard",
5759
+ configuredByWrangler: false
5760
+ },
5761
+ postDeployInstructions: result.instructions
5762
+ };
5763
+ if (isMachineFormat(opts.format)) {
5764
+ console.log(JSON.stringify(summary, null, 2));
5765
+ return;
5766
+ }
5767
+ console.log(`Cloudflare direct-push traffic source connected for project "${project}".`);
5768
+ console.log(` Source ID: ${result.sourceId}`);
5769
+ console.log(` Worker version: ${result.workerVersion}`);
5770
+ console.log(` Worker script: ${artifacts.workerScriptPath}`);
5771
+ console.log(` Wrangler config: ${artifacts.wranglerTomlPath}`);
5772
+ console.log(` Deployment: ${opts.deploy ? "Worker deployed; route not attached" : "not requested"}`);
5773
+ console.log("");
5774
+ console.log("The required manual route is a catch-all. Inspect existing Worker routes before deployment.");
5775
+ console.log("Wrangler deployment does not attach the route. Attach it manually after the Worker deploys.");
5776
+ console.log("Required: set the route Request limit failure mode to Fail open before route activation.");
5777
+ console.log("Wrangler cannot configure this toggle. Workers Free includes 100,000 requests per day for the account.");
5778
+ console.log("Use one authoritative server-traffic source for this site. Overlapping adapters double-count project totals.");
5779
+ if (opts.deploy) {
5780
+ console.log("");
5781
+ console.log("Required Cloudflare Dashboard steps:");
5782
+ console.log(result.instructions);
5783
+ console.log("Keep the Canonry public HTTPS URL reachable so the Worker can push events.");
5784
+ } else if (deploymentCredential) {
5785
+ console.log("Next: install and authenticate Wrangler. Inspect the route, then rerun with --deploy --confirm-route --confirm-fail-open and --zone-id <id>.");
5786
+ } else {
5787
+ console.log("Auto-deploy is unavailable because this CLI and the Canonry server do not share local config. Ask the server operator to deploy and install the Worker secrets.");
5788
+ }
5789
+ }
5295
5790
  async function trafficConnectWordpress(project, opts) {
5296
5791
  if (!opts.url) {
5297
5792
  throw new CliError({
@@ -5319,9 +5814,9 @@ async function trafficConnectWordpress(project, opts) {
5319
5814
  }
5320
5815
  let applicationPassword = opts.appPassword?.trim() ?? "";
5321
5816
  if (!applicationPassword && opts.appPasswordFile) {
5322
- const fs17 = await import("fs");
5817
+ const fs18 = await import("fs");
5323
5818
  try {
5324
- applicationPassword = fs17.readFileSync(opts.appPasswordFile, "utf-8").trim();
5819
+ applicationPassword = fs18.readFileSync(opts.appPasswordFile, "utf-8").trim();
5325
5820
  } catch (e) {
5326
5821
  const msg = e instanceof Error ? e.message : String(e);
5327
5822
  throw new CliError({
@@ -5377,10 +5872,10 @@ async function trafficConnectCloudRun(project, opts) {
5377
5872
  details: { project }
5378
5873
  });
5379
5874
  }
5380
- const fs17 = await import("fs");
5875
+ const fs18 = await import("fs");
5381
5876
  let keyJson;
5382
5877
  try {
5383
- keyJson = fs17.readFileSync(opts.serviceAccountKey, "utf-8");
5878
+ keyJson = fs18.readFileSync(opts.serviceAccountKey, "utf-8");
5384
5879
  JSON.parse(keyJson);
5385
5880
  } catch (e) {
5386
5881
  const msg = e instanceof Error ? e.message : String(e);
@@ -5440,9 +5935,9 @@ async function trafficConnectVercel(project, opts) {
5440
5935
  }
5441
5936
  let token = opts.token?.trim() ?? "";
5442
5937
  if (!token && opts.tokenFile) {
5443
- const fs17 = await import("fs");
5938
+ const fs18 = await import("fs");
5444
5939
  try {
5445
- token = fs17.readFileSync(opts.tokenFile, "utf-8").trim();
5940
+ token = fs18.readFileSync(opts.tokenFile, "utf-8").trim();
5446
5941
  } catch (e) {
5447
5942
  const msg = e instanceof Error ? e.message : String(e);
5448
5943
  throw new CliError({
@@ -5552,11 +6047,11 @@ async function trafficBackfill(project, opts) {
5552
6047
  console.log(`Inspect rebuilt rollups: canonry traffic events ${project} --source ${opts.source} --since-minutes ${submitted.daysApplied * 24 * 60}`);
5553
6048
  return;
5554
6049
  }
5555
- const errorMessage2 = final.error?.message ?? null;
6050
+ const errorMessage3 = final.error?.message ?? null;
5556
6051
  throw new CliError({
5557
6052
  code: "TRAFFIC_BACKFILL_FAILED",
5558
- message: errorMessage2 ?? "backfill run did not complete successfully",
5559
- displayMessage: `Error: backfill run ${final.id} ${final.status}${errorMessage2 ? ` \u2014 ${errorMessage2}` : ""}`,
6053
+ message: errorMessage3 ?? "backfill run did not complete successfully",
6054
+ displayMessage: `Error: backfill run ${final.id} ${final.status}${errorMessage3 ? ` \u2014 ${errorMessage3}` : ""}`,
5560
6055
  details: { project, runId: final.id, status: final.status }
5561
6056
  });
5562
6057
  }
@@ -5791,6 +6286,36 @@ async function trafficEvents(project, opts) {
5791
6286
 
5792
6287
  // src/cli-commands/traffic.ts
5793
6288
  var TRAFFIC_CLI_COMMANDS = [
6289
+ {
6290
+ path: ["traffic", "connect", "cloudflare"],
6291
+ usage: "canonry traffic connect cloudflare <project> [--display-name <name>] [--zone-id <id>] [--account-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open] [--format json]",
6292
+ options: {
6293
+ "display-name": stringOption(),
6294
+ "zone-id": stringOption(),
6295
+ "account-id": stringOption(),
6296
+ "output-dir": stringOption(),
6297
+ deploy: { type: "boolean" },
6298
+ "confirm-route": { type: "boolean" },
6299
+ "confirm-fail-open": { type: "boolean" }
6300
+ },
6301
+ run: async (input) => {
6302
+ const project = requireProject(
6303
+ input,
6304
+ "traffic.connect.cloudflare",
6305
+ "canonry traffic connect cloudflare <project> [--zone-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open]"
6306
+ );
6307
+ await trafficConnectCloudflare(project, {
6308
+ displayName: getString(input.values, "display-name"),
6309
+ zoneId: getString(input.values, "zone-id"),
6310
+ accountId: getString(input.values, "account-id"),
6311
+ outputDirectory: getString(input.values, "output-dir"),
6312
+ deploy: getBoolean(input.values, "deploy"),
6313
+ confirmRoute: getBoolean(input.values, "confirm-route"),
6314
+ confirmFailOpen: getBoolean(input.values, "confirm-fail-open"),
6315
+ format: input.format
6316
+ });
6317
+ }
6318
+ },
5794
6319
  {
5795
6320
  path: ["traffic", "connect", "cloud-run"],
5796
6321
  usage: "canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path> [--service <name>] [--location <region>] [--display-name <name>] [--format json]",
@@ -5890,7 +6415,7 @@ var TRAFFIC_CLI_COMMANDS = [
5890
6415
  unknownSubcommand(input.positionals[0], {
5891
6416
  command: "traffic connect",
5892
6417
  usage: "canonry traffic connect <provider> <project> [args]",
5893
- available: ["cloud-run", "wordpress", "vercel"]
6418
+ available: ["cloud-run", "cloudflare", "wordpress", "vercel"]
5894
6419
  });
5895
6420
  }
5896
6421
  },
@@ -5998,7 +6523,7 @@ var TRAFFIC_CLI_COMMANDS = [
5998
6523
  },
5999
6524
  {
6000
6525
  path: ["traffic", "events"],
6001
- usage: "canonry traffic events <project> [--kind crawler|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--granularity hour|day] [--format json]",
6526
+ usage: "canonry traffic events <project> [--kind crawler|ai-user-fetch|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--granularity hour|day] [--format json]",
6002
6527
  options: {
6003
6528
  kind: stringOption(),
6004
6529
  source: stringOption(),
@@ -6270,10 +6795,10 @@ Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} acc
6270
6795
  console.log("(Ensure this URI is listed in your Google Cloud Console OAuth client's authorized redirect URIs)\n");
6271
6796
  }
6272
6797
  try {
6273
- const { spawn: spawn2 } = await import("child_process");
6798
+ const { spawn: spawn3 } = await import("child_process");
6274
6799
  const platform = process.platform;
6275
6800
  const [cmd, ...extraArgs] = platform === "darwin" ? ["open", authUrl] : platform === "win32" ? ["cmd", "/c", "start", "", authUrl] : ["xdg-open", authUrl];
6276
- spawn2(cmd, [...extraArgs], { detached: true, stdio: "ignore" }).unref();
6801
+ spawn3(cmd, [...extraArgs], { detached: true, stdio: "ignore" }).unref();
6277
6802
  console.log("(Browser opened automatically)");
6278
6803
  } catch {
6279
6804
  console.log("(Could not open browser automatically \u2014 please copy the URL above)");
@@ -7785,7 +8310,7 @@ var USERS_CLI_COMMANDS = [
7785
8310
  ];
7786
8311
 
7787
8312
  // src/commands/keyword.ts
7788
- import fs4 from "fs";
8313
+ import fs5 from "fs";
7789
8314
  function getClient14() {
7790
8315
  return createApiClient();
7791
8316
  }
@@ -7853,7 +8378,7 @@ async function listKeywords(project, format) {
7853
8378
  }
7854
8379
  }
7855
8380
  async function importKeywords(project, filePath, format) {
7856
- if (!fs4.existsSync(filePath)) {
8381
+ if (!fs5.existsSync(filePath)) {
7857
8382
  throw new CliError({
7858
8383
  code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
7859
8384
  message: `File not found: ${filePath}`,
@@ -7864,7 +8389,7 @@ async function importKeywords(project, filePath, format) {
7864
8389
  }
7865
8390
  });
7866
8391
  }
7867
- const content = fs4.readFileSync(filePath, "utf-8");
8392
+ const content = fs5.readFileSync(filePath, "utf-8");
7868
8393
  const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
7869
8394
  if (keywords.length === 0) {
7870
8395
  if (isMachineFormat(format)) {
@@ -8065,7 +8590,7 @@ var KEYWORD_CLI_COMMANDS = [
8065
8590
  ];
8066
8591
 
8067
8592
  // src/commands/query.ts
8068
- import fs5 from "fs";
8593
+ import fs6 from "fs";
8069
8594
  function getClient15() {
8070
8595
  return createApiClient();
8071
8596
  }
@@ -8156,7 +8681,7 @@ async function listQueries(project, format) {
8156
8681
  }
8157
8682
  }
8158
8683
  async function importQueries(project, filePath, format) {
8159
- if (!fs5.existsSync(filePath)) {
8684
+ if (!fs6.existsSync(filePath)) {
8160
8685
  throw new CliError({
8161
8686
  code: "QUERY_IMPORT_FILE_NOT_FOUND",
8162
8687
  message: `File not found: ${filePath}`,
@@ -8167,7 +8692,7 @@ async function importQueries(project, filePath, format) {
8167
8692
  }
8168
8693
  });
8169
8694
  }
8170
- const content = fs5.readFileSync(filePath, "utf-8");
8695
+ const content = fs6.readFileSync(filePath, "utf-8");
8171
8696
  const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
8172
8697
  if (queries2.length === 0) {
8173
8698
  if (isMachineFormat(format)) {
@@ -8369,16 +8894,16 @@ var QUERY_CLI_COMMANDS = [
8369
8894
  ];
8370
8895
 
8371
8896
  // src/commands/mcp.ts
8372
- import fs6 from "fs";
8373
- import path3 from "path";
8897
+ import fs7 from "fs";
8898
+ import path4 from "path";
8374
8899
  import { createRequire } from "module";
8375
8900
 
8376
8901
  // src/mcp-clients.ts
8377
- import os from "os";
8378
- import path2 from "path";
8902
+ import os2 from "os";
8903
+ import path3 from "path";
8379
8904
  var CLAUDE_DESKTOP_CONFIG_FILENAME = "claude_desktop_config.json";
8380
8905
  function homeRelative(...segments) {
8381
- return path2.join(os.homedir(), ...segments);
8906
+ return path3.join(os2.homedir(), ...segments);
8382
8907
  }
8383
8908
  function claudeDesktopConfigPath() {
8384
8909
  switch (process.platform) {
@@ -8386,7 +8911,7 @@ function claudeDesktopConfigPath() {
8386
8911
  return homeRelative("Library", "Application Support", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
8387
8912
  case "win32": {
8388
8913
  const appData = process.env.APPDATA ?? homeRelative("AppData", "Roaming");
8389
- return path2.join(appData, "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
8914
+ return path3.join(appData, "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
8390
8915
  }
8391
8916
  default:
8392
8917
  return homeRelative(".config", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
@@ -8399,7 +8924,7 @@ function codexConfigPath() {
8399
8924
  return homeRelative(".codex", "config.toml");
8400
8925
  }
8401
8926
  function claudeCodeProjectConfigPath() {
8402
- return path2.join(process.cwd(), ".mcp.json");
8927
+ return path3.join(process.cwd(), ".mcp.json");
8403
8928
  }
8404
8929
  var SUPPORTED_MCP_CLIENTS = [
8405
8930
  {
@@ -8442,7 +8967,7 @@ function listMcpClientIds() {
8442
8967
  var _require = createRequire(import.meta.url);
8443
8968
  function resolveCanonryMcpBin() {
8444
8969
  const packageJsonPath = _require.resolve("../package.json");
8445
- const packageRoot = path3.dirname(packageJsonPath);
8970
+ const packageRoot = path4.dirname(packageJsonPath);
8446
8971
  const pkg = _require("../package.json");
8447
8972
  const relativeBin = pkg.bin?.["canonry-mcp"];
8448
8973
  if (!relativeBin) {
@@ -8452,7 +8977,7 @@ function resolveCanonryMcpBin() {
8452
8977
  exitCode: 2
8453
8978
  });
8454
8979
  }
8455
- return path3.resolve(packageRoot, relativeBin);
8980
+ return path4.resolve(packageRoot, relativeBin);
8456
8981
  }
8457
8982
  function buildEntry(opts) {
8458
8983
  const target = opts.binPath ?? resolveCanonryMcpBin();
@@ -8485,8 +9010,8 @@ function renderClientSnippet(client, serverName, entry) {
8485
9010
  return renderJsonSnippet(serverName, entry, client.format);
8486
9011
  }
8487
9012
  function readJsonConfig(configPath) {
8488
- if (!fs6.existsSync(configPath)) return {};
8489
- const raw = fs6.readFileSync(configPath, "utf-8").trim();
9013
+ if (!fs7.existsSync(configPath)) return {};
9014
+ const raw = fs7.readFileSync(configPath, "utf-8").trim();
8490
9015
  if (!raw) return {};
8491
9016
  try {
8492
9017
  const parsed = JSON.parse(raw);
@@ -8504,14 +9029,14 @@ function readJsonConfig(configPath) {
8504
9029
  }
8505
9030
  }
8506
9031
  function writeJsonConfig(configPath, value) {
8507
- fs6.mkdirSync(path3.dirname(configPath), { recursive: true });
8508
- fs6.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
9032
+ fs7.mkdirSync(path4.dirname(configPath), { recursive: true });
9033
+ fs7.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
8509
9034
  `, "utf-8");
8510
9035
  }
8511
9036
  function backupConfigIfPresent(configPath) {
8512
- if (!fs6.existsSync(configPath)) return void 0;
9037
+ if (!fs7.existsSync(configPath)) return void 0;
8513
9038
  const backupPath = `${configPath}.canonry.bak`;
8514
- fs6.copyFileSync(configPath, backupPath);
9039
+ fs7.copyFileSync(configPath, backupPath);
8515
9040
  return backupPath;
8516
9041
  }
8517
9042
  function findClientOrThrow(id) {
@@ -8867,13 +9392,13 @@ var NOTIFY_CLI_COMMANDS = [
8867
9392
  ];
8868
9393
 
8869
9394
  // src/commands/apply.ts
8870
- import fs7 from "fs";
9395
+ import fs8 from "fs";
8871
9396
  import { parseAllDocuments } from "yaml";
8872
9397
  async function applyConfigFile(filePath) {
8873
- if (!fs7.existsSync(filePath)) {
9398
+ if (!fs8.existsSync(filePath)) {
8874
9399
  throw new Error(`File not found: ${filePath}`);
8875
9400
  }
8876
- const content = fs7.readFileSync(filePath, "utf-8");
9401
+ const content = fs8.readFileSync(filePath, "utf-8");
8877
9402
  const docs = parseAllDocuments(content);
8878
9403
  const client = createApiClient();
8879
9404
  const errors = [];
@@ -9276,8 +9801,8 @@ async function loadLatestRunForExport(client, project) {
9276
9801
  }
9277
9802
 
9278
9803
  // src/commands/results-export.ts
9279
- import fs8 from "fs";
9280
- import path4 from "path";
9804
+ import fs9 from "fs";
9805
+ import path5 from "path";
9281
9806
  async function exportResults(project, opts) {
9282
9807
  const { output, ...request } = opts;
9283
9808
  const artifact = await createApiClient().downloadResultsExport(project, request);
@@ -9285,9 +9810,9 @@ async function exportResults(project, opts) {
9285
9810
  process.stdout.write(artifact.content);
9286
9811
  return;
9287
9812
  }
9288
- const target = output ? path4.resolve(output) : path4.resolve(process.cwd(), path4.basename(artifact.filename));
9289
- fs8.mkdirSync(path4.dirname(target), { recursive: true });
9290
- fs8.writeFileSync(target, artifact.content, "utf8");
9813
+ const target = output ? path5.resolve(output) : path5.resolve(process.cwd(), path5.basename(artifact.filename));
9814
+ fs9.mkdirSync(path5.dirname(target), { recursive: true });
9815
+ fs9.writeFileSync(target, artifact.content, "utf8");
9291
9816
  console.log(`Results export written to ${target}`);
9292
9817
  }
9293
9818
 
@@ -10003,11 +10528,11 @@ function parseProviderModelAssignments(assignments) {
10003
10528
  }
10004
10529
 
10005
10530
  // src/commands/report.ts
10006
- import fs9 from "fs";
10007
- import path5 from "path";
10531
+ import fs10 from "fs";
10532
+ import path6 from "path";
10008
10533
  function defaultOutputPath(project, audience) {
10009
10534
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
10010
- return path5.resolve(process.cwd(), `canonry-report-${project}-${audience}-${date}.html`);
10535
+ return path6.resolve(process.cwd(), `canonry-report-${project}-${audience}-${date}.html`);
10011
10536
  }
10012
10537
  async function runReportCommand(project, opts = {}) {
10013
10538
  const client = createApiClient();
@@ -10018,12 +10543,12 @@ async function runReportCommand(project, opts = {}) {
10018
10543
  return;
10019
10544
  }
10020
10545
  const html = renderReportHtml(report, { audience });
10021
- const targetPath = opts.output ? path5.resolve(opts.output) : defaultOutputPath(project, audience);
10022
- const dir = path5.dirname(targetPath);
10023
- if (!fs9.existsSync(dir)) {
10024
- fs9.mkdirSync(dir, { recursive: true });
10546
+ const targetPath = opts.output ? path6.resolve(opts.output) : defaultOutputPath(project, audience);
10547
+ const dir = path6.dirname(targetPath);
10548
+ if (!fs10.existsSync(dir)) {
10549
+ fs10.mkdirSync(dir, { recursive: true });
10025
10550
  }
10026
- fs9.writeFileSync(targetPath, html, "utf-8");
10551
+ fs10.writeFileSync(targetPath, html, "utf-8");
10027
10552
  console.log(`Report written to ${targetPath}`);
10028
10553
  }
10029
10554
 
@@ -10998,12 +11523,12 @@ var SKILLS_CLI_COMMANDS = [
10998
11523
  ];
10999
11524
 
11000
11525
  // src/commands/snapshot.ts
11001
- import fs11 from "fs";
11002
- import path7 from "path";
11526
+ import fs12 from "fs";
11527
+ import path8 from "path";
11003
11528
 
11004
11529
  // src/snapshot-pdf.ts
11005
- import fs10 from "fs";
11006
- import path6 from "path";
11530
+ import fs11 from "fs";
11531
+ import path7 from "path";
11007
11532
  import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
11008
11533
  var PAGE_WIDTH = 612;
11009
11534
  var PAGE_HEIGHT = 792;
@@ -11215,9 +11740,9 @@ async function writeSnapshotPdf(report, outputPath) {
11215
11740
  renderCompetitors(pdf, report);
11216
11741
  renderQueries(pdf, report);
11217
11742
  const bytes = await doc.save();
11218
- const resolvedPath = path6.resolve(outputPath);
11219
- fs10.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
11220
- fs10.writeFileSync(resolvedPath, bytes);
11743
+ const resolvedPath = path7.resolve(outputPath);
11744
+ fs11.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
11745
+ fs11.writeFileSync(resolvedPath, bytes);
11221
11746
  return resolvedPath;
11222
11747
  }
11223
11748
  function renderCover(pdf, report) {
@@ -11375,9 +11900,9 @@ Markdown saved: ${savedMdPath}`);
11375
11900
  PDF saved: ${savedPdfPath}`);
11376
11901
  }
11377
11902
  function writeSnapshotMarkdown(report, outputPath) {
11378
- const resolvedPath = path7.resolve(outputPath);
11379
- fs11.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
11380
- fs11.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
11903
+ const resolvedPath = path8.resolve(outputPath);
11904
+ fs12.mkdirSync(path8.dirname(resolvedPath), { recursive: true });
11905
+ fs12.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
11381
11906
  return resolvedPath;
11382
11907
  }
11383
11908
  function formatSnapshotMarkdown(report) {
@@ -12360,7 +12885,7 @@ var CONTENT_CLI_COMMANDS = [
12360
12885
 
12361
12886
  // src/commands/bootstrap.ts
12362
12887
  import crypto from "crypto";
12363
- import path8 from "path";
12888
+ import path9 from "path";
12364
12889
  import { eq } from "drizzle-orm";
12365
12890
 
12366
12891
  // ../config/src/index.ts
@@ -12517,7 +13042,7 @@ async function bootstrapCommand(_opts) {
12517
13042
  );
12518
13043
  }
12519
13044
  const configDir = getConfigDir();
12520
- const databasePath = env.databasePath || path8.join(configDir, "data.db");
13045
+ const databasePath = env.databasePath || path9.join(configDir, "data.db");
12521
13046
  const existing = configExists();
12522
13047
  const existingConfig = existing ? loadConfig() : void 0;
12523
13048
  let rawApiKey;
@@ -12604,11 +13129,11 @@ async function bootstrapCommand(_opts) {
12604
13129
  }
12605
13130
 
12606
13131
  // src/commands/daemon.ts
12607
- import { spawn } from "child_process";
12608
- import fs12 from "fs";
12609
- import path9 from "path";
13132
+ import { spawn as spawn2 } from "child_process";
13133
+ import fs13 from "fs";
13134
+ import path10 from "path";
12610
13135
  function getPidPath() {
12611
- return path9.join(getConfigDir(), "canonry.pid");
13136
+ return path10.join(getConfigDir(), "canonry.pid");
12612
13137
  }
12613
13138
  function isProcessAlive(pid) {
12614
13139
  try {
@@ -12648,8 +13173,8 @@ function buildServeForwardArgs(opts) {
12648
13173
  async function startDaemon(opts) {
12649
13174
  const pidPath = getPidPath();
12650
13175
  const format = opts.format ?? "text";
12651
- if (fs12.existsSync(pidPath)) {
12652
- const existingPid = parseInt(fs12.readFileSync(pidPath, "utf-8").trim(), 10);
13176
+ if (fs13.existsSync(pidPath)) {
13177
+ const existingPid = parseInt(fs13.readFileSync(pidPath, "utf-8").trim(), 10);
12653
13178
  if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
12654
13179
  throw new CliError({
12655
13180
  code: "DAEMON_ALREADY_RUNNING",
@@ -12660,13 +13185,13 @@ async function startDaemon(opts) {
12660
13185
  }
12661
13186
  });
12662
13187
  }
12663
- fs12.unlinkSync(pidPath);
13188
+ fs13.unlinkSync(pidPath);
12664
13189
  }
12665
- const cliPath = path9.resolve(new URL(import.meta.url).pathname);
13190
+ const cliPath = path10.resolve(new URL(import.meta.url).pathname);
12666
13191
  const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
12667
13192
  const args = inSourceMode ? ["--import", "tsx", cliPath, "serve"] : [cliPath, "serve"];
12668
13193
  args.push(...buildServeForwardArgs(opts));
12669
- const child = spawn(process.execPath, args, {
13194
+ const child = spawn2(process.execPath, args, {
12670
13195
  detached: true,
12671
13196
  stdio: "ignore"
12672
13197
  });
@@ -12679,10 +13204,10 @@ async function startDaemon(opts) {
12679
13204
  });
12680
13205
  }
12681
13206
  const configDir = getConfigDir();
12682
- if (!fs12.existsSync(configDir)) {
12683
- fs12.mkdirSync(configDir, { recursive: true });
13207
+ if (!fs13.existsSync(configDir)) {
13208
+ fs13.mkdirSync(configDir, { recursive: true });
12684
13209
  }
12685
- fs12.writeFileSync(pidPath, String(child.pid), "utf-8");
13210
+ fs13.writeFileSync(pidPath, String(child.pid), "utf-8");
12686
13211
  const port = opts.port ?? "4100";
12687
13212
  const host = opts.host ?? "127.0.0.1";
12688
13213
  if (!isMachineFormat(format)) {
@@ -12691,7 +13216,7 @@ async function startDaemon(opts) {
12691
13216
  const ready = await waitForReady(host, port);
12692
13217
  if (!ready) {
12693
13218
  try {
12694
- fs12.unlinkSync(pidPath);
13219
+ fs13.unlinkSync(pidPath);
12695
13220
  } catch {
12696
13221
  }
12697
13222
  throw new CliError({
@@ -12723,7 +13248,7 @@ async function startDaemon(opts) {
12723
13248
  }
12724
13249
  function stopDaemon(format = "text") {
12725
13250
  const pidPath = getPidPath();
12726
- if (!fs12.existsSync(pidPath)) {
13251
+ if (!fs13.existsSync(pidPath)) {
12727
13252
  if (isMachineFormat(format)) {
12728
13253
  console.log(JSON.stringify({
12729
13254
  stopped: false,
@@ -12734,7 +13259,7 @@ function stopDaemon(format = "text") {
12734
13259
  console.log("Canonry is not running (no PID file found)");
12735
13260
  return;
12736
13261
  }
12737
- const pid = parseInt(fs12.readFileSync(pidPath, "utf-8").trim(), 10);
13262
+ const pid = parseInt(fs13.readFileSync(pidPath, "utf-8").trim(), 10);
12738
13263
  if (isNaN(pid)) {
12739
13264
  if (isMachineFormat(format)) {
12740
13265
  console.log(JSON.stringify({
@@ -12745,7 +13270,7 @@ function stopDaemon(format = "text") {
12745
13270
  } else {
12746
13271
  console.error("Invalid PID file. Removing it.");
12747
13272
  }
12748
- fs12.unlinkSync(pidPath);
13273
+ fs13.unlinkSync(pidPath);
12749
13274
  return;
12750
13275
  }
12751
13276
  if (!isProcessAlive(pid)) {
@@ -12759,12 +13284,12 @@ function stopDaemon(format = "text") {
12759
13284
  } else {
12760
13285
  console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
12761
13286
  }
12762
- fs12.unlinkSync(pidPath);
13287
+ fs13.unlinkSync(pidPath);
12763
13288
  return;
12764
13289
  }
12765
13290
  try {
12766
13291
  process.kill(pid, "SIGTERM");
12767
- fs12.unlinkSync(pidPath);
13292
+ fs13.unlinkSync(pidPath);
12768
13293
  if (isMachineFormat(format)) {
12769
13294
  console.log(JSON.stringify({
12770
13295
  stopped: true,
@@ -12788,9 +13313,9 @@ function stopDaemon(format = "text") {
12788
13313
 
12789
13314
  // src/commands/init.ts
12790
13315
  import crypto2 from "crypto";
12791
- import fs13 from "fs";
13316
+ import fs14 from "fs";
12792
13317
  import readline2 from "readline";
12793
- import path10 from "path";
13318
+ import path11 from "path";
12794
13319
  var pendingServeHandoff = false;
12795
13320
  function consumePendingServeHandoff() {
12796
13321
  const pending = pendingServeHandoff;
@@ -12825,8 +13350,8 @@ var DEFAULT_QUOTA = {
12825
13350
  var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
12826
13351
  function cwdLooksLikeProject(dir) {
12827
13352
  const home = process.env.HOME ?? "";
12828
- if (home && path10.resolve(dir) === path10.resolve(home)) return false;
12829
- return PROJECT_MARKERS.some((marker) => fs13.existsSync(path10.join(dir, marker)));
13353
+ if (home && path11.resolve(dir) === path11.resolve(home)) return false;
13354
+ return PROJECT_MARKERS.some((marker) => fs14.existsSync(path11.join(dir, marker)));
12830
13355
  }
12831
13356
  var DEFAULT_AGENT_MODELS = {
12832
13357
  anthropic: "anthropic/claude-sonnet-4-6",
@@ -12857,8 +13382,8 @@ async function initCommand(opts) {
12857
13382
  return void 0;
12858
13383
  }
12859
13384
  const configDir = getConfigDir();
12860
- if (!fs13.existsSync(configDir)) {
12861
- fs13.mkdirSync(configDir, { recursive: true });
13385
+ if (!fs14.existsSync(configDir)) {
13386
+ fs14.mkdirSync(configDir, { recursive: true });
12862
13387
  }
12863
13388
  const bootstrapEnv = getBootstrapEnv(process.env, {
12864
13389
  GEMINI_API_KEY: opts?.geminiKey,
@@ -12973,7 +13498,7 @@ async function initCommand(opts) {
12973
13498
  const rawApiKey = `cnry_${crypto2.randomBytes(16).toString("hex")}`;
12974
13499
  const keyHash = crypto2.createHash("sha256").update(rawApiKey).digest("hex");
12975
13500
  const keyPrefix = rawApiKey.slice(0, 9);
12976
- const databasePath = path10.join(configDir, "data.db");
13501
+ const databasePath = path11.join(configDir, "data.db");
12977
13502
  const db = createClient(databasePath);
12978
13503
  migrate(db);
12979
13504
  db.insert(apiKeys).values({
@@ -13159,8 +13684,8 @@ function encodeLegacySetupState(state) {
13159
13684
  }
13160
13685
 
13161
13686
  // src/agent-plugin.ts
13162
- import fs14 from "fs";
13163
- import path11 from "path";
13687
+ import fs15 from "fs";
13688
+ import path12 from "path";
13164
13689
  var CANONRY_PLUGIN_ID = "canonry@canonry";
13165
13690
  var REQUIRED_SKILLS = ["canonry", "aero"];
13166
13691
  var AGENT_PLUGINS_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
@@ -13204,7 +13729,7 @@ function isCanonryPluginId(value) {
13204
13729
  }
13205
13730
  function readJson(filePath) {
13206
13731
  try {
13207
- return JSON.parse(fs14.readFileSync(filePath, "utf8"));
13732
+ return JSON.parse(fs15.readFileSync(filePath, "utf8"));
13208
13733
  } catch {
13209
13734
  return null;
13210
13735
  }
@@ -13223,7 +13748,7 @@ function readCodexPluginConfigured(filePath) {
13223
13748
  try {
13224
13749
  let currentPlugin = null;
13225
13750
  let inPluginsTable = false;
13226
- for (const line of fs14.readFileSync(filePath, "utf8").split(/\r?\n/)) {
13751
+ for (const line of fs15.readFileSync(filePath, "utf8").split(/\r?\n/)) {
13227
13752
  const section = /^\s*\[plugins\.(?:"([^"]+)"|'([^']+)'|(\S+))\]\s*(?:#.*)?$/.exec(line);
13228
13753
  if (section) {
13229
13754
  const captures = section.slice(1, 4);
@@ -13256,21 +13781,21 @@ function readCodexPluginConfigured(filePath) {
13256
13781
  }
13257
13782
  }
13258
13783
  function verifiedPluginVersion(root, client) {
13259
- if (client === "codex" && fs14.existsSync(path11.join(root, "plugin.json"))) {
13260
- const manifest2 = readJson(path11.join(root, "plugin.json"));
13784
+ if (client === "codex" && fs15.existsSync(path12.join(root, "plugin.json"))) {
13785
+ const manifest2 = readJson(path12.join(root, "plugin.json"));
13261
13786
  if (manifest2 && typeof manifest2 === "object") {
13262
13787
  const plugin2 = manifest2;
13263
13788
  if (plugin2.$schema === AGENT_PLUGINS_SCHEMA) {
13264
13789
  if (plugin2.name !== "canonry" || typeof plugin2.version !== "string" || !parseSemver(plugin2.version)) {
13265
13790
  return null;
13266
13791
  }
13267
- const mcp2 = readJson(path11.join(root, "mcp.json"));
13792
+ const mcp2 = readJson(path12.join(root, "mcp.json"));
13268
13793
  if (!mcp2 || typeof mcp2 !== "object") return null;
13269
13794
  const portableMcp = mcp2;
13270
13795
  if (portableMcp.$schema !== AGENT_PLUGINS_MCP_SCHEMA || portableMcp.mcpServers?.canonry?.type !== "stdio" || portableMcp.mcpServers.canonry.command !== "canonry-mcp") {
13271
13796
  return null;
13272
13797
  }
13273
- return REQUIRED_SKILLS.every((name) => fs14.existsSync(path11.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
13798
+ return REQUIRED_SKILLS.every((name) => fs15.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
13274
13799
  }
13275
13800
  if (typeof plugin2.$schema === "string" && plugin2.$schema.startsWith("https://agent-plugins.org/schemas/")) {
13276
13801
  return null;
@@ -13278,20 +13803,20 @@ function verifiedPluginVersion(root, client) {
13278
13803
  }
13279
13804
  }
13280
13805
  const manifestDir = client === "claude-code" ? ".claude-plugin" : ".codex-plugin";
13281
- const manifest = readJson(path11.join(root, manifestDir, "plugin.json"));
13806
+ const manifest = readJson(path12.join(root, manifestDir, "plugin.json"));
13282
13807
  if (!manifest || typeof manifest !== "object") return null;
13283
13808
  const plugin = manifest;
13284
13809
  if (plugin.name !== "canonry" || typeof plugin.version !== "string" || !parseSemver(plugin.version) || plugin.skills !== "./skills/" || plugin.mcpServers !== "./.mcp.json") {
13285
13810
  return null;
13286
13811
  }
13287
- const mcp = readJson(path11.join(root, ".mcp.json"));
13812
+ const mcp = readJson(path12.join(root, ".mcp.json"));
13288
13813
  if (!mcp || typeof mcp !== "object") return null;
13289
13814
  const canonryServer = mcp.mcpServers?.canonry;
13290
13815
  if (canonryServer?.command !== "canonry-mcp") return null;
13291
- return REQUIRED_SKILLS.every((name) => fs14.existsSync(path11.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
13816
+ return REQUIRED_SKILLS.every((name) => fs15.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
13292
13817
  }
13293
13818
  function readClaudeUserInstallPaths(claudeConfigDir) {
13294
- const parsed = readJson(path11.join(claudeConfigDir, "plugins", "installed_plugins.json"));
13819
+ const parsed = readJson(path12.join(claudeConfigDir, "plugins", "installed_plugins.json"));
13295
13820
  if (!parsed || typeof parsed !== "object") return [];
13296
13821
  const plugins = parsed.plugins;
13297
13822
  if (!plugins || typeof plugins !== "object") return [];
@@ -13305,7 +13830,7 @@ function readClaudeUserInstallPaths(claudeConfigDir) {
13305
13830
  }
13306
13831
  function listDirectories(dir) {
13307
13832
  try {
13308
- return fs14.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path11.join(dir, entry.name));
13833
+ return fs15.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path12.join(dir, entry.name));
13309
13834
  } catch {
13310
13835
  return [];
13311
13836
  }
@@ -13317,8 +13842,8 @@ function verifiedClaudeVersion(claudeConfigDir) {
13317
13842
  return newestVersion(readClaudeUserInstallPaths(claudeConfigDir).map((installPath) => verifiedPluginVersion(installPath, "claude-code")).filter((version) => version !== null));
13318
13843
  }
13319
13844
  function verifiedCodexVersion(codexHome) {
13320
- const pluginCache = path11.join(codexHome, "plugins", "cache", "canonry", "canonry");
13321
- const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version: path11.basename(installPath) })).filter((candidate) => parseSemver(candidate.version) !== null).sort((left, right) => compareSemver(left.version, right.version));
13845
+ const pluginCache = path12.join(codexHome, "plugins", "cache", "canonry", "canonry");
13846
+ const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version: path12.basename(installPath) })).filter((candidate) => parseSemver(candidate.version) !== null).sort((left, right) => compareSemver(left.version, right.version));
13322
13847
  const active = versionedRoots.at(-1);
13323
13848
  if (!active) return verifiedPluginVersion(pluginCache, "codex");
13324
13849
  const manifestVersion = verifiedPluginVersion(active.installPath, "codex");
@@ -13326,12 +13851,12 @@ function verifiedCodexVersion(codexHome) {
13326
13851
  }
13327
13852
  function detectCanonryAgentPlugin(opts = {}) {
13328
13853
  const home = opts.home?.trim() || null;
13329
- const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ? path11.join(home, ".claude") : null);
13330
- const codexHome = opts.codexHome?.trim() || (home ? path11.join(home, ".codex") : null);
13854
+ const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ? path12.join(home, ".claude") : null);
13855
+ const codexHome = opts.codexHome?.trim() || (home ? path12.join(home, ".codex") : null);
13331
13856
  const configuredClients = [];
13332
13857
  const verifiedClients = [];
13333
13858
  const verifiedClientVersions = {};
13334
- const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(path11.join(claudeConfigDir, "settings.json")) : false;
13859
+ const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(path12.join(claudeConfigDir, "settings.json")) : false;
13335
13860
  if (claudeConfigured) {
13336
13861
  configuredClients.push("claude-code");
13337
13862
  const version = claudeConfigDir ? verifiedClaudeVersion(claudeConfigDir) : null;
@@ -13340,7 +13865,7 @@ function detectCanonryAgentPlugin(opts = {}) {
13340
13865
  verifiedClientVersions["claude-code"] = version;
13341
13866
  }
13342
13867
  }
13343
- const codexConfigured = codexHome ? readCodexPluginConfigured(path11.join(codexHome, "config.toml")) : false;
13868
+ const codexConfigured = codexHome ? readCodexPluginConfigured(path12.join(codexHome, "config.toml")) : false;
13344
13869
  if (codexConfigured) {
13345
13870
  configuredClients.push("codex");
13346
13871
  const version = codexHome ? verifiedCodexVersion(codexHome) : null;
@@ -14603,14 +15128,14 @@ Usage: ${usage}`, {
14603
15128
  }
14604
15129
  }
14605
15130
  ];
14606
- function siteHealthAlias(sourcePath, path12, usage) {
15131
+ function siteHealthAlias(sourcePath, path13, usage) {
14607
15132
  const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
14608
15133
  (candidate) => candidate.path.join(" ") === sourcePath.join(" ")
14609
15134
  );
14610
15135
  if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
14611
15136
  return {
14612
15137
  ...source,
14613
- path: path12,
15138
+ path: path13,
14614
15139
  usage,
14615
15140
  run: async (input) => {
14616
15141
  try {
@@ -14621,7 +15146,7 @@ function siteHealthAlias(sourcePath, path12, usage) {
14621
15146
  throw usageError(`${firstLine}
14622
15147
  Usage: ${usage}`, {
14623
15148
  message: error.message,
14624
- details: { ...error.details, command: path12.join("."), usage }
15149
+ details: { ...error.details, command: path13.join("."), usage }
14625
15150
  });
14626
15151
  }
14627
15152
  }
@@ -14920,7 +15445,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
14920
15445
  ];
14921
15446
 
14922
15447
  // src/cli-commands/wordpress.ts
14923
- import fs15 from "fs";
15448
+ import fs16 from "fs";
14924
15449
 
14925
15450
  // src/commands/wordpress.ts
14926
15451
  function getClient28() {
@@ -15159,12 +15684,12 @@ async function wordpressSetMeta(project, body) {
15159
15684
  printPageDetail(result);
15160
15685
  }
15161
15686
  async function wordpressBulkSetMeta(project, opts) {
15162
- const fs17 = await import("fs/promises");
15163
- const path12 = await import("path");
15164
- const filePath = path12.resolve(opts.from);
15687
+ const fs18 = await import("fs/promises");
15688
+ const path13 = await import("path");
15689
+ const filePath = path13.resolve(opts.from);
15165
15690
  let raw;
15166
15691
  try {
15167
- raw = await fs17.readFile(filePath, "utf8");
15692
+ raw = await fs18.readFile(filePath, "utf8");
15168
15693
  } catch {
15169
15694
  throw new CliError({
15170
15695
  code: "FILE_READ_ERROR",
@@ -15261,13 +15786,13 @@ async function wordpressSetSchema(project, body) {
15261
15786
  printManualAssist(`Schema update for "${body.slug}"`, result);
15262
15787
  }
15263
15788
  async function wordpressSchemaDeploy(project, opts) {
15264
- const fs17 = await import("fs/promises");
15265
- const path12 = await import("path");
15789
+ const fs18 = await import("fs/promises");
15790
+ const path13 = await import("path");
15266
15791
  const yaml = await loadYamlModule();
15267
- const filePath = path12.resolve(opts.profile);
15792
+ const filePath = path13.resolve(opts.profile);
15268
15793
  let raw;
15269
15794
  try {
15270
- raw = await fs17.readFile(filePath, "utf8");
15795
+ raw = await fs18.readFile(filePath, "utf8");
15271
15796
  } catch {
15272
15797
  throw new CliError({
15273
15798
  code: "FILE_READ_ERROR",
@@ -15372,13 +15897,13 @@ async function wordpressOnboard(project, opts) {
15372
15897
  }
15373
15898
  let profileData;
15374
15899
  if (opts.profile) {
15375
- const fs17 = await import("fs/promises");
15376
- const path12 = await import("path");
15900
+ const fs18 = await import("fs/promises");
15901
+ const path13 = await import("path");
15377
15902
  const yaml = await loadYamlModule();
15378
- const filePath = path12.resolve(opts.profile);
15903
+ const filePath = path13.resolve(opts.profile);
15379
15904
  let raw;
15380
15905
  try {
15381
- raw = await fs17.readFile(filePath, "utf8");
15906
+ raw = await fs18.readFile(filePath, "utf8");
15382
15907
  } catch {
15383
15908
  throw new CliError({
15384
15909
  code: "FILE_READ_ERROR",
@@ -15527,7 +16052,7 @@ function resolveContent(input, command, usage, options) {
15527
16052
  }
15528
16053
  if (contentFile) {
15529
16054
  try {
15530
- return fs15.readFileSync(contentFile, "utf-8");
16055
+ return fs16.readFileSync(contentFile, "utf-8");
15531
16056
  } catch (error) {
15532
16057
  const message = error instanceof Error ? error.message : String(error);
15533
16058
  throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
@@ -16494,16 +17019,16 @@ Usage: ${usage}`, {
16494
17019
 
16495
17020
  // src/commands/measurement-plan.ts
16496
17021
  import crypto3 from "crypto";
16497
- import fs16 from "fs";
17022
+ import fs17 from "fs";
16498
17023
  import { parse } from "yaml";
16499
17024
  import { z as z2 } from "zod";
16500
17025
  function readPlan(source) {
16501
- const content = source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
17026
+ const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
16502
17027
  const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
16503
17028
  return measurementPlanInputSchema.parse(parsed);
16504
17029
  }
16505
17030
  function readDiscoveryRule(source) {
16506
- const content = source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
17031
+ const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
16507
17032
  const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
16508
17033
  return measurementDiscoveryRuleSchema.parse(parsed);
16509
17034
  }
@@ -16552,7 +17077,7 @@ var advancedDeactivateInputSchema = z2.object({
16552
17077
  }).strict();
16553
17078
  function readAdvancedMeasurementInput(source) {
16554
17079
  if (source === void 0) return {};
16555
- const content = source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
17080
+ const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
16556
17081
  return JSON.parse(content);
16557
17082
  }
16558
17083
  var advancedMeasurementCollectionKeys = {
@@ -16734,7 +17259,7 @@ async function replaceMeasurementPlanAssignments(project, opts) {
16734
17259
  console.log(JSON.stringify({ preview, result }, null, 2));
16735
17260
  }
16736
17261
  function readGroupMembershipCsv(source) {
16737
- return source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
17262
+ return source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
16738
17263
  }
16739
17264
  async function previewMeasurementPlanGroups(project, source) {
16740
17265
  const preview = await createApiClient().previewMeasurementDraftGroupMembership(project, {