@ainyc/canonry 4.142.0 → 4.144.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/assets/agent-workspace/skills/canonry/references/canonry-cli.md +33 -0
  2. package/assets/assets/{AuditHistoryPanel-CfaF5B_e.js → AuditHistoryPanel-DxRP9WsD.js} +1 -1
  3. package/assets/assets/{BacklinksPage-BIytWBpL.js → BacklinksPage-yiEbyV67.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-0tQT6GE7.js → ChartPrimitives-D2hRlMHn.js} +1 -1
  5. package/assets/assets/{HistoryPage-Bn6IH8WV.js → HistoryPage-CW6MJ-dR.js} +1 -1
  6. package/assets/assets/ProjectPage-Bnr7I5qu.js +9 -0
  7. package/assets/assets/{RunRow-RHd8Bd52.js → RunRow-Dr607W0P.js} +1 -1
  8. package/assets/assets/{RunsPage-BsDqqQFX.js → RunsPage-CtF3P7CA.js} +1 -1
  9. package/assets/assets/SettingsPage-3-uzs2lp.js +1 -0
  10. package/assets/assets/{TrafficPage-I19b7i9m.js → TrafficPage-Bww4KUn8.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-DOLEAPsc.js → TrafficSourceDetailPage-DiHxUH2H.js} +1 -1
  12. package/assets/assets/{arrow-left-BG9o86__.js → arrow-left-D-PcVbQ4.js} +1 -1
  13. package/assets/assets/{extract-error-message-D0cgM2XG.js → extract-error-message-BRq0yLWg.js} +1 -1
  14. package/assets/assets/index-ANFzZ3nU.css +1 -0
  15. package/assets/assets/index-b5M4QpB0.js +213 -0
  16. package/assets/assets/{trash-2-C_THWxO_.js → trash-2-B6aJTnJl.js} +1 -1
  17. package/assets/index.html +2 -2
  18. package/dist/{chunk-UFPP5CSN.js → chunk-3NYTF3YC.js} +346 -97
  19. package/dist/{chunk-NR6OGEV3.js → chunk-7D2GZU6O.js} +8174 -1431
  20. package/dist/{chunk-LS6SEZVF.js → chunk-WADGZMWT.js} +5457 -3192
  21. package/dist/{chunk-D7NJUGNQ.js → chunk-Z672VQ7G.js} +363 -2
  22. package/dist/cli.js +534 -164
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-EOUP37XN.js → intelligence-service-OLIGR6HE.js} +2 -2
  25. package/dist/mcp.js +2 -2
  26. package/package.json +7 -7
  27. package/assets/assets/ProjectPage-DsQic9wX.js +0 -8
  28. package/assets/assets/SettingsPage-CX2LkFDB.js +0 -1
  29. package/assets/assets/index-BDo9nl4O.css +0 -1
  30. package/assets/assets/index-CYGBt2f0.js +0 -213
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  showFirstRunNotice,
31
31
  trackCliCommandFinished,
32
32
  trackEvent
33
- } from "./chunk-UFPP5CSN.js";
33
+ } from "./chunk-3NYTF3YC.js";
34
34
  import {
35
35
  CliError,
36
36
  EXIT_SYSTEM_ERROR,
@@ -47,7 +47,7 @@ import {
47
47
  saveConfig,
48
48
  saveConfigPatch,
49
49
  usageError
50
- } from "./chunk-D7NJUGNQ.js";
50
+ } from "./chunk-Z672VQ7G.js";
51
51
  import {
52
52
  apiKeys,
53
53
  createClient,
@@ -55,7 +55,7 @@ import {
55
55
  projects,
56
56
  queries,
57
57
  renderReportHtml
58
- } from "./chunk-NR6OGEV3.js";
58
+ } from "./chunk-7D2GZU6O.js";
59
59
  import {
60
60
  AdsDeliverySnapshotStatuses,
61
61
  AdsHistoricalCampaignRollupStatuses,
@@ -74,6 +74,8 @@ import {
74
74
  RunStatuses,
75
75
  TrafficEventKinds,
76
76
  TrafficSeriesGranularities,
77
+ USER_PASSWORD_MIN_LENGTH,
78
+ UserRoles,
77
79
  adsActivateTreeRequestSchema,
78
80
  adsActivationGrantCreateRequestSchema,
79
81
  adsAdCreateRequestSchema,
@@ -96,12 +98,15 @@ import {
96
98
  formatMicros,
97
99
  formatRatio,
98
100
  formatRunErrorOneLine,
101
+ measurementDiscoveryRequestSchema,
102
+ measurementDiscoveryRuleSchema,
103
+ measurementPlanInputSchema,
99
104
  normalizeProjectAliases,
100
105
  notificationEventSchema,
101
106
  providerQuotaPolicySchema,
102
107
  resolveProviderInput,
103
108
  winnabilityClassSchema
104
- } from "./chunk-LS6SEZVF.js";
109
+ } from "./chunk-WADGZMWT.js";
105
110
 
106
111
  // src/cli.ts
107
112
  import { pathToFileURL } from "url";
@@ -3847,9 +3852,9 @@ async function gaConnect(project, opts) {
3847
3852
  propertyId: opts.propertyId
3848
3853
  };
3849
3854
  if (opts.keyFile) {
3850
- const fs16 = await import("fs");
3855
+ const fs17 = await import("fs");
3851
3856
  try {
3852
- const content = fs16.readFileSync(opts.keyFile, "utf-8");
3857
+ const content = fs17.readFileSync(opts.keyFile, "utf-8");
3853
3858
  JSON.parse(content);
3854
3859
  body.keyJson = content;
3855
3860
  } catch (e) {
@@ -5298,9 +5303,9 @@ async function trafficConnectWordpress(project, opts) {
5298
5303
  }
5299
5304
  let applicationPassword = opts.appPassword?.trim() ?? "";
5300
5305
  if (!applicationPassword && opts.appPasswordFile) {
5301
- const fs16 = await import("fs");
5306
+ const fs17 = await import("fs");
5302
5307
  try {
5303
- applicationPassword = fs16.readFileSync(opts.appPasswordFile, "utf-8").trim();
5308
+ applicationPassword = fs17.readFileSync(opts.appPasswordFile, "utf-8").trim();
5304
5309
  } catch (e) {
5305
5310
  const msg = e instanceof Error ? e.message : String(e);
5306
5311
  throw new CliError({
@@ -5356,10 +5361,10 @@ async function trafficConnectCloudRun(project, opts) {
5356
5361
  details: { project }
5357
5362
  });
5358
5363
  }
5359
- const fs16 = await import("fs");
5364
+ const fs17 = await import("fs");
5360
5365
  let keyJson;
5361
5366
  try {
5362
- keyJson = fs16.readFileSync(opts.serviceAccountKey, "utf-8");
5367
+ keyJson = fs17.readFileSync(opts.serviceAccountKey, "utf-8");
5363
5368
  JSON.parse(keyJson);
5364
5369
  } catch (e) {
5365
5370
  const msg = e instanceof Error ? e.message : String(e);
@@ -5419,9 +5424,9 @@ async function trafficConnectVercel(project, opts) {
5419
5424
  }
5420
5425
  let token = opts.token?.trim() ?? "";
5421
5426
  if (!token && opts.tokenFile) {
5422
- const fs16 = await import("fs");
5427
+ const fs17 = await import("fs");
5423
5428
  try {
5424
- token = fs16.readFileSync(opts.tokenFile, "utf-8").trim();
5429
+ token = fs17.readFileSync(opts.tokenFile, "utf-8").trim();
5425
5430
  } catch (e) {
5426
5431
  const msg = e instanceof Error ? e.message : String(e);
5427
5432
  throw new CliError({
@@ -7546,13 +7551,224 @@ var KEYS_CLI_COMMANDS = [
7546
7551
  }
7547
7552
  ];
7548
7553
 
7554
+ // src/cli-prompt.ts
7555
+ import readline from "readline";
7556
+ async function promptHiddenInput(question) {
7557
+ const input = process.stdin;
7558
+ const output = process.stdout;
7559
+ if (!input.isTTY) {
7560
+ const rl2 = readline.createInterface({ input, output });
7561
+ try {
7562
+ return await new Promise((resolve) => {
7563
+ rl2.question(question, resolve);
7564
+ });
7565
+ } finally {
7566
+ rl2.close();
7567
+ }
7568
+ }
7569
+ const rl = readline.createInterface({ input, output, terminal: true });
7570
+ const mutable = rl;
7571
+ const originalWrite = mutable._writeToOutput?.bind(rl);
7572
+ let muted = false;
7573
+ mutable._writeToOutput = (chunk) => {
7574
+ if (muted) {
7575
+ output.write("");
7576
+ return;
7577
+ }
7578
+ originalWrite?.(chunk);
7579
+ };
7580
+ try {
7581
+ return await new Promise((resolve) => {
7582
+ rl.question(question, (answer) => {
7583
+ output.write("\n");
7584
+ resolve(answer);
7585
+ });
7586
+ muted = true;
7587
+ });
7588
+ } finally {
7589
+ muted = false;
7590
+ if (originalWrite) mutable._writeToOutput = originalWrite;
7591
+ rl.close();
7592
+ }
7593
+ }
7594
+ async function readAllStdin() {
7595
+ const chunks = [];
7596
+ for await (const chunk of process.stdin) {
7597
+ chunks.push(Buffer.from(chunk));
7598
+ }
7599
+ return Buffer.concat(chunks).toString("utf8");
7600
+ }
7601
+
7602
+ // src/commands/users.ts
7603
+ function getClient13() {
7604
+ return createApiClient();
7605
+ }
7606
+ var ROLE_NAMES = Object.values(UserRoles);
7607
+ function parseRole(role) {
7608
+ const normalized = role.trim().toLowerCase();
7609
+ if (ROLE_NAMES.includes(normalized)) return normalized;
7610
+ throw new CliError({
7611
+ code: "CLI_USAGE_ERROR",
7612
+ message: `Unknown role "${role}" \u2014 choose ${ROLE_NAMES.join(" or ")}`,
7613
+ displayMessage: `Error: unknown role "${role}". Choose ${ROLE_NAMES.join(" or ")}.`
7614
+ });
7615
+ }
7616
+ async function listUsers(format) {
7617
+ const client = getClient13();
7618
+ const { users } = await client.listUsers();
7619
+ if (format === "json") {
7620
+ console.log(JSON.stringify({ users }, null, 2));
7621
+ return;
7622
+ } else if (format === "jsonl") {
7623
+ emitJsonl(users);
7624
+ return;
7625
+ }
7626
+ if (users.length === 0) {
7627
+ console.log("No accounts yet \u2014 the dashboard opens without a sign-in.");
7628
+ console.log("Create the first one with: canonry user create --name <name> --role admin");
7629
+ return;
7630
+ }
7631
+ console.log(`${"NAME".padEnd(24)} ${"ROLE".padEnd(8)} ${"CREATED".padEnd(12)} LAST SIGN-IN`);
7632
+ for (const user of users) {
7633
+ const lastLogin = user.lastLoginAt ? formatIsoDate(user.lastLoginAt) : "never";
7634
+ console.log(
7635
+ `${user.name.padEnd(24)} ${user.role.padEnd(8)} ${formatIsoDate(user.createdAt).padEnd(12)} ${lastLogin}`
7636
+ );
7637
+ }
7638
+ }
7639
+ async function createUser(opts) {
7640
+ const role = parseRole(opts.role);
7641
+ const password = opts.passwordStdin ? (await (opts.readStdin ?? readAllStdin)()).replace(/\r?\n$/, "") : await askForPassword();
7642
+ if (password.length < USER_PASSWORD_MIN_LENGTH) {
7643
+ throw new CliError({
7644
+ code: "CLI_USAGE_ERROR",
7645
+ message: "Password too short",
7646
+ displayMessage: `Error: the password must be at least ${USER_PASSWORD_MIN_LENGTH} characters.`
7647
+ });
7648
+ }
7649
+ const client = getClient13();
7650
+ const wasFirst = await isFirstAccount(client);
7651
+ const created = await client.createUser({ name: opts.name, role, password });
7652
+ if (isMachineFormat(opts.format)) {
7653
+ console.log(JSON.stringify(created, null, 2));
7654
+ return;
7655
+ }
7656
+ printCreated(created, wasFirst);
7657
+ }
7658
+ async function askForPassword() {
7659
+ const first = await promptHiddenInput("Password: ");
7660
+ const second = await promptHiddenInput("Confirm password: ");
7661
+ if (first !== second) {
7662
+ throw new CliError({
7663
+ code: "CLI_USAGE_ERROR",
7664
+ message: "Passwords do not match",
7665
+ displayMessage: "Error: the two passwords do not match. Nothing was created."
7666
+ });
7667
+ }
7668
+ return first;
7669
+ }
7670
+ async function isFirstAccount(client) {
7671
+ try {
7672
+ const { users } = await client.listUsers();
7673
+ return users.length === 0;
7674
+ } catch {
7675
+ return false;
7676
+ }
7677
+ }
7678
+ function printCreated(created, wasFirst) {
7679
+ console.log(`Account "${created.name}" created with ${created.role} access.
7680
+ `);
7681
+ if (wasFirst) {
7682
+ console.log("This is the first account on this install, so the dashboard now asks");
7683
+ console.log("everyone to sign in. API keys are unaffected and keep working as before.\n");
7684
+ }
7685
+ if (created.role === UserRoles.viewer) {
7686
+ console.log("A viewer can read everything and change nothing.");
7687
+ } else {
7688
+ console.log("An administrator can do everything the install could already do.");
7689
+ }
7690
+ }
7691
+ async function deleteUser(name, format) {
7692
+ const client = getClient13();
7693
+ const result = await client.deleteUser(name);
7694
+ if (isMachineFormat(format)) {
7695
+ console.log(JSON.stringify(result, null, 2));
7696
+ return;
7697
+ }
7698
+ console.log(`Account "${result.name}" deleted. Any browser signed in as them is signed out now.`);
7699
+ }
7700
+
7701
+ // src/cli-commands/users.ts
7702
+ var CREATE_USAGE = "canonry user create --name <name> --role <admin|viewer> [--password-stdin] [--format json]";
7703
+ var USERS_CLI_COMMANDS = [
7704
+ {
7705
+ path: ["user", "list"],
7706
+ usage: "canonry user list [--format json|jsonl]",
7707
+ run: async (input) => {
7708
+ await listUsers(input.format);
7709
+ }
7710
+ },
7711
+ {
7712
+ path: ["user", "create"],
7713
+ usage: CREATE_USAGE,
7714
+ options: {
7715
+ name: stringOption(),
7716
+ role: stringOption(),
7717
+ // There is deliberately no `--password` flag: a password given on the
7718
+ // command line lands in the shell history and the process list.
7719
+ "password-stdin": { type: "boolean" }
7720
+ },
7721
+ run: async (input) => {
7722
+ const name = requireStringOption(input, "name", {
7723
+ command: "user.create",
7724
+ usage: CREATE_USAGE,
7725
+ message: "--name is required"
7726
+ });
7727
+ const role = requireStringOption(input, "role", {
7728
+ command: "user.create",
7729
+ usage: CREATE_USAGE,
7730
+ message: "--role is required (admin or viewer)"
7731
+ });
7732
+ await createUser({
7733
+ name,
7734
+ role,
7735
+ passwordStdin: getBoolean(input.values, "password-stdin"),
7736
+ format: input.format
7737
+ });
7738
+ }
7739
+ },
7740
+ {
7741
+ path: ["user", "delete"],
7742
+ usage: "canonry user delete <name> [--format json]",
7743
+ run: async (input) => {
7744
+ const name = requirePositional(input, 0, {
7745
+ command: "user.delete",
7746
+ usage: "canonry user delete <name> [--format json]",
7747
+ message: "Account name is required"
7748
+ });
7749
+ await deleteUser(name, input.format);
7750
+ }
7751
+ },
7752
+ {
7753
+ path: ["user"],
7754
+ usage: "canonry user <list|create|delete>",
7755
+ run: async (input) => {
7756
+ unknownSubcommand(input.positionals[0], {
7757
+ command: "user",
7758
+ usage: "canonry user <list|create|delete>",
7759
+ available: ["list", "create", "delete"]
7760
+ });
7761
+ }
7762
+ }
7763
+ ];
7764
+
7549
7765
  // src/commands/keyword.ts
7550
7766
  import fs4 from "fs";
7551
- function getClient13() {
7767
+ function getClient14() {
7552
7768
  return createApiClient();
7553
7769
  }
7554
7770
  async function addKeywords(project, keywords, format) {
7555
- const client = getClient13();
7771
+ const client = getClient14();
7556
7772
  await client.appendKeywords(project, keywords);
7557
7773
  if (isMachineFormat(format)) {
7558
7774
  console.log(JSON.stringify({
@@ -7565,7 +7781,7 @@ async function addKeywords(project, keywords, format) {
7565
7781
  console.log(`Added ${keywords.length} key phrase(s) to "${project}".`);
7566
7782
  }
7567
7783
  async function replaceKeywords(project, keywords, format) {
7568
- const client = getClient13();
7784
+ const client = getClient14();
7569
7785
  await client.putKeywords(project, keywords);
7570
7786
  if (isMachineFormat(format)) {
7571
7787
  console.log(JSON.stringify({
@@ -7578,7 +7794,7 @@ async function replaceKeywords(project, keywords, format) {
7578
7794
  console.log(`Set ${keywords.length} key phrase(s) for "${project}".`);
7579
7795
  }
7580
7796
  async function removeKeywords(project, keywords, format) {
7581
- const client = getClient13();
7797
+ const client = getClient14();
7582
7798
  const existing = await client.listKeywords(project);
7583
7799
  const existingSet = new Set(existing.map((k) => k.keyword));
7584
7800
  const removedKeywords = keywords.filter((k) => existingSet.has(k));
@@ -7595,7 +7811,7 @@ async function removeKeywords(project, keywords, format) {
7595
7811
  console.log(`Removed ${removedKeywords.length} key phrase(s) from "${project}".`);
7596
7812
  }
7597
7813
  async function listKeywords(project, format) {
7598
- const client = getClient13();
7814
+ const client = getClient14();
7599
7815
  const kws = await client.listKeywords(project);
7600
7816
  if (format === "json") {
7601
7817
  console.log(JSON.stringify(kws, null, 2));
@@ -7641,7 +7857,7 @@ async function importKeywords(project, filePath, format) {
7641
7857
  console.log("No key phrases found in file.");
7642
7858
  return;
7643
7859
  }
7644
- const client = getClient13();
7860
+ const client = getClient14();
7645
7861
  await client.appendKeywords(project, keywords);
7646
7862
  if (isMachineFormat(format)) {
7647
7863
  console.log(JSON.stringify({
@@ -7655,7 +7871,7 @@ async function importKeywords(project, filePath, format) {
7655
7871
  console.log(`Imported ${keywords.length} key phrase(s) to "${project}".`);
7656
7872
  }
7657
7873
  async function generateKeywords(project, provider, opts) {
7658
- const client = getClient13();
7874
+ const client = getClient14();
7659
7875
  const result = await client.generateKeywords(project, provider, opts.count);
7660
7876
  const saved = Boolean(opts.save && result.keywords.length > 0);
7661
7877
  if (!isMachineFormat(opts.format)) {
@@ -7828,11 +8044,11 @@ var KEYWORD_CLI_COMMANDS = [
7828
8044
 
7829
8045
  // src/commands/query.ts
7830
8046
  import fs5 from "fs";
7831
- function getClient14() {
8047
+ function getClient15() {
7832
8048
  return createApiClient();
7833
8049
  }
7834
8050
  async function addQueries(project, queries2, format) {
7835
- const client = getClient14();
8051
+ const client = getClient15();
7836
8052
  await client.appendQueries(project, queries2);
7837
8053
  if (isMachineFormat(format)) {
7838
8054
  console.log(JSON.stringify({
@@ -7845,7 +8061,7 @@ async function addQueries(project, queries2, format) {
7845
8061
  console.log(`Added ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
7846
8062
  }
7847
8063
  async function replaceQueries(project, queries2, opts) {
7848
- const client = getClient14();
8064
+ const client = getClient15();
7849
8065
  const isJson = isMachineFormat(opts?.format);
7850
8066
  if (opts?.dryRun) {
7851
8067
  const preview = await client.previewReplaceQueries(project, queries2);
@@ -7881,7 +8097,7 @@ async function replaceQueries(project, queries2, opts) {
7881
8097
  console.log(`Set ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} for "${project}".`);
7882
8098
  }
7883
8099
  async function removeQueries(project, queries2, format) {
7884
- const client = getClient14();
8100
+ const client = getClient15();
7885
8101
  const existing = await client.listQueries(project);
7886
8102
  const existingSet = new Set(existing.map((q) => q.query));
7887
8103
  const removedQueries = queries2.filter((q) => existingSet.has(q));
@@ -7898,7 +8114,7 @@ async function removeQueries(project, queries2, format) {
7898
8114
  console.log(`Removed ${removedQueries.length} ${removedQueries.length === 1 ? "query" : "queries"} from "${project}".`);
7899
8115
  }
7900
8116
  async function listQueries(project, format) {
7901
- const client = getClient14();
8117
+ const client = getClient15();
7902
8118
  const qs = await client.listQueries(project);
7903
8119
  if (format === "json") {
7904
8120
  console.log(JSON.stringify(qs, null, 2));
@@ -7944,7 +8160,7 @@ async function importQueries(project, filePath, format) {
7944
8160
  console.log("No queries found in file.");
7945
8161
  return;
7946
8162
  }
7947
- const client = getClient14();
8163
+ const client = getClient15();
7948
8164
  await client.appendQueries(project, queries2);
7949
8165
  if (isMachineFormat(format)) {
7950
8166
  console.log(JSON.stringify({
@@ -7958,7 +8174,7 @@ async function importQueries(project, filePath, format) {
7958
8174
  console.log(`Imported ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
7959
8175
  }
7960
8176
  async function generateQueries(project, provider, opts) {
7961
- const client = getClient14();
8177
+ const client = getClient15();
7962
8178
  const result = await client.generateQueries(project, provider, opts.count);
7963
8179
  const saved = Boolean(opts.save && result.queries.length > 0);
7964
8180
  if (!isMachineFormat(opts.format)) {
@@ -8453,11 +8669,11 @@ var MCP_CLI_COMMANDS = [
8453
8669
  ];
8454
8670
 
8455
8671
  // src/commands/notify.ts
8456
- function getClient15() {
8672
+ function getClient16() {
8457
8673
  return createApiClient();
8458
8674
  }
8459
8675
  async function addNotification(project, opts) {
8460
- const client = getClient15();
8676
+ const client = getClient16();
8461
8677
  const result = await client.createNotification(project, {
8462
8678
  channel: "webhook",
8463
8679
  url: opts.webhook,
@@ -8471,7 +8687,7 @@ async function addNotification(project, opts) {
8471
8687
  printNotification(result);
8472
8688
  }
8473
8689
  async function listNotifications(project, format) {
8474
- const client = getClient15();
8690
+ const client = getClient16();
8475
8691
  const results = await client.listNotifications(project);
8476
8692
  if (format === "json") {
8477
8693
  console.log(JSON.stringify(results, null, 2));
@@ -8492,7 +8708,7 @@ async function listNotifications(project, format) {
8492
8708
  }
8493
8709
  }
8494
8710
  async function removeNotification(project, id, format) {
8495
- const client = getClient15();
8711
+ const client = getClient16();
8496
8712
  await client.deleteNotification(project, id);
8497
8713
  if (isMachineFormat(format)) {
8498
8714
  console.log(JSON.stringify({ project, id, removed: true }, null, 2));
@@ -8501,7 +8717,7 @@ async function removeNotification(project, id, format) {
8501
8717
  console.log(`Notification ${id} removed from "${project}"`);
8502
8718
  }
8503
8719
  async function testNotification(project, id, format) {
8504
- const client = getClient15();
8720
+ const client = getClient16();
8505
8721
  const result = await client.testNotification(project, id);
8506
8722
  if (isMachineFormat(format)) {
8507
8723
  console.log(JSON.stringify({ project, id, ...result }, null, 2));
@@ -8698,11 +8914,11 @@ async function applyConfigs(filePaths, format) {
8698
8914
  }
8699
8915
 
8700
8916
  // src/commands/analytics.ts
8701
- function getClient16() {
8917
+ function getClient17() {
8702
8918
  return createApiClient();
8703
8919
  }
8704
8920
  async function showAnalytics(project, options) {
8705
- const client = getClient16();
8921
+ const client = getClient17();
8706
8922
  const features = options.feature ? [options.feature] : ["metrics", "gaps", "sources"];
8707
8923
  const results = {};
8708
8924
  for (const feature of features) {
@@ -8905,7 +9121,7 @@ Source Origin Breakdown`);
8905
9121
  }
8906
9122
 
8907
9123
  // src/commands/sources.ts
8908
- function getClient17() {
9124
+ function getClient18() {
8909
9125
  return createApiClient();
8910
9126
  }
8911
9127
  var pct = (n) => `${(n * 100).toFixed(1)}%`;
@@ -8916,7 +9132,7 @@ async function showSources(project, options) {
8916
9132
  details: { command: "sources", option: "limit", value: options.limit }
8917
9133
  });
8918
9134
  }
8919
- const client = getClient17();
9135
+ const client = getClient18();
8920
9136
  const data = await client.getAnalyticsSources(project, { window: options.window, limit: options.limit });
8921
9137
  if (options.format === "jsonl") {
8922
9138
  emitJsonl(data.ranked.entries.map((e) => ({ project, ...e })));
@@ -8967,11 +9183,11 @@ function printRankedEntries(list, indent = "") {
8967
9183
  }
8968
9184
 
8969
9185
  // src/commands/evidence.ts
8970
- function getClient18() {
9186
+ function getClient19() {
8971
9187
  return createApiClient();
8972
9188
  }
8973
9189
  async function showEvidence(project, format) {
8974
- const client = getClient18();
9190
+ const client = getClient19();
8975
9191
  const timeline = await client.getTimeline(project);
8976
9192
  if (format === "json") {
8977
9193
  const enriched = timeline.map((entry) => ({
@@ -9054,7 +9270,7 @@ async function exportResults(project, opts) {
9054
9270
  }
9055
9271
 
9056
9272
  // src/commands/history.ts
9057
- function getClient19() {
9273
+ function getClient20() {
9058
9274
  return createApiClient();
9059
9275
  }
9060
9276
  function originLabel(entry) {
@@ -9065,7 +9281,7 @@ function originLabel(entry) {
9065
9281
  return entry.actor;
9066
9282
  }
9067
9283
  async function showHistory(project, format, opts = {}) {
9068
- const client = getClient19();
9284
+ const client = getClient20();
9069
9285
  try {
9070
9286
  const entries = project ? await client.getHistory(project, opts) : await client.getGlobalHistory(opts);
9071
9287
  if (format === "json") {
@@ -9106,11 +9322,11 @@ async function showHistory(project, format, opts = {}) {
9106
9322
  }
9107
9323
 
9108
9324
  // src/commands/status.ts
9109
- function getClient20() {
9325
+ function getClient21() {
9110
9326
  return createApiClient();
9111
9327
  }
9112
9328
  async function showStatus(project, format) {
9113
- const client = getClient20();
9329
+ const client = getClient21();
9114
9330
  const projectData = await client.getProject(project);
9115
9331
  const latest = await getLatestRunSummary(client, project);
9116
9332
  if (isMachineFormat(format)) {
@@ -9309,11 +9525,11 @@ var OPERATOR_CLI_COMMANDS = [
9309
9525
  ];
9310
9526
 
9311
9527
  // src/commands/project.ts
9312
- function getClient21() {
9528
+ function getClient22() {
9313
9529
  return createApiClient();
9314
9530
  }
9315
9531
  async function createProject(name, opts) {
9316
- const client = getClient21();
9532
+ const client = getClient22();
9317
9533
  const result = await client.putProject(name, {
9318
9534
  displayName: opts.displayName,
9319
9535
  canonicalDomain: opts.domain,
@@ -9331,7 +9547,7 @@ async function createProject(name, opts) {
9331
9547
  console.log(`Project created: ${result.name} (${result.id})`);
9332
9548
  }
9333
9549
  async function listProjects(format) {
9334
- const client = getClient21();
9550
+ const client = getClient22();
9335
9551
  const projects2 = await client.listProjects();
9336
9552
  if (format === "json") {
9337
9553
  console.log(JSON.stringify(projects2, null, 2));
@@ -9363,7 +9579,7 @@ async function listProjects(format) {
9363
9579
  }
9364
9580
  }
9365
9581
  async function showProject(name, format) {
9366
- const client = getClient21();
9582
+ const client = getClient22();
9367
9583
  const project = await client.getProject(name);
9368
9584
  if (isMachineFormat(format)) {
9369
9585
  console.log(JSON.stringify(project, null, 2));
@@ -9395,7 +9611,7 @@ async function showProject(name, format) {
9395
9611
  if (project.updatedAt) console.log(` Updated: ${project.updatedAt}`);
9396
9612
  }
9397
9613
  async function updateProjectSettings(name, opts) {
9398
- const client = getClient21();
9614
+ const client = getClient22();
9399
9615
  const project = await client.getProject(name);
9400
9616
  let ownedDomains = opts.ownedDomains ?? project.ownedDomains ?? [];
9401
9617
  if (opts.addOwnedDomain) {
@@ -9458,7 +9674,7 @@ async function updateProjectSettings(name, opts) {
9458
9674
  }
9459
9675
  }
9460
9676
  async function deleteProject(name, opts) {
9461
- const client = getClient21();
9677
+ const client = getClient22();
9462
9678
  const isJson = isMachineFormat(opts?.format);
9463
9679
  if (opts?.dryRun) {
9464
9680
  const preview = await client.previewProjectDelete(name);
@@ -9488,7 +9704,7 @@ async function deleteProject(name, opts) {
9488
9704
  console.log(`Project deleted: ${name}`);
9489
9705
  }
9490
9706
  async function addLocation(project, opts) {
9491
- const client = getClient21();
9707
+ const client = getClient22();
9492
9708
  const location = await client.addLocation(project, {
9493
9709
  label: opts.label,
9494
9710
  city: opts.city,
@@ -9503,7 +9719,7 @@ async function addLocation(project, opts) {
9503
9719
  console.log(`Location added: ${opts.label} (${opts.city}, ${opts.region}, ${opts.country})`);
9504
9720
  }
9505
9721
  async function listLocations(project, format) {
9506
- const client = getClient21();
9722
+ const client = getClient22();
9507
9723
  const result = await client.listLocations(project);
9508
9724
  if (format === "json") {
9509
9725
  console.log(JSON.stringify(result, null, 2));
@@ -9537,7 +9753,7 @@ async function listLocations(project, format) {
9537
9753
  }
9538
9754
  }
9539
9755
  async function removeLocation(project, label, format) {
9540
- const client = getClient21();
9756
+ const client = getClient22();
9541
9757
  await client.removeLocation(project, label);
9542
9758
  if (isMachineFormat(format)) {
9543
9759
  console.log(JSON.stringify({ project, label, removed: true }, null, 2));
@@ -9546,7 +9762,7 @@ async function removeLocation(project, label, format) {
9546
9762
  console.log(`Location removed: ${label}`);
9547
9763
  }
9548
9764
  async function setDefaultLocation(project, label, format) {
9549
- const client = getClient21();
9765
+ const client = getClient22();
9550
9766
  const result = await client.setDefaultLocation(project, label);
9551
9767
  if (isMachineFormat(format)) {
9552
9768
  console.log(JSON.stringify({ project, ...result }, null, 2));
@@ -9894,12 +10110,12 @@ var ORGANIC_EVIDENCE_CLI_COMMANDS = [
9894
10110
  ];
9895
10111
 
9896
10112
  // src/commands/run.ts
9897
- function getClient22() {
10113
+ function getClient23() {
9898
10114
  return createApiClient();
9899
10115
  }
9900
10116
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
9901
10117
  async function triggerRun(project, opts) {
9902
- const client = getClient22();
10118
+ const client = getClient23();
9903
10119
  const body = {};
9904
10120
  if (opts?.provider) {
9905
10121
  const providerInputs = opts.provider.split(",").map((s) => s.trim()).filter(Boolean);
@@ -9909,6 +10125,12 @@ async function triggerRun(project, opts) {
9909
10125
  if (opts?.queries?.length) {
9910
10126
  body.queries = opts.queries;
9911
10127
  }
10128
+ if (opts?.groups?.length || opts?.targets?.length) {
10129
+ body.measurementScope = {
10130
+ ...opts.groups?.length ? { groups: opts.groups } : {},
10131
+ ...opts.targets?.length ? { targets: opts.targets } : {}
10132
+ };
10133
+ }
9912
10134
  if (opts?.location) {
9913
10135
  body.location = opts.location;
9914
10136
  }
@@ -10001,7 +10223,7 @@ async function triggerRun(project, opts) {
10001
10223
  }
10002
10224
  }
10003
10225
  async function triggerRunAll(opts) {
10004
- const client = getClient22();
10226
+ const client = getClient23();
10005
10227
  const projects2 = await client.listProjects();
10006
10228
  if (projects2.length === 0) {
10007
10229
  if (isMachineFormat(opts?.format)) {
@@ -10084,7 +10306,7 @@ async function triggerRunAll(opts) {
10084
10306
  }
10085
10307
  }
10086
10308
  async function cancelRun(project, runId, format) {
10087
- const client = getClient22();
10309
+ const client = getClient23();
10088
10310
  let targetId = runId;
10089
10311
  if (!targetId) {
10090
10312
  const runs = await client.listRuns(project);
@@ -10116,7 +10338,7 @@ To cancel by ID : canonry run cancel ${project} <run-id>`,
10116
10338
  console.log(`Run ${result.id} cancelled.`);
10117
10339
  }
10118
10340
  async function showRun(id, format) {
10119
- const client = getClient22();
10341
+ const client = getClient23();
10120
10342
  const run = await client.getRun(id);
10121
10343
  if (isMachineFormat(format)) {
10122
10344
  console.log(JSON.stringify(run, null, 2));
@@ -10125,7 +10347,7 @@ async function showRun(id, format) {
10125
10347
  printRunDetail(run);
10126
10348
  }
10127
10349
  async function listRuns(project, opts) {
10128
- const client = getClient22();
10350
+ const client = getClient23();
10129
10351
  const runs = await client.listRuns(project, opts?.limit, opts?.kind);
10130
10352
  if (opts?.format === "json") {
10131
10353
  console.log(JSON.stringify(runs, null, 2));
@@ -10192,6 +10414,90 @@ function printRunDetail(run) {
10192
10414
  }
10193
10415
 
10194
10416
  // src/cli-commands/run.ts
10417
+ var RUN_TRIGGER_USAGE = "canonry run trigger <project> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--wait] [--format json]";
10418
+ var RUN_USAGE2 = "canonry run <project|--all> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--wait] [--format json]";
10419
+ var RUN_TRIGGER_OPTIONS = {
10420
+ provider: stringOption(),
10421
+ query: multiStringOption(),
10422
+ // Measure one slice of the project's published measurement plan. A group
10423
+ // stands for its member targets; repeat either flag to widen the slice.
10424
+ group: multiStringOption(),
10425
+ target: multiStringOption(),
10426
+ wait: { type: "boolean", default: false },
10427
+ all: { type: "boolean", default: false },
10428
+ location: stringOption(),
10429
+ "all-locations": { type: "boolean", default: false },
10430
+ "no-location": { type: "boolean", default: false },
10431
+ // Probe runs are operator/agent test runs — they write a snapshot
10432
+ // for inspection but are excluded from dashboard / analytics /
10433
+ // intelligence / notifications. Use when you want to verify a
10434
+ // provider works without polluting the project's metrics.
10435
+ probe: { type: "boolean", default: false }
10436
+ };
10437
+ async function triggerRunCommand(input, command) {
10438
+ const groups = getStringArray(input.values, "group") ?? [];
10439
+ const targets = getStringArray(input.values, "target") ?? [];
10440
+ const queries2 = getStringArray(input.values, "query") ?? [];
10441
+ const usage = command === "run" ? RUN_USAGE2 : RUN_TRIGGER_USAGE;
10442
+ if (getBoolean(input.values, "all")) {
10443
+ if (input.positionals.length > 0) {
10444
+ throw usageError("Error: --all cannot be combined with a project name", {
10445
+ message: "--all cannot be combined with a project name",
10446
+ details: {
10447
+ command,
10448
+ usage: "canonry run --all [--provider <name>] [--wait] [--format json]"
10449
+ }
10450
+ });
10451
+ }
10452
+ if (queries2.length) {
10453
+ throw usageError("Error: --query cannot be combined with --all (query scope is project-specific)", {
10454
+ message: "--query cannot be combined with --all (query scope is project-specific)",
10455
+ details: {
10456
+ command,
10457
+ usage: "canonry run <project> --query <q>... [--provider <name>] [--wait] [--format json]"
10458
+ }
10459
+ });
10460
+ }
10461
+ if (groups.length || targets.length) {
10462
+ throw usageError("Error: --group and --target cannot be combined with --all (a plan slice belongs to one project)", {
10463
+ message: "--group and --target cannot be combined with --all (a plan slice belongs to one project)",
10464
+ details: { command, usage }
10465
+ });
10466
+ }
10467
+ await triggerRunAll({
10468
+ provider: getString(input.values, "provider"),
10469
+ wait: getBoolean(input.values, "wait"),
10470
+ allLocations: getBoolean(input.values, "all-locations"),
10471
+ noLocation: getBoolean(input.values, "no-location"),
10472
+ format: input.format
10473
+ });
10474
+ return;
10475
+ }
10476
+ if (queries2.length && (groups.length || targets.length)) {
10477
+ throw usageError("Error: --query cannot be combined with --group or --target (pick questions, or pick a slice of the plan)", {
10478
+ message: "--query cannot be combined with --group or --target (pick questions, or pick a slice of the plan)",
10479
+ details: { command, usage }
10480
+ });
10481
+ }
10482
+ const project = requireProject(
10483
+ input,
10484
+ command,
10485
+ usage,
10486
+ command === "run" ? "project name is required (or use --all)" : "project name is required"
10487
+ );
10488
+ await triggerRun(project, {
10489
+ provider: getString(input.values, "provider"),
10490
+ queries: queries2.length ? queries2 : void 0,
10491
+ groups: groups.length ? groups : void 0,
10492
+ targets: targets.length ? targets : void 0,
10493
+ wait: getBoolean(input.values, "wait"),
10494
+ location: getString(input.values, "location"),
10495
+ allLocations: getBoolean(input.values, "all-locations"),
10496
+ noLocation: getBoolean(input.values, "no-location"),
10497
+ probe: getBoolean(input.values, "probe"),
10498
+ format: input.format
10499
+ });
10500
+ }
10195
10501
  var RUN_CLI_COMMANDS = [
10196
10502
  {
10197
10503
  path: ["run", "show"],
@@ -10213,69 +10519,25 @@ var RUN_CLI_COMMANDS = [
10213
10519
  await cancelRun(project, input.positionals[1], input.format);
10214
10520
  }
10215
10521
  },
10522
+ {
10523
+ path: ["run", "trigger"],
10524
+ usage: RUN_TRIGGER_USAGE,
10525
+ options: RUN_TRIGGER_OPTIONS,
10526
+ run: (input) => triggerRunCommand(
10527
+ // `canonry run trigger` with nothing after it is ambiguous: the
10528
+ // subcommand with no project, or the older form naming a project called
10529
+ // "trigger". Nobody types the subcommand without a project, and a project
10530
+ // whose name collides with a keyword still has to be runnable, so the
10531
+ // project wins and the word is handed back as the positional.
10532
+ input.positionals.length === 0 && !getBoolean(input.values, "all") ? { ...input, positionals: ["trigger"] } : input,
10533
+ "run.trigger"
10534
+ )
10535
+ },
10216
10536
  {
10217
10537
  path: ["run"],
10218
- usage: "canonry run <project|--all> [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--wait] [--format json]",
10219
- options: {
10220
- provider: stringOption(),
10221
- query: multiStringOption(),
10222
- wait: { type: "boolean", default: false },
10223
- all: { type: "boolean", default: false },
10224
- location: stringOption(),
10225
- "all-locations": { type: "boolean", default: false },
10226
- "no-location": { type: "boolean", default: false },
10227
- // Probe runs are operator/agent test runs — they write a snapshot
10228
- // for inspection but are excluded from dashboard / analytics /
10229
- // intelligence / notifications. Use when you want to verify a
10230
- // provider works without polluting the project's metrics.
10231
- probe: { type: "boolean", default: false }
10232
- },
10233
- run: async (input) => {
10234
- if (getBoolean(input.values, "all")) {
10235
- if (input.positionals.length > 0) {
10236
- throw usageError("Error: --all cannot be combined with a project name", {
10237
- message: "--all cannot be combined with a project name",
10238
- details: {
10239
- command: "run",
10240
- usage: "canonry run --all [--provider <name>] [--wait] [--format json]"
10241
- }
10242
- });
10243
- }
10244
- if (getStringArray(input.values, "query")?.length) {
10245
- throw usageError("Error: --query cannot be combined with --all (query scope is project-specific)", {
10246
- message: "--query cannot be combined with --all (query scope is project-specific)",
10247
- details: {
10248
- command: "run",
10249
- usage: "canonry run <project> --query <q>... [--provider <name>] [--wait] [--format json]"
10250
- }
10251
- });
10252
- }
10253
- await triggerRunAll({
10254
- provider: getString(input.values, "provider"),
10255
- wait: getBoolean(input.values, "wait"),
10256
- allLocations: getBoolean(input.values, "all-locations"),
10257
- noLocation: getBoolean(input.values, "no-location"),
10258
- format: input.format
10259
- });
10260
- return;
10261
- }
10262
- const project = requireProject(
10263
- input,
10264
- "run",
10265
- "canonry run <project> [--provider <name>] [--query <q>...] [--wait] [--format json]",
10266
- "project name is required (or use --all)"
10267
- );
10268
- await triggerRun(project, {
10269
- provider: getString(input.values, "provider"),
10270
- queries: getStringArray(input.values, "query"),
10271
- wait: getBoolean(input.values, "wait"),
10272
- location: getString(input.values, "location"),
10273
- allLocations: getBoolean(input.values, "all-locations"),
10274
- noLocation: getBoolean(input.values, "no-location"),
10275
- probe: getBoolean(input.values, "probe"),
10276
- format: input.format
10277
- });
10278
- }
10538
+ usage: RUN_USAGE2,
10539
+ options: RUN_TRIGGER_OPTIONS,
10540
+ run: (input) => triggerRunCommand(input, "run")
10279
10541
  },
10280
10542
  {
10281
10543
  path: ["runs"],
@@ -10300,11 +10562,11 @@ var RUN_CLI_COMMANDS = [
10300
10562
  ];
10301
10563
 
10302
10564
  // src/commands/schedule.ts
10303
- function getClient23() {
10565
+ function getClient24() {
10304
10566
  return createApiClient();
10305
10567
  }
10306
10568
  async function setSchedule(project, opts) {
10307
- const client = getClient23();
10569
+ const client = getClient24();
10308
10570
  const body = {};
10309
10571
  if (opts.kind) body.kind = opts.kind;
10310
10572
  if (opts.sourceId) body.sourceId = opts.sourceId;
@@ -10321,7 +10583,7 @@ async function setSchedule(project, opts) {
10321
10583
  printSchedule(result);
10322
10584
  }
10323
10585
  async function showSchedule(project, format, kind) {
10324
- const client = getClient23();
10586
+ const client = getClient24();
10325
10587
  const result = await client.getSchedule(project, kind);
10326
10588
  if (isMachineFormat(format)) {
10327
10589
  console.log(JSON.stringify(result, null, 2));
@@ -10330,7 +10592,7 @@ async function showSchedule(project, format, kind) {
10330
10592
  printSchedule(result);
10331
10593
  }
10332
10594
  async function enableSchedule(project, format, kind) {
10333
- const client = getClient23();
10595
+ const client = getClient24();
10334
10596
  const current = await client.getSchedule(project, kind);
10335
10597
  const body = { kind: current.kind, timezone: current.timezone, enabled: true };
10336
10598
  if (current.preset) body.preset = current.preset;
@@ -10345,7 +10607,7 @@ async function enableSchedule(project, format, kind) {
10345
10607
  console.log(`Schedule enabled for "${project}" (kind: ${result.kind})`);
10346
10608
  }
10347
10609
  async function disableSchedule(project, format, kind) {
10348
- const client = getClient23();
10610
+ const client = getClient24();
10349
10611
  const current = await client.getSchedule(project, kind);
10350
10612
  const body = { kind: current.kind, timezone: current.timezone, enabled: false };
10351
10613
  if (current.preset) body.preset = current.preset;
@@ -10360,7 +10622,7 @@ async function disableSchedule(project, format, kind) {
10360
10622
  console.log(`Schedule disabled for "${project}" (kind: ${result.kind})`);
10361
10623
  }
10362
10624
  async function removeSchedule(project, format, kind) {
10363
- const client = getClient23();
10625
+ const client = getClient24();
10364
10626
  await client.deleteSchedule(project, kind);
10365
10627
  const resolvedKind = kind ?? "answer-visibility";
10366
10628
  if (isMachineFormat(format)) {
@@ -10478,11 +10740,11 @@ var SCHEDULE_CLI_COMMANDS = [
10478
10740
  ];
10479
10741
 
10480
10742
  // src/commands/settings.ts
10481
- function getClient24() {
10743
+ function getClient25() {
10482
10744
  return createApiClient();
10483
10745
  }
10484
10746
  async function setProvider(name, opts) {
10485
- const client = getClient24();
10747
+ const client = getClient25();
10486
10748
  const { format, ...payload } = opts;
10487
10749
  const result = await client.updateProvider(name, payload);
10488
10750
  if (isMachineFormat(format)) {
@@ -10498,7 +10760,7 @@ async function setProvider(name, opts) {
10498
10760
  }
10499
10761
  }
10500
10762
  async function showSettings(format) {
10501
- const client = getClient24();
10763
+ const client = getClient25();
10502
10764
  const config = loadConfig();
10503
10765
  const settings = await client.getSettings();
10504
10766
  if (isMachineFormat(format)) {
@@ -11048,7 +11310,7 @@ function wrapText(font, text, size, maxWidth) {
11048
11310
  }
11049
11311
 
11050
11312
  // src/commands/snapshot.ts
11051
- function getClient25() {
11313
+ function getClient26() {
11052
11314
  return createApiClient();
11053
11315
  }
11054
11316
  function slugify(value) {
@@ -11059,7 +11321,7 @@ function autoOutputPath(companyName, ext) {
11059
11321
  return `${slugify(companyName)}-snapshot-${date}.${ext}`;
11060
11322
  }
11061
11323
  async function createSnapshotReport(companyName, opts) {
11062
- const client = getClient25();
11324
+ const client = getClient26();
11063
11325
  const report = await client.createSnapshot({
11064
11326
  companyName,
11065
11327
  domain: opts.domain,
@@ -12505,7 +12767,7 @@ function stopDaemon(format = "text") {
12505
12767
  // src/commands/init.ts
12506
12768
  import crypto2 from "crypto";
12507
12769
  import fs13 from "fs";
12508
- import readline from "readline";
12770
+ import readline2 from "readline";
12509
12771
  import path10 from "path";
12510
12772
  var pendingServeHandoff = false;
12511
12773
  function consumePendingServeHandoff() {
@@ -12522,7 +12784,7 @@ async function promptProviderApiKey(label, envVar, promptFn = prompt, env = proc
12522
12784
  return promptFn(`${label} API key (press Enter to skip): `);
12523
12785
  }
12524
12786
  function prompt(question) {
12525
- const rl = readline.createInterface({
12787
+ const rl = readline2.createInterface({
12526
12788
  input: process.stdin,
12527
12789
  output: process.stdout
12528
12790
  });
@@ -13435,11 +13697,11 @@ var SYSTEM_CLI_COMMANDS = [
13435
13697
  ];
13436
13698
 
13437
13699
  // src/commands/technical-aeo.ts
13438
- function getClient26() {
13700
+ function getClient27() {
13439
13701
  return createApiClient();
13440
13702
  }
13441
13703
  async function technicalAeoScore(project, opts) {
13442
- const client = getClient26();
13704
+ const client = getClient27();
13443
13705
  const score = await client.getTechnicalAeoScore(project, { runId: opts.runId });
13444
13706
  if (isMachineFormat(opts.format)) {
13445
13707
  console.log(JSON.stringify(score, null, 2));
@@ -13475,7 +13737,7 @@ async function technicalAeoScore(project, opts) {
13475
13737
  console.log(lines.join("\n"));
13476
13738
  }
13477
13739
  async function technicalAeoPages(project, opts) {
13478
- const client = getClient26();
13740
+ const client = getClient27();
13479
13741
  const status = opts.status === "success" || opts.status === "error" ? opts.status : void 0;
13480
13742
  const res = await client.getTechnicalAeoPages(project, { runId: opts.runId, status, sort: opts.sort, limit: opts.limit });
13481
13743
  if (opts.format === "jsonl") {
@@ -13501,7 +13763,7 @@ async function technicalAeoPages(project, opts) {
13501
13763
  console.log(lines.join("\n"));
13502
13764
  }
13503
13765
  async function technicalAeoTrend(project, opts) {
13504
- const client = getClient26();
13766
+ const client = getClient27();
13505
13767
  const res = await client.getTechnicalAeoTrend(project, { limit: opts.limit });
13506
13768
  if (opts.format === "jsonl") {
13507
13769
  emitJsonl(res.points.map((p) => ({ project, ...p })));
@@ -13523,7 +13785,7 @@ async function technicalAeoTrend(project, opts) {
13523
13785
  console.log(lines.join("\n"));
13524
13786
  }
13525
13787
  async function technicalAeoRun(project, opts) {
13526
- const client = getClient26();
13788
+ const client = getClient27();
13527
13789
  const { runId, status } = await client.triggerSiteAudit(project, {
13528
13790
  sitemapUrl: opts.sitemapUrl,
13529
13791
  limit: opts.limit
@@ -13882,7 +14144,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
13882
14144
  import fs15 from "fs";
13883
14145
 
13884
14146
  // src/commands/wordpress.ts
13885
- function getClient27() {
14147
+ function getClient28() {
13886
14148
  return createApiClient();
13887
14149
  }
13888
14150
  async function loadYamlModule() {
@@ -14032,7 +14294,7 @@ async function wordpressConnect(project, opts) {
14032
14294
  details: { project }
14033
14295
  });
14034
14296
  }
14035
- const client = getClient27();
14297
+ const client = getClient28();
14036
14298
  const result = await client.wordpressConnect(project, {
14037
14299
  url: opts.url,
14038
14300
  stagingUrl: opts.stagingUrl,
@@ -14049,7 +14311,7 @@ async function wordpressConnect(project, opts) {
14049
14311
  printWordpressStatus(project, result);
14050
14312
  }
14051
14313
  async function wordpressDisconnect(project, format) {
14052
- const client = getClient27();
14314
+ const client = getClient28();
14053
14315
  await client.wordpressDisconnect(project);
14054
14316
  if (isMachineFormat(format)) {
14055
14317
  printJson2({ project, disconnected: true });
@@ -14058,7 +14320,7 @@ async function wordpressDisconnect(project, format) {
14058
14320
  console.log(`WordPress disconnected from project "${project}".`);
14059
14321
  }
14060
14322
  async function wordpressStatus(project, format) {
14061
- const client = getClient27();
14323
+ const client = getClient28();
14062
14324
  const result = await client.wordpressStatus(project);
14063
14325
  if (isMachineFormat(format)) {
14064
14326
  printJson2(result);
@@ -14067,7 +14329,7 @@ async function wordpressStatus(project, format) {
14067
14329
  printWordpressStatus(project, result);
14068
14330
  }
14069
14331
  async function wordpressPages(project, opts) {
14070
- const client = getClient27();
14332
+ const client = getClient28();
14071
14333
  const result = await client.wordpressPages(project, opts.env);
14072
14334
  if (isMachineFormat(opts.format)) {
14073
14335
  printJson2(result);
@@ -14076,7 +14338,7 @@ async function wordpressPages(project, opts) {
14076
14338
  printPages(project, result.env, result.pages);
14077
14339
  }
14078
14340
  async function wordpressPage(project, slug, opts) {
14079
- const client = getClient27();
14341
+ const client = getClient28();
14080
14342
  const result = await client.wordpressPage(project, slug, opts.env);
14081
14343
  if (isMachineFormat(opts.format)) {
14082
14344
  printJson2(result);
@@ -14085,7 +14347,7 @@ async function wordpressPage(project, slug, opts) {
14085
14347
  printPageDetail(result);
14086
14348
  }
14087
14349
  async function wordpressCreatePage(project, body) {
14088
- const client = getClient27();
14350
+ const client = getClient28();
14089
14351
  const result = await client.wordpressCreatePage(project, body);
14090
14352
  if (isMachineFormat(body.format)) {
14091
14353
  printJson2(result);
@@ -14096,7 +14358,7 @@ async function wordpressCreatePage(project, body) {
14096
14358
  printPageDetail(result);
14097
14359
  }
14098
14360
  async function wordpressUpdatePage(project, body) {
14099
- const client = getClient27();
14361
+ const client = getClient28();
14100
14362
  const result = await client.wordpressUpdatePage(project, body);
14101
14363
  if (isMachineFormat(body.format)) {
14102
14364
  printJson2(result);
@@ -14107,7 +14369,7 @@ async function wordpressUpdatePage(project, body) {
14107
14369
  printPageDetail(result);
14108
14370
  }
14109
14371
  async function wordpressSetMeta(project, body) {
14110
- const client = getClient27();
14372
+ const client = getClient28();
14111
14373
  const result = await client.wordpressSetMeta(project, body);
14112
14374
  if (isMachineFormat(body.format)) {
14113
14375
  printJson2(result);
@@ -14118,12 +14380,12 @@ async function wordpressSetMeta(project, body) {
14118
14380
  printPageDetail(result);
14119
14381
  }
14120
14382
  async function wordpressBulkSetMeta(project, opts) {
14121
- const fs16 = await import("fs/promises");
14383
+ const fs17 = await import("fs/promises");
14122
14384
  const path12 = await import("path");
14123
14385
  const filePath = path12.resolve(opts.from);
14124
14386
  let raw;
14125
14387
  try {
14126
- raw = await fs16.readFile(filePath, "utf8");
14388
+ raw = await fs17.readFile(filePath, "utf8");
14127
14389
  } catch {
14128
14390
  throw new CliError({
14129
14391
  code: "FILE_READ_ERROR",
@@ -14157,7 +14419,7 @@ async function wordpressBulkSetMeta(project, opts) {
14157
14419
  details: { path: filePath }
14158
14420
  });
14159
14421
  }
14160
- const client = getClient27();
14422
+ const client = getClient28();
14161
14423
  const result = await client.wordpressBulkSetMeta(project, { entries, env: opts.env });
14162
14424
  if (isMachineFormat(opts.format)) {
14163
14425
  printJson2(result);
@@ -14200,7 +14462,7 @@ async function wordpressBulkSetMeta(project, opts) {
14200
14462
  Total: ${applied.length} applied, ${skipped.length} skipped, ${manual.length} manual`);
14201
14463
  }
14202
14464
  async function wordpressSchema(project, slug, opts) {
14203
- const client = getClient27();
14465
+ const client = getClient28();
14204
14466
  const result = await client.wordpressSchema(project, slug, opts.env);
14205
14467
  if (isMachineFormat(opts.format)) {
14206
14468
  printJson2(result);
@@ -14211,7 +14473,7 @@ async function wordpressSchema(project, slug, opts) {
14211
14473
  printSchemaBlocks(result.blocks);
14212
14474
  }
14213
14475
  async function wordpressSetSchema(project, body) {
14214
- const client = getClient27();
14476
+ const client = getClient28();
14215
14477
  const result = await client.wordpressSetSchema(project, body);
14216
14478
  if (isMachineFormat(body.format)) {
14217
14479
  printJson2(result);
@@ -14220,13 +14482,13 @@ async function wordpressSetSchema(project, body) {
14220
14482
  printManualAssist(`Schema update for "${body.slug}"`, result);
14221
14483
  }
14222
14484
  async function wordpressSchemaDeploy(project, opts) {
14223
- const fs16 = await import("fs/promises");
14485
+ const fs17 = await import("fs/promises");
14224
14486
  const path12 = await import("path");
14225
14487
  const yaml = await loadYamlModule();
14226
14488
  const filePath = path12.resolve(opts.profile);
14227
14489
  let raw;
14228
14490
  try {
14229
- raw = await fs16.readFile(filePath, "utf8");
14491
+ raw = await fs17.readFile(filePath, "utf8");
14230
14492
  } catch {
14231
14493
  throw new CliError({
14232
14494
  code: "FILE_READ_ERROR",
@@ -14259,7 +14521,7 @@ async function wordpressSchemaDeploy(project, opts) {
14259
14521
  details: { path: filePath }
14260
14522
  });
14261
14523
  }
14262
- const client = getClient27();
14524
+ const client = getClient28();
14263
14525
  const result = await client.wordpressSchemaDeploy(project, { profile: parsed, env: opts.env });
14264
14526
  if (isMachineFormat(opts.format)) {
14265
14527
  printJson2(result);
@@ -14298,7 +14560,7 @@ async function wordpressSchemaDeploy(project, opts) {
14298
14560
  Total: ${deployed} deployed, ${stripped} stripped, ${skipped} skipped, ${failed} failed`);
14299
14561
  }
14300
14562
  async function wordpressSchemaStatus(project, opts) {
14301
- const client = getClient27();
14563
+ const client = getClient28();
14302
14564
  const result = await client.wordpressSchemaStatus(project, opts.env);
14303
14565
  if (isMachineFormat(opts.format)) {
14304
14566
  printJson2(result);
@@ -14331,13 +14593,13 @@ async function wordpressOnboard(project, opts) {
14331
14593
  }
14332
14594
  let profileData;
14333
14595
  if (opts.profile) {
14334
- const fs16 = await import("fs/promises");
14596
+ const fs17 = await import("fs/promises");
14335
14597
  const path12 = await import("path");
14336
14598
  const yaml = await loadYamlModule();
14337
14599
  const filePath = path12.resolve(opts.profile);
14338
14600
  let raw;
14339
14601
  try {
14340
- raw = await fs16.readFile(filePath, "utf8");
14602
+ raw = await fs17.readFile(filePath, "utf8");
14341
14603
  } catch {
14342
14604
  throw new CliError({
14343
14605
  code: "FILE_READ_ERROR",
@@ -14357,7 +14619,7 @@ async function wordpressOnboard(project, opts) {
14357
14619
  });
14358
14620
  }
14359
14621
  }
14360
- const client = getClient27();
14622
+ const client = getClient28();
14361
14623
  const result = await client.wordpressOnboard(project, {
14362
14624
  url: opts.url,
14363
14625
  username: opts.user,
@@ -14382,7 +14644,7 @@ async function wordpressOnboard(project, opts) {
14382
14644
  }
14383
14645
  }
14384
14646
  async function wordpressLlmsTxt(project, opts) {
14385
- const client = getClient27();
14647
+ const client = getClient28();
14386
14648
  const result = await client.wordpressLlmsTxt(project, opts.env);
14387
14649
  if (isMachineFormat(opts.format)) {
14388
14650
  printJson2(result);
@@ -14393,7 +14655,7 @@ async function wordpressLlmsTxt(project, opts) {
14393
14655
  console.log(result.content ?? "(not found)");
14394
14656
  }
14395
14657
  async function wordpressSetLlmsTxt(project, body) {
14396
- const client = getClient27();
14658
+ const client = getClient28();
14397
14659
  const result = await client.wordpressSetLlmsTxt(project, body);
14398
14660
  if (isMachineFormat(body.format)) {
14399
14661
  printJson2(result);
@@ -14402,7 +14664,7 @@ async function wordpressSetLlmsTxt(project, body) {
14402
14664
  printManualAssist(`llms.txt update for "${project}"`, result);
14403
14665
  }
14404
14666
  async function wordpressAudit(project, opts) {
14405
- const client = getClient27();
14667
+ const client = getClient28();
14406
14668
  const result = await client.wordpressAudit(project, opts.env);
14407
14669
  if (isMachineFormat(opts.format)) {
14408
14670
  printJson2(result);
@@ -14416,7 +14678,7 @@ async function wordpressAudit(project, opts) {
14416
14678
  printAuditIssues(result.issues);
14417
14679
  }
14418
14680
  async function wordpressDiff(project, slug, format) {
14419
- const client = getClient27();
14681
+ const client = getClient28();
14420
14682
  const result = await client.wordpressDiff(project, slug);
14421
14683
  if (isMachineFormat(format)) {
14422
14684
  printJson2(result);
@@ -14425,7 +14687,7 @@ async function wordpressDiff(project, slug, format) {
14425
14687
  printDiff(result);
14426
14688
  }
14427
14689
  async function wordpressStagingStatus(project, format) {
14428
- const client = getClient27();
14690
+ const client = getClient28();
14429
14691
  const result = await client.wordpressStagingStatus(project);
14430
14692
  if (isMachineFormat(format)) {
14431
14693
  printJson2(result);
@@ -14439,7 +14701,7 @@ async function wordpressStagingStatus(project, format) {
14439
14701
  console.log(` Admin URL: ${result.adminUrl}`);
14440
14702
  }
14441
14703
  async function wordpressStagingPush(project, format) {
14442
- const client = getClient27();
14704
+ const client = getClient28();
14443
14705
  const result = await client.wordpressStagingPush(project);
14444
14706
  if (isMachineFormat(format)) {
14445
14707
  printJson2(result);
@@ -15451,6 +15713,112 @@ Usage: ${usage}`, {
15451
15713
  }
15452
15714
  ];
15453
15715
 
15716
+ // src/commands/measurement-plan.ts
15717
+ import fs16 from "fs";
15718
+ import { parse } from "yaml";
15719
+ function readPlan(source) {
15720
+ const content = source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
15721
+ const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
15722
+ return measurementPlanInputSchema.parse(parsed);
15723
+ }
15724
+ function readDiscoveryRule(source) {
15725
+ const content = source === "-" ? fs16.readFileSync(0, "utf8") : fs16.readFileSync(source, "utf8");
15726
+ const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
15727
+ return measurementDiscoveryRuleSchema.parse(parsed);
15728
+ }
15729
+ async function showMeasurementPlan(project, revision) {
15730
+ const client = createApiClient();
15731
+ console.log(JSON.stringify(revision === void 0 ? await client.getMeasurementPlan(project) : await client.getMeasurementPlanVersion(project, revision), null, 2));
15732
+ }
15733
+ async function listMeasurementPlanVersions(project) {
15734
+ console.log(JSON.stringify(await createApiClient().listMeasurementPlanVersions(project), null, 2));
15735
+ }
15736
+ async function publishMeasurementPlan(project, source) {
15737
+ const client = createApiClient();
15738
+ const plan = readPlan(source);
15739
+ const current = await client.getMeasurementPlan(project);
15740
+ console.log(JSON.stringify(await client.publishMeasurementPlan(project, {
15741
+ expectedActiveRevision: current.active?.revision ?? null,
15742
+ plan
15743
+ }), null, 2));
15744
+ }
15745
+ async function retireMeasurementPlanSegment(project, stableKey) {
15746
+ console.log(JSON.stringify(await createApiClient().retireMeasurementPlanSegment(project, stableKey), null, 2));
15747
+ }
15748
+ async function discoverMeasurementTargets(project, sitemapUrl, ruleSource, maxUrls) {
15749
+ const request = measurementDiscoveryRequestSchema.parse({
15750
+ sitemapUrl,
15751
+ rule: readDiscoveryRule(ruleSource),
15752
+ ...maxUrls === void 0 ? {} : { maxUrls }
15753
+ });
15754
+ console.log(JSON.stringify(await createApiClient().discoverMeasurementTargets(project, request), null, 2));
15755
+ }
15756
+ async function showMeasurementReport(project, revision) {
15757
+ console.log(JSON.stringify(await createApiClient().getMeasurementReport(project, revision), null, 2));
15758
+ }
15759
+
15760
+ // src/cli-commands/measurement-plan.ts
15761
+ var MEASUREMENT_PLAN_CLI_COMMANDS = [
15762
+ { path: ["measurement-plan", "show"], usage: "canonry measurement-plan show <project> [--revision N] [--format json]", options: { revision: stringOption() }, run: async (input) => {
15763
+ const value = getString(input.values, "revision");
15764
+ const revision = value === void 0 ? void 0 : Number(value);
15765
+ if (revision !== void 0 && (!Number.isInteger(revision) || revision <= 0)) throw usageError("--revision must be a positive integer");
15766
+ await showMeasurementPlan(requireProject(input, "measurement-plan.show", "canonry measurement-plan show <project> [--revision N]"), revision);
15767
+ } },
15768
+ { path: ["measurement-plan", "versions"], usage: "canonry measurement-plan versions <project> [--format json]", run: (input) => listMeasurementPlanVersions(requireProject(input, "measurement-plan.versions", "canonry measurement-plan versions <project>")) },
15769
+ { path: ["measurement-plan", "publish"], usage: "canonry measurement-plan publish <project> <yaml|json|-> [--format json]", run: (input) => {
15770
+ const project = requireProject(input, "measurement-plan.publish", "canonry measurement-plan publish <project> <yaml|json|->");
15771
+ const source = input.positionals[1];
15772
+ if (!source) throw usageError("plan file path or - is required");
15773
+ return publishMeasurementPlan(project, source);
15774
+ } },
15775
+ { path: ["measurement-plan", "retire"], usage: "canonry measurement-plan retire <project> <stable-key> [--format json]", run: (input) => {
15776
+ const project = requireProject(input, "measurement-plan.retire", "canonry measurement-plan retire <project> <stable-key>");
15777
+ const stableKey = input.positionals[1];
15778
+ if (!stableKey) throw usageError("stable segment key is required");
15779
+ return retireMeasurementPlanSegment(project, stableKey);
15780
+ } },
15781
+ {
15782
+ path: ["measurement-plan", "discover"],
15783
+ usage: "canonry measurement-plan discover <project> --sitemap-url <url> --rule <yaml|json|-> [--max-urls N] [--format json]",
15784
+ options: { "sitemap-url": stringOption(), rule: stringOption(), "max-urls": stringOption() },
15785
+ run: (input) => {
15786
+ const usage = "canonry measurement-plan discover <project> --sitemap-url <url> --rule <yaml|json|-> [--max-urls N]";
15787
+ const project = requireProject(input, "measurement-plan.discover", usage);
15788
+ const sitemapUrl = requireStringOption(input, "sitemap-url", {
15789
+ command: "measurement-plan.discover",
15790
+ usage,
15791
+ message: "--sitemap-url is required"
15792
+ });
15793
+ const rule = requireStringOption(input, "rule", {
15794
+ command: "measurement-plan.discover",
15795
+ usage,
15796
+ message: "--rule is required"
15797
+ });
15798
+ const maxUrlsValue = getString(input.values, "max-urls");
15799
+ const maxUrls = maxUrlsValue === void 0 ? void 0 : Number(maxUrlsValue);
15800
+ if (maxUrls !== void 0 && (!Number.isInteger(maxUrls) || maxUrls < 1 || maxUrls > 1e4)) {
15801
+ throw usageError("--max-urls must be an integer from 1 to 10000");
15802
+ }
15803
+ return discoverMeasurementTargets(project, sitemapUrl, rule, maxUrls);
15804
+ }
15805
+ },
15806
+ {
15807
+ path: ["measurement-plan", "report"],
15808
+ usage: "canonry measurement-plan report <project> --revision N [--format json]",
15809
+ options: { revision: stringOption() },
15810
+ run: (input) => {
15811
+ const project = requireProject(input, "measurement-plan.report", "canonry measurement-plan report <project> --revision N");
15812
+ const value = getString(input.values, "revision");
15813
+ const revision = value === void 0 ? void 0 : Number(value);
15814
+ if (revision === void 0 || !Number.isInteger(revision) || revision <= 0) {
15815
+ throw usageError("--revision must be a positive integer");
15816
+ }
15817
+ return showMeasurementReport(project, revision);
15818
+ }
15819
+ }
15820
+ ];
15821
+
15454
15822
  // src/cli-commands.ts
15455
15823
  var REGISTERED_CLI_COMMANDS = [
15456
15824
  ...BACKFILL_CLI_COMMANDS,
@@ -15462,6 +15830,7 @@ var REGISTERED_CLI_COMMANDS = [
15462
15830
  ...QUERY_CLI_COMMANDS,
15463
15831
  ...KEYWORD_CLI_COMMANDS,
15464
15832
  ...KEYS_CLI_COMMANDS,
15833
+ ...USERS_CLI_COMMANDS,
15465
15834
  ...COMPETITOR_CLI_COMMANDS,
15466
15835
  ...SETTINGS_CLI_COMMANDS,
15467
15836
  ...SKILLS_CLI_COMMANDS,
@@ -15487,7 +15856,8 @@ var REGISTERED_CLI_COMMANDS = [
15487
15856
  ...TECHNICAL_AEO_CLI_COMMANDS,
15488
15857
  ...DOCTOR_CLI_COMMANDS,
15489
15858
  ...GET_CLI_COMMANDS,
15490
- ...MCP_CLI_COMMANDS
15859
+ ...MCP_CLI_COMMANDS,
15860
+ ...MEASUREMENT_PLAN_CLI_COMMANDS
15491
15861
  ];
15492
15862
 
15493
15863
  // src/setup-nudge.ts