@ainyc/canonry 4.64.1 → 4.68.1

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 (28) hide show
  1. package/README.md +1 -0
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +38 -2
  3. package/assets/assets/{BacklinksPage-B9oyoljV.js → BacklinksPage-B9Q2r9zM.js} +1 -1
  4. package/assets/assets/ChartPrimitives-D-YWOWK-.js +1 -0
  5. package/assets/assets/ProjectPage-BXgxWKyT.js +6 -0
  6. package/assets/assets/{RunRow-vHM36Fdi.js → RunRow-CJpjUaht.js} +1 -1
  7. package/assets/assets/{RunsPage-Cr58nTet.js → RunsPage-DVl_pgqe.js} +1 -1
  8. package/assets/assets/{SettingsPage-BuiP8ZOv.js → SettingsPage-CAiVL2mo.js} +1 -1
  9. package/assets/assets/{TrafficPage-bwOxChyo.js → TrafficPage-B8dnHFqk.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-B0uY6VIB.js → TrafficSourceDetailPage-37s8p8eZ.js} +1 -1
  11. package/assets/assets/{extract-error-message-CWdzuNp4.js → extract-error-message-DwqbPRoa.js} +1 -1
  12. package/assets/assets/index-BquJzH0t.css +1 -0
  13. package/assets/assets/{index-DiN_mzYU.js → index-DFo2OL9c.js} +79 -79
  14. package/assets/assets/{server-traffic-D3aICbxr.js → server-traffic-CMFP8-x2.js} +1 -1
  15. package/assets/assets/{trash-2-CVKno2W2.js → trash-2-C4sYVIa6.js} +1 -1
  16. package/assets/index.html +2 -2
  17. package/dist/{chunk-34PATQZM.js → chunk-D75O5A27.js} +36 -1
  18. package/dist/{chunk-MDNDIBUM.js → chunk-GZYLAE6M.js} +57 -19
  19. package/dist/{chunk-KHN3XMOR.js → chunk-Y24OE7R3.js} +64 -26
  20. package/dist/{chunk-64IDABSF.js → chunk-YYFBMDLC.js} +22 -31
  21. package/dist/cli.js +433 -219
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-SMU5JVVD.js → intelligence-service-5V2JWQ6K.js} +2 -2
  24. package/dist/mcp.js +2 -2
  25. package/package.json +8 -7
  26. package/assets/assets/ChartPrimitives-CvfM24iC.js +0 -1
  27. package/assets/assets/ProjectPage-DELbOAlm.js +0 -6
  28. package/assets/assets/index-yMJe1bJR.css +0 -1
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  setTelemetrySource,
28
28
  showFirstRunNotice,
29
29
  trackEvent
30
- } from "./chunk-MDNDIBUM.js";
30
+ } from "./chunk-GZYLAE6M.js";
31
31
  import {
32
32
  CliError,
33
33
  EXIT_SYSTEM_ERROR,
@@ -37,13 +37,14 @@ import {
37
37
  getConfigDir,
38
38
  getConfigPath,
39
39
  isEndpointMissing,
40
+ isMachineFormat,
40
41
  loadConfig,
41
42
  loadConfigRaw,
42
43
  printCliError,
43
44
  saveConfig,
44
45
  saveConfigPatch,
45
46
  usageError
46
- } from "./chunk-64IDABSF.js";
47
+ } from "./chunk-YYFBMDLC.js";
47
48
  import {
48
49
  apiKeys,
49
50
  createClient,
@@ -51,7 +52,7 @@ import {
51
52
  projects,
52
53
  queries,
53
54
  renderReportHtml
54
- } from "./chunk-KHN3XMOR.js";
55
+ } from "./chunk-Y24OE7R3.js";
55
56
  import {
56
57
  CcReleaseSyncStatuses,
57
58
  CheckScopes,
@@ -68,7 +69,7 @@ import {
68
69
  notificationEventSchema,
69
70
  providerQuotaPolicySchema,
70
71
  resolveProviderInput
71
- } from "./chunk-34PATQZM.js";
72
+ } from "./chunk-D75O5A27.js";
72
73
 
73
74
  // src/cli.ts
74
75
  import { pathToFileURL } from "url";
@@ -126,7 +127,9 @@ function withGlobalOptions(options) {
126
127
  return base;
127
128
  }
128
129
  function toFormat(value, fallbackFormat) {
129
- return value === "json" ? "json" : fallbackFormat;
130
+ if (value === "json") return "json";
131
+ if (value === "jsonl") return "jsonl";
132
+ return fallbackFormat;
130
133
  }
131
134
  function printGroupHelp(group, specs) {
132
135
  const parentSpec = specs.find((s) => s.path.length === 1 && s.path[0] === group);
@@ -423,6 +426,16 @@ var BACKFILL_CLI_COMMANDS = [
423
426
  }
424
427
  ];
425
428
 
429
+ // src/cli-output.ts
430
+ function emitJsonl(records) {
431
+ let out = "";
432
+ for (const record of records) {
433
+ out += `${JSON.stringify(record)}
434
+ `;
435
+ }
436
+ if (out) process.stdout.write(out);
437
+ }
438
+
426
439
  // src/commands/backlinks.ts
427
440
  function getClient() {
428
441
  return createApiClient();
@@ -530,7 +543,7 @@ async function pollRun(runId, format) {
530
543
  }
531
544
  async function backlinksInstall(opts = {}) {
532
545
  const result = await getClient().backlinksInstall();
533
- if (opts.format === "json") {
546
+ if (isMachineFormat(opts.format)) {
534
547
  printJson(result);
535
548
  return;
536
549
  }
@@ -538,7 +551,7 @@ async function backlinksInstall(opts = {}) {
538
551
  }
539
552
  async function backlinksDoctor(opts = {}) {
540
553
  const status = await getClient().backlinksStatus();
541
- if (opts.format === "json") {
554
+ if (isMachineFormat(opts.format)) {
542
555
  printJson(status);
543
556
  return;
544
557
  }
@@ -548,7 +561,7 @@ async function backlinksSync(opts) {
548
561
  const client = getClient();
549
562
  const sync = await client.backlinksTriggerSync(opts.release);
550
563
  const final = opts.wait ? await pollSync(sync.id, opts.format) : sync;
551
- if (opts.format === "json") {
564
+ if (isMachineFormat(opts.format)) {
552
565
  printJson(final);
553
566
  return;
554
567
  }
@@ -579,7 +592,7 @@ function formatLatestRelease(result) {
579
592
  }
580
593
  async function backlinksLatestRelease(opts = {}) {
581
594
  const result = await getClient().backlinksLatestRelease();
582
- if (opts.format === "json") {
595
+ if (isMachineFormat(opts.format)) {
583
596
  printJson(result);
584
597
  return;
585
598
  }
@@ -587,7 +600,7 @@ async function backlinksLatestRelease(opts = {}) {
587
600
  }
588
601
  async function backlinksStatus(opts = {}) {
589
602
  const sync = await getClient().backlinksLatestSync();
590
- if (opts.format === "json") {
603
+ if (isMachineFormat(opts.format)) {
591
604
  printJson(sync);
592
605
  return;
593
606
  }
@@ -607,6 +620,11 @@ async function backlinksList(opts) {
607
620
  if (opts.format === "json") {
608
621
  printJson(response);
609
622
  return;
623
+ } else if (opts.format === "jsonl") {
624
+ const release = response.summary?.release ?? null;
625
+ const targetDomain = response.summary?.targetDomain ?? null;
626
+ emitJsonl(response.rows.map((row) => ({ project: opts.project, release, targetDomain, ...row })));
627
+ return;
610
628
  }
611
629
  console.log(formatSummaryAndDomains(opts.project, response));
612
630
  }
@@ -615,6 +633,9 @@ async function backlinksReleases(opts = {}) {
615
633
  if (opts.format === "json") {
616
634
  printJson(rows);
617
635
  return;
636
+ } else if (opts.format === "jsonl") {
637
+ emitJsonl(rows);
638
+ return;
618
639
  }
619
640
  console.log(formatCachedReleases(rows));
620
641
  }
@@ -622,7 +643,7 @@ async function backlinksExtract(opts) {
622
643
  const client = getClient();
623
644
  const run = await client.backlinksExtract(opts.project, opts.release);
624
645
  const final = opts.wait ? await pollRun(run.id, opts.format) : run;
625
- if (opts.format === "json") {
646
+ if (isMachineFormat(opts.format)) {
626
647
  printJson(final);
627
648
  return;
628
649
  }
@@ -634,7 +655,7 @@ async function backlinksCachePrune(opts) {
634
655
  throw new Error("Usage: canonry backlinks cache prune --release <id>");
635
656
  }
636
657
  const result = await getClient().backlinksPruneCache(opts.release);
637
- if (opts.format === "json") {
658
+ if (isMachineFormat(opts.format)) {
638
659
  printJson({ pruned: opts.release, ...result });
639
660
  return;
640
661
  }
@@ -814,7 +835,7 @@ async function bingConnect(project, opts) {
814
835
  }
815
836
  const client = getClient2();
816
837
  const result = await client.bingConnect(project, { apiKey });
817
- if (opts?.format === "json") {
838
+ if (isMachineFormat(opts?.format)) {
818
839
  console.log(JSON.stringify(result, null, 2));
819
840
  return;
820
841
  }
@@ -835,7 +856,7 @@ Set the active site with: canonry bing set-site ${project} <url>`);
835
856
  async function bingDisconnect(project, format) {
836
857
  const client = getClient2();
837
858
  await client.bingDisconnect(project);
838
- if (format === "json") {
859
+ if (isMachineFormat(format)) {
839
860
  console.log(JSON.stringify({ project, disconnected: true }, null, 2));
840
861
  return;
841
862
  }
@@ -844,7 +865,7 @@ async function bingDisconnect(project, format) {
844
865
  async function bingStatus(project, format) {
845
866
  const client = getClient2();
846
867
  const result = await client.bingStatus(project);
847
- if (format === "json") {
868
+ if (isMachineFormat(format)) {
848
869
  console.log(JSON.stringify(result, null, 2));
849
870
  return;
850
871
  }
@@ -865,6 +886,9 @@ async function bingSites(project, format) {
865
886
  if (format === "json") {
866
887
  console.log(JSON.stringify(result, null, 2));
867
888
  return;
889
+ } else if (format === "jsonl") {
890
+ emitJsonl(result.sites.map((site) => ({ project, ...site })));
891
+ return;
868
892
  }
869
893
  if (result.sites.length === 0) {
870
894
  console.log("No sites registered in Bing Webmaster Tools.");
@@ -884,7 +908,7 @@ Use "canonry bing set-site <project> <url>" to select a site.`);
884
908
  async function bingSetSite(project, siteUrl, format) {
885
909
  const client = getClient2();
886
910
  await client.bingSetSite(project, siteUrl);
887
- if (format === "json") {
911
+ if (isMachineFormat(format)) {
888
912
  console.log(JSON.stringify({ project, siteUrl }, null, 2));
889
913
  return;
890
914
  }
@@ -893,7 +917,7 @@ async function bingSetSite(project, siteUrl, format) {
893
917
  async function bingCoverage(project, format) {
894
918
  const client = getClient2();
895
919
  const result = await client.bingCoverage(project);
896
- if (format === "json") {
920
+ if (isMachineFormat(format)) {
897
921
  console.log(JSON.stringify(result, null, 2));
898
922
  return;
899
923
  }
@@ -944,6 +968,9 @@ async function bingCoverageHistory(project, opts) {
944
968
  if (opts.format === "json") {
945
969
  console.log(JSON.stringify(rows, null, 2));
946
970
  return;
971
+ } else if (opts.format === "jsonl") {
972
+ emitJsonl(rows.map((row) => ({ project, ...row })));
973
+ return;
947
974
  }
948
975
  if (rows.length === 0) {
949
976
  console.log('No coverage history found. Run "canonry bing coverage" or "canonry bing refresh" first to capture a snapshot.');
@@ -961,7 +988,7 @@ Bing Coverage History for "${project}" (${rows.length} snapshots):
961
988
  async function bingInspect(project, url, format) {
962
989
  const client = getClient2();
963
990
  const result = await client.bingInspectUrl(project, url);
964
- if (format === "json") {
991
+ if (isMachineFormat(format)) {
965
992
  console.log(JSON.stringify(result, null, 2));
966
993
  return;
967
994
  }
@@ -985,6 +1012,9 @@ async function bingInspections(project, opts) {
985
1012
  if (opts.format === "json") {
986
1013
  console.log(JSON.stringify(rows, null, 2));
987
1014
  return;
1015
+ } else if (opts.format === "jsonl") {
1016
+ emitJsonl(rows.map((row) => ({ project, ...row })));
1017
+ return;
988
1018
  }
989
1019
  if (rows.length === 0) {
990
1020
  console.log("No Bing URL inspections found.");
@@ -1008,14 +1038,14 @@ async function bingRefresh(project, format) {
1008
1038
  const rows = await client.bingInspections(project);
1009
1039
  const uniqueUrls = [...new Set(rows.map((r) => r.url))];
1010
1040
  if (uniqueUrls.length === 0) {
1011
- if (format === "json") {
1041
+ if (isMachineFormat(format)) {
1012
1042
  console.log(JSON.stringify({ refreshed: 0, message: "No previously inspected URLs to refresh." }, null, 2));
1013
1043
  return;
1014
1044
  }
1015
1045
  console.log('No previously inspected URLs to refresh. Run "canonry bing inspect <project> <url>" first.');
1016
1046
  return;
1017
1047
  }
1018
- if (format !== "json") {
1048
+ if (!isMachineFormat(format)) {
1019
1049
  process.stderr.write(`Re-inspecting ${uniqueUrls.length} URL(s) via Bing`);
1020
1050
  }
1021
1051
  const CONCURRENCY = 10;
@@ -1030,11 +1060,11 @@ async function bingRefresh(project, format) {
1030
1060
  errors.push(r.reason instanceof Error ? r.reason.message : String(r.reason));
1031
1061
  }
1032
1062
  }
1033
- if (format !== "json") {
1063
+ if (!isMachineFormat(format)) {
1034
1064
  process.stderr.write(".");
1035
1065
  }
1036
1066
  }
1037
- if (format !== "json") {
1067
+ if (!isMachineFormat(format)) {
1038
1068
  process.stderr.write("\n");
1039
1069
  }
1040
1070
  if (errors.length > 0) {
@@ -1059,7 +1089,7 @@ async function bingRequestIndexing(project, opts) {
1059
1089
  });
1060
1090
  }
1061
1091
  const result = await client.bingRequestIndexing(project, body);
1062
- if (opts.format === "json") {
1092
+ if (isMachineFormat(opts.format)) {
1063
1093
  console.log(JSON.stringify(result, null, 2));
1064
1094
  return;
1065
1095
  }
@@ -1083,11 +1113,11 @@ async function bingInspectSitemap(project, opts) {
1083
1113
  const run = await client.bingInspectSitemap(project, {
1084
1114
  sitemapUrl: opts.sitemapUrl
1085
1115
  });
1086
- if (!opts.wait && opts.format === "json") {
1116
+ if (!opts.wait && isMachineFormat(opts.format)) {
1087
1117
  console.log(JSON.stringify(run, null, 2));
1088
1118
  return;
1089
1119
  }
1090
- if (opts.format !== "json") {
1120
+ if (!isMachineFormat(opts.format)) {
1091
1121
  console.log(`Bing sitemap inspection started (run ${run.id})`);
1092
1122
  }
1093
1123
  if (opts.wait) {
@@ -1103,7 +1133,7 @@ async function bingInspectSitemap(project, opts) {
1103
1133
  failureMessage: "Bing sitemap inspection failed.",
1104
1134
  details: { project }
1105
1135
  });
1106
- if (opts.format === "json") {
1136
+ if (isMachineFormat(opts.format)) {
1107
1137
  console.log(JSON.stringify(current, null, 2));
1108
1138
  return;
1109
1139
  }
@@ -1120,6 +1150,9 @@ async function bingPerformance(project, format) {
1120
1150
  if (format === "json") {
1121
1151
  console.log(JSON.stringify(rows, null, 2));
1122
1152
  return;
1153
+ } else if (format === "jsonl") {
1154
+ emitJsonl(rows.map((row) => ({ project, ...row })));
1155
+ return;
1123
1156
  }
1124
1157
  if (rows.length === 0) {
1125
1158
  console.log("No Bing performance data found.");
@@ -1330,7 +1363,7 @@ async function cdpConnect(opts) {
1330
1363
  port
1331
1364
  };
1332
1365
  saveConfigPatch(config);
1333
- if (opts.format === "json") {
1366
+ if (isMachineFormat(opts.format)) {
1334
1367
  console.log(JSON.stringify({
1335
1368
  host,
1336
1369
  port,
@@ -1346,7 +1379,7 @@ async function cdpStatus(format) {
1346
1379
  const client = getClient3();
1347
1380
  try {
1348
1381
  const status = await client.getCdpStatus();
1349
- if (format === "json") {
1382
+ if (isMachineFormat(format)) {
1350
1383
  console.log(JSON.stringify(status, null, 2));
1351
1384
  return;
1352
1385
  }
@@ -1368,7 +1401,7 @@ async function cdpStatus(format) {
1368
1401
  } catch (err) {
1369
1402
  const msg = err instanceof Error ? err.message : String(err);
1370
1403
  if (msg.includes("501") || msg.includes("not configured")) {
1371
- if (format === "json") {
1404
+ if (isMachineFormat(format)) {
1372
1405
  console.log(JSON.stringify({
1373
1406
  connected: false,
1374
1407
  endpoint: "",
@@ -1408,7 +1441,7 @@ async function cdpScreenshot(query, opts) {
1408
1441
  }
1409
1442
  try {
1410
1443
  const response = await client.cdpScreenshot(query, body.targets);
1411
- if (opts?.format === "json") {
1444
+ if (isMachineFormat(opts?.format)) {
1412
1445
  console.log(JSON.stringify(response, null, 2));
1413
1446
  return;
1414
1447
  }
@@ -1563,7 +1596,7 @@ Sessions already started (recover with \`canonry discover show ${project} <id>\`
1563
1596
  }
1564
1597
  }
1565
1598
  if (!opts.wait) {
1566
- if (opts.format === "json") {
1599
+ if (isMachineFormat(opts.format)) {
1567
1600
  console.log(JSON.stringify(multiAngle ? runs.map((r) => r.start) : runs[0].start, null, 2));
1568
1601
  return;
1569
1602
  }
@@ -1601,7 +1634,7 @@ Sessions already started (recover with \`canonry discover show ${project} <id>\`
1601
1634
  }
1602
1635
  });
1603
1636
  if (results.length > 0) {
1604
- if (opts.format === "json") {
1637
+ if (isMachineFormat(opts.format)) {
1605
1638
  console.log(JSON.stringify(multiAngle ? results.map((r) => r.session) : results[0].session, null, 2));
1606
1639
  } else {
1607
1640
  for (const { angle, session } of results) {
@@ -1648,6 +1681,11 @@ async function discoverProbe(project, sessionId, opts) {
1648
1681
  console.log(JSON.stringify(session, null, 2));
1649
1682
  return;
1650
1683
  }
1684
+ if (opts.format === "jsonl") {
1685
+ const context = { project, sessionId };
1686
+ emitJsonl(session.probes.map((probe) => ({ ...context, ...probe })));
1687
+ return;
1688
+ }
1651
1689
  printSessionDetail(session);
1652
1690
  }
1653
1691
  async function discoverList(project, opts) {
@@ -1657,6 +1695,10 @@ async function discoverList(project, opts) {
1657
1695
  console.log(JSON.stringify(sessions, null, 2));
1658
1696
  return;
1659
1697
  }
1698
+ if (opts.format === "jsonl") {
1699
+ emitJsonl(sessions.map((s) => ({ project, ...s })));
1700
+ return;
1701
+ }
1660
1702
  if (sessions.length === 0) {
1661
1703
  console.log(`No discovery sessions for "${project}".`);
1662
1704
  return;
@@ -1682,12 +1724,17 @@ async function discoverShow(project, sessionId, opts) {
1682
1724
  console.log(JSON.stringify(session, null, 2));
1683
1725
  return;
1684
1726
  }
1727
+ if (opts.format === "jsonl") {
1728
+ const context = { project, sessionId };
1729
+ emitJsonl(session.probes.map((probe) => ({ ...context, ...probe })));
1730
+ return;
1731
+ }
1685
1732
  printSessionDetail(session);
1686
1733
  }
1687
1734
  async function discoverPromotePreview(project, sessionId, opts) {
1688
1735
  const client = getClient4();
1689
1736
  const preview = await client.previewDiscoveryPromote(project, sessionId);
1690
- if (opts.format === "json") {
1737
+ if (isMachineFormat(opts.format)) {
1691
1738
  console.log(JSON.stringify(preview, null, 2));
1692
1739
  return;
1693
1740
  }
@@ -1716,7 +1763,7 @@ async function discoverPromote(project, sessionId, opts) {
1716
1763
  if (opts.competitorTypes && opts.competitorTypes.length > 0) body.competitorTypes = opts.competitorTypes;
1717
1764
  if (opts.includeCompetitors === false) body.includeCompetitors = false;
1718
1765
  const result = await client.promoteDiscovery(project, sessionId, body);
1719
- if (opts.format === "json") {
1766
+ if (isMachineFormat(opts.format)) {
1720
1767
  console.log(JSON.stringify(result, null, 2));
1721
1768
  return;
1722
1769
  }
@@ -2038,6 +2085,8 @@ async function doctorCommand(opts) {
2038
2085
  });
2039
2086
  if (opts.format === "json") {
2040
2087
  console.log(JSON.stringify(report, null, 2));
2088
+ } else if (opts.format === "jsonl") {
2089
+ emitJsonl(report.checks.map((check) => ({ project: report.project, ...check })));
2041
2090
  } else {
2042
2091
  printHumanReport(report);
2043
2092
  }
@@ -2067,6 +2116,12 @@ async function runDoctorAll(opts) {
2067
2116
  });
2068
2117
  if (opts.format === "json") {
2069
2118
  console.log(JSON.stringify(byKey, null, 2));
2119
+ } else if (opts.format === "jsonl") {
2120
+ const scopes = ["__global__", ...projects2.map((p) => p.name)];
2121
+ emitJsonl(scopes.flatMap((key) => {
2122
+ const project = key === "__global__" ? null : key;
2123
+ return byKey[key].checks.map((check) => ({ project, ...check }));
2124
+ }));
2070
2125
  } else {
2071
2126
  printAllHumanReport(byKey, projects2.map((p) => p.name));
2072
2127
  }
@@ -2149,7 +2204,7 @@ ${header}`);
2149
2204
  }
2150
2205
 
2151
2206
  // src/cli-commands/doctor.ts
2152
- var USAGE = "canonry doctor [--project <name>|--all] [--check <id>...] [--format json]";
2207
+ var USAGE = "canonry doctor [--project <name>|--all] [--check <id>...] [--format json|jsonl]";
2153
2208
  var DOCTOR_CLI_COMMANDS = [
2154
2209
  {
2155
2210
  path: ["doctor"],
@@ -2207,7 +2262,7 @@ async function gaConnect(project, opts) {
2207
2262
  }
2208
2263
  const client = getClient5();
2209
2264
  const result = await client.gaConnect(project, body);
2210
- if (opts.format === "json") {
2265
+ if (isMachineFormat(opts.format)) {
2211
2266
  console.log(JSON.stringify(result, null, 2));
2212
2267
  return;
2213
2268
  }
@@ -2222,7 +2277,7 @@ async function gaConnect(project, opts) {
2222
2277
  async function gaDisconnect(project, format) {
2223
2278
  const client = getClient5();
2224
2279
  await client.gaDisconnect(project);
2225
- if (format === "json") {
2280
+ if (isMachineFormat(format)) {
2226
2281
  console.log(JSON.stringify({ project, disconnected: true }, null, 2));
2227
2282
  return;
2228
2283
  }
@@ -2231,7 +2286,7 @@ async function gaDisconnect(project, format) {
2231
2286
  async function gaStatus(project, format) {
2232
2287
  const client = getClient5();
2233
2288
  const result = await client.gaStatus(project);
2234
- if (format === "json") {
2289
+ if (isMachineFormat(format)) {
2235
2290
  console.log(JSON.stringify(result, null, 2));
2236
2291
  return;
2237
2292
  }
@@ -2260,7 +2315,7 @@ async function gaSync(project, opts) {
2260
2315
  if (opts?.days) body.days = opts.days;
2261
2316
  if (opts?.only) body.only = opts.only;
2262
2317
  const result = await client.gaSync(project, body);
2263
- if (opts?.format === "json") {
2318
+ if (isMachineFormat(opts?.format)) {
2264
2319
  console.log(JSON.stringify(result, null, 2));
2265
2320
  return;
2266
2321
  }
@@ -2280,7 +2335,7 @@ async function gaTraffic(project, opts) {
2280
2335
  if (opts?.limit) params.limit = String(opts.limit);
2281
2336
  if (opts?.window) params.window = opts.window;
2282
2337
  const result = await client.gaTraffic(project, Object.keys(params).length > 0 ? params : void 0);
2283
- if (opts?.format === "json") {
2338
+ if (isMachineFormat(opts?.format)) {
2284
2339
  console.log(JSON.stringify(result, null, 2));
2285
2340
  return;
2286
2341
  }
@@ -2370,6 +2425,9 @@ async function gaAiReferralHistory(project, opts) {
2370
2425
  if (opts?.format === "json") {
2371
2426
  console.log(JSON.stringify(result, null, 2));
2372
2427
  return;
2428
+ } else if (opts?.format === "jsonl") {
2429
+ emitJsonl(result.map((row) => ({ project, window: opts?.window, ...row })));
2430
+ return;
2373
2431
  }
2374
2432
  if (result.length === 0) {
2375
2433
  console.log(`No AI referral history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
@@ -2395,6 +2453,9 @@ async function gaSocialReferralHistory(project, opts) {
2395
2453
  if (opts?.format === "json") {
2396
2454
  console.log(JSON.stringify(result, null, 2));
2397
2455
  return;
2456
+ } else if (opts?.format === "jsonl") {
2457
+ emitJsonl(result.map((row) => ({ project, window: opts?.window, ...row })));
2458
+ return;
2398
2459
  }
2399
2460
  if (result.length === 0) {
2400
2461
  console.log(`No social referral history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
@@ -2420,6 +2481,9 @@ async function gaSessionHistory(project, opts) {
2420
2481
  if (opts?.format === "json") {
2421
2482
  console.log(JSON.stringify(result, null, 2));
2422
2483
  return;
2484
+ } else if (opts?.format === "jsonl") {
2485
+ emitJsonl(result.map((row) => ({ project, window: opts?.window, ...row })));
2486
+ return;
2423
2487
  }
2424
2488
  if (result.length === 0) {
2425
2489
  console.log(`No session history.${opts?.window ? " Try a wider window or omit --window." : ' Run "canonry ga sync <project>" first.'}`);
@@ -2442,6 +2506,9 @@ async function gaCoverage(project, format) {
2442
2506
  if (format === "json") {
2443
2507
  console.log(JSON.stringify(result, null, 2));
2444
2508
  return;
2509
+ } else if (format === "jsonl") {
2510
+ emitJsonl(result.pages.map((row) => ({ project, ...row })));
2511
+ return;
2445
2512
  }
2446
2513
  if (result.pages.length === 0) {
2447
2514
  console.log('No GA4 coverage data. Run "canonry ga sync <project>" first.');
@@ -2464,7 +2531,7 @@ async function gaSocialReferralSummary(project, opts) {
2464
2531
  const traffic = await client.gaTraffic(project);
2465
2532
  if (opts?.trend) {
2466
2533
  const trend = await client.gaSocialReferralTrend(project);
2467
- if (opts.format === "json") {
2534
+ if (isMachineFormat(opts.format)) {
2468
2535
  console.log(JSON.stringify({
2469
2536
  socialSessions: traffic.socialSessions,
2470
2537
  socialUsers: traffic.socialUsers,
@@ -2497,7 +2564,7 @@ async function gaSocialReferralSummary(project, opts) {
2497
2564
  }
2498
2565
  return;
2499
2566
  }
2500
- if (opts?.format === "json") {
2567
+ if (isMachineFormat(opts?.format)) {
2501
2568
  console.log(JSON.stringify({
2502
2569
  socialSessions: traffic.socialSessions,
2503
2570
  socialUsers: traffic.socialUsers,
@@ -2526,7 +2593,7 @@ async function gaAttribution(project, opts) {
2526
2593
  const fmtTrend = (pct2) => pct2 === null ? "n/a" : `${pct2 >= 0 ? "+" : ""}${pct2}%`;
2527
2594
  if (opts?.trend) {
2528
2595
  const trend = await client.gaAttributionTrend(project);
2529
- if (opts.format === "json") {
2596
+ if (isMachineFormat(opts.format)) {
2530
2597
  console.log(JSON.stringify({
2531
2598
  totalSessions: traffic.totalSessions,
2532
2599
  totalUsers: traffic.totalUsers,
@@ -2594,7 +2661,7 @@ async function gaAttribution(project, opts) {
2594
2661
  }
2595
2662
  return;
2596
2663
  }
2597
- if (opts?.format === "json") {
2664
+ if (isMachineFormat(opts?.format)) {
2598
2665
  console.log(JSON.stringify({
2599
2666
  totalSessions: traffic.totalSessions,
2600
2667
  totalUsers: traffic.totalUsers,
@@ -2871,7 +2938,7 @@ async function gbpConnect(project, opts) {
2871
2938
  type: "gbp",
2872
2939
  publicUrl: opts.publicUrl
2873
2940
  });
2874
- if (opts.format === "json") {
2941
+ if (isMachineFormat(opts.format)) {
2875
2942
  console.log(JSON.stringify({ project, type: "gbp", authUrl, redirectUri: redirectUri ?? null }, null, 2));
2876
2943
  return;
2877
2944
  }
@@ -2887,7 +2954,7 @@ async function gbpConnect(project, opts) {
2887
2954
  async function gbpDisconnect(project, opts) {
2888
2955
  const client = getClient6();
2889
2956
  await client.disconnectGbp(project);
2890
- if (opts.format === "json") {
2957
+ if (isMachineFormat(opts.format)) {
2891
2958
  console.log(JSON.stringify({ project, disconnected: true }, null, 2));
2892
2959
  return;
2893
2960
  }
@@ -2899,7 +2966,7 @@ async function gbpLocationsList(project, opts) {
2899
2966
  project,
2900
2967
  opts.selectedOnly ? { selected: true } : void 0
2901
2968
  );
2902
- if (opts.format === "json") {
2969
+ if (isMachineFormat(opts.format)) {
2903
2970
  console.log(JSON.stringify(response, null, 2));
2904
2971
  return;
2905
2972
  }
@@ -2908,7 +2975,7 @@ async function gbpLocationsList(project, opts) {
2908
2975
  async function gbpAccounts(project, opts) {
2909
2976
  const client = getClient6();
2910
2977
  const response = await client.listGbpAccounts(project);
2911
- if (opts.format === "json") {
2978
+ if (isMachineFormat(opts.format)) {
2912
2979
  console.log(JSON.stringify(response, null, 2));
2913
2980
  return;
2914
2981
  }
@@ -2939,7 +3006,7 @@ async function gbpLocationsDiscover(project, opts) {
2939
3006
  ...opts.switchAccount ? { switchAccount: true } : {}
2940
3007
  } : void 0;
2941
3008
  const response = await client.discoverGbpLocations(project, body);
2942
- if (opts.format === "json") {
3009
+ if (isMachineFormat(opts.format)) {
2943
3010
  console.log(JSON.stringify(response, null, 2));
2944
3011
  return;
2945
3012
  }
@@ -2950,7 +3017,7 @@ Discovered ${response.totalDiscovered} location(s); ${response.totalSelected} se
2950
3017
  async function gbpLocationSelect(project, opts) {
2951
3018
  const client = getClient6();
2952
3019
  const updated = await client.setGbpLocationSelection(project, opts.location, true);
2953
- if (opts.format === "json") {
3020
+ if (isMachineFormat(opts.format)) {
2954
3021
  console.log(JSON.stringify(updated, null, 2));
2955
3022
  return;
2956
3023
  }
@@ -2959,7 +3026,7 @@ async function gbpLocationSelect(project, opts) {
2959
3026
  async function gbpLocationDeselect(project, opts) {
2960
3027
  const client = getClient6();
2961
3028
  const updated = await client.setGbpLocationSelection(project, opts.location, false);
2962
- if (opts.format === "json") {
3029
+ if (isMachineFormat(opts.format)) {
2963
3030
  console.log(JSON.stringify(updated, null, 2));
2964
3031
  return;
2965
3032
  }
@@ -2973,7 +3040,7 @@ async function gbpSync(project, opts) {
2973
3040
  monthsOfKeywords: opts.months
2974
3041
  });
2975
3042
  if (!opts.wait) {
2976
- if (opts.format === "json") {
3043
+ if (isMachineFormat(opts.format)) {
2977
3044
  console.log(JSON.stringify({ runId, status }, null, 2));
2978
3045
  return;
2979
3046
  }
@@ -2983,19 +3050,19 @@ async function gbpSync(project, opts) {
2983
3050
  const terminal = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
2984
3051
  const start = Date.now();
2985
3052
  const timeoutMs = 10 * 60 * 1e3;
2986
- if (opts.format !== "json") process.stderr.write("Syncing");
3053
+ if (!isMachineFormat(opts.format)) process.stderr.write("Syncing");
2987
3054
  let final = status;
2988
3055
  while (Date.now() - start < timeoutMs) {
2989
3056
  await new Promise((r) => setTimeout(r, 2e3));
2990
3057
  const run = await client.getRun(runId);
2991
- if (opts.format !== "json") process.stderr.write(".");
3058
+ if (!isMachineFormat(opts.format)) process.stderr.write(".");
2992
3059
  if (terminal.has(run.status)) {
2993
3060
  final = run.status;
2994
3061
  break;
2995
3062
  }
2996
3063
  }
2997
- if (opts.format !== "json") process.stderr.write("\n");
2998
- if (opts.format === "json") {
3064
+ if (!isMachineFormat(opts.format)) process.stderr.write("\n");
3065
+ if (isMachineFormat(opts.format)) {
2999
3066
  console.log(JSON.stringify({ runId, status: final }, null, 2));
3000
3067
  return;
3001
3068
  }
@@ -3004,7 +3071,7 @@ async function gbpSync(project, opts) {
3004
3071
  async function gbpMetrics(project, opts) {
3005
3072
  const client = getClient6();
3006
3073
  const response = await client.listGbpMetrics(project, { locationName: opts.location, metric: opts.metric });
3007
- if (opts.format === "json") {
3074
+ if (isMachineFormat(opts.format)) {
3008
3075
  console.log(JSON.stringify(response, null, 2));
3009
3076
  return;
3010
3077
  }
@@ -3022,7 +3089,7 @@ async function gbpMetrics(project, opts) {
3022
3089
  async function gbpKeywords(project, opts) {
3023
3090
  const client = getClient6();
3024
3091
  const response = await client.listGbpKeywords(project, { locationName: opts.location });
3025
- if (opts.format === "json") {
3092
+ if (isMachineFormat(opts.format)) {
3026
3093
  console.log(JSON.stringify(response, null, 2));
3027
3094
  return;
3028
3095
  }
@@ -3039,7 +3106,7 @@ async function gbpKeywords(project, opts) {
3039
3106
  async function gbpPlaceActions(project, opts) {
3040
3107
  const client = getClient6();
3041
3108
  const response = await client.listGbpPlaceActions(project, { locationName: opts.location });
3042
- if (opts.format === "json") {
3109
+ if (isMachineFormat(opts.format)) {
3043
3110
  console.log(JSON.stringify(response, null, 2));
3044
3111
  return;
3045
3112
  }
@@ -3056,7 +3123,7 @@ async function gbpPlaceActions(project, opts) {
3056
3123
  async function gbpLodging(project, opts) {
3057
3124
  const client = getClient6();
3058
3125
  const response = await client.listGbpLodging(project, { locationName: opts.location });
3059
- if (opts.format === "json") {
3126
+ if (isMachineFormat(opts.format)) {
3060
3127
  console.log(JSON.stringify(response, null, 2));
3061
3128
  return;
3062
3129
  }
@@ -3073,7 +3140,7 @@ async function gbpLodging(project, opts) {
3073
3140
  async function gbpPlaces(project, opts) {
3074
3141
  const client = getClient6();
3075
3142
  const response = await client.listGbpPlaces(project, { locationName: opts.location });
3076
- if (opts.format === "json") {
3143
+ if (isMachineFormat(opts.format)) {
3077
3144
  console.log(JSON.stringify(response, null, 2));
3078
3145
  return;
3079
3146
  }
@@ -3094,7 +3161,7 @@ function fmtDelta(pct2) {
3094
3161
  async function gbpSummary(project, opts) {
3095
3162
  const client = getClient6();
3096
3163
  const s = await client.getGbpSummary(project, { locationName: opts.location });
3097
- if (opts.format === "json") {
3164
+ if (isMachineFormat(opts.format)) {
3098
3165
  console.log(JSON.stringify(s, null, 2));
3099
3166
  return;
3100
3167
  }
@@ -3336,7 +3403,7 @@ async function getCommand(opts) {
3336
3403
  details: { project: opts.project, path: opts.path, from: source }
3337
3404
  });
3338
3405
  }
3339
- if (opts.format === "json") {
3406
+ if (isMachineFormat(opts.format)) {
3340
3407
  console.log(JSON.stringify(leaf, null, 2));
3341
3408
  return;
3342
3409
  }
@@ -3471,7 +3538,7 @@ async function trafficConnectWordpress(project, opts) {
3471
3538
  applicationPassword,
3472
3539
  displayName: opts.displayName
3473
3540
  });
3474
- if (opts.format === "json") {
3541
+ if (isMachineFormat(opts.format)) {
3475
3542
  console.log(JSON.stringify(result, null, 2));
3476
3543
  return;
3477
3544
  }
@@ -3523,7 +3590,7 @@ async function trafficConnectCloudRun(project, opts) {
3523
3590
  displayName: opts.displayName,
3524
3591
  keyJson
3525
3592
  });
3526
- if (opts.format === "json") {
3593
+ if (isMachineFormat(opts.format)) {
3527
3594
  console.log(JSON.stringify(result, null, 2));
3528
3595
  return;
3529
3596
  }
@@ -3601,7 +3668,7 @@ async function trafficConnectVercel(project, opts) {
3601
3668
  environment: opts.environment,
3602
3669
  displayName: opts.displayName
3603
3670
  });
3604
- if (opts.format === "json") {
3671
+ if (isMachineFormat(opts.format)) {
3605
3672
  console.log(JSON.stringify(result, null, 2));
3606
3673
  return;
3607
3674
  }
@@ -3629,7 +3696,7 @@ async function trafficBackfill(project, opts) {
3629
3696
  days: opts.days
3630
3697
  });
3631
3698
  if (!opts.wait) {
3632
- if (opts.format === "json") {
3699
+ if (isMachineFormat(opts.format)) {
3633
3700
  console.log(JSON.stringify(submitted, null, 2));
3634
3701
  return;
3635
3702
  }
@@ -3661,7 +3728,7 @@ async function trafficBackfill(project, opts) {
3661
3728
  details: { project, runId: submitted.runId }
3662
3729
  });
3663
3730
  }
3664
- if (opts.format === "json") {
3731
+ if (isMachineFormat(opts.format)) {
3665
3732
  console.log(JSON.stringify({ ...submitted, finalStatus: final.status, finalRun: final }, null, 2));
3666
3733
  return;
3667
3734
  }
@@ -3697,7 +3764,7 @@ async function trafficSync(project, opts) {
3697
3764
  const result = await client.trafficSync(project, opts.source, {
3698
3765
  sinceMinutes: opts.sinceMinutes
3699
3766
  });
3700
- if (opts.format === "json") {
3767
+ if (isMachineFormat(opts.format)) {
3701
3768
  console.log(JSON.stringify(result, null, 2));
3702
3769
  return;
3703
3770
  }
@@ -3733,7 +3800,7 @@ async function trafficReset(project, opts) {
3733
3800
  }
3734
3801
  const client = getClient7();
3735
3802
  const updated = await client.trafficReset(project, opts.source);
3736
- if (opts.format === "json") {
3803
+ if (isMachineFormat(opts.format)) {
3737
3804
  console.log(JSON.stringify(updated, null, 2));
3738
3805
  return;
3739
3806
  }
@@ -3760,6 +3827,9 @@ async function trafficSources(project, opts) {
3760
3827
  if (opts.format === "json") {
3761
3828
  console.log(JSON.stringify(result, null, 2));
3762
3829
  return;
3830
+ } else if (opts.format === "jsonl") {
3831
+ emitJsonl(result.sources.map((source) => ({ project, ...source })));
3832
+ return;
3763
3833
  }
3764
3834
  if (result.sources.length === 0) {
3765
3835
  console.log(`No traffic sources connected for project "${project}".`);
@@ -3779,6 +3849,9 @@ async function trafficStatus(project, opts) {
3779
3849
  if (opts.format === "json") {
3780
3850
  console.log(JSON.stringify(result, null, 2));
3781
3851
  return;
3852
+ } else if (opts.format === "jsonl") {
3853
+ emitJsonl(details.map((detail) => ({ project, ...detail })));
3854
+ return;
3782
3855
  }
3783
3856
  if (details.length === 0) {
3784
3857
  console.log(`No traffic sources connected for project "${project}".`);
@@ -3864,6 +3937,14 @@ async function trafficEvents(project, opts) {
3864
3937
  if (opts.format === "json") {
3865
3938
  console.log(JSON.stringify(result, null, 2));
3866
3939
  return;
3940
+ } else if (opts.format === "jsonl") {
3941
+ emitJsonl(result.events.map((event) => ({
3942
+ project,
3943
+ windowStart: result.windowStart,
3944
+ windowEnd: result.windowEnd,
3945
+ ...event
3946
+ })));
3947
+ return;
3867
3948
  }
3868
3949
  console.log(`Traffic events for "${project}" ${result.windowStart} \u2192 ${result.windowEnd}`);
3869
3950
  console.log(` Crawler hits (window): ${result.totals.crawlerHits}`);
@@ -4151,7 +4232,7 @@ async function addCompetitors(project, domains, format) {
4151
4232
  const current = await client.appendCompetitors(project, domains);
4152
4233
  const currentDomains = current.map((c) => c.domain);
4153
4234
  const addedDomains = currentDomains.filter((domain) => requested.has(domain) && !existingSet.has(domain));
4154
- if (format === "json") {
4235
+ if (isMachineFormat(format)) {
4155
4236
  console.log(JSON.stringify({
4156
4237
  project,
4157
4238
  domains: currentDomains,
@@ -4174,7 +4255,7 @@ async function removeCompetitors(project, domains, format) {
4174
4255
  const current = await client.deleteCompetitors(project, domains);
4175
4256
  const currentSet = new Set(current.map((c) => c.domain));
4176
4257
  const removedDomains = existingDomains.filter((domain) => requested.has(domain) && !currentSet.has(domain));
4177
- if (format === "json") {
4258
+ if (isMachineFormat(format)) {
4178
4259
  console.log(JSON.stringify({
4179
4260
  project,
4180
4261
  domains: current.map((c) => c.domain),
@@ -4201,6 +4282,9 @@ async function listCompetitors(project, format) {
4201
4282
  if (format === "json") {
4202
4283
  console.log(JSON.stringify(comps, null, 2));
4203
4284
  return;
4285
+ } else if (format === "jsonl") {
4286
+ emitJsonl(comps.map((c) => ({ project, ...c })));
4287
+ return;
4204
4288
  }
4205
4289
  if (comps.length === 0) {
4206
4290
  console.log(`No competitors found for "${project}".`);
@@ -4337,7 +4421,7 @@ async function googleConnect(project, opts) {
4337
4421
  type: opts.type,
4338
4422
  publicUrl: opts.publicUrl
4339
4423
  });
4340
- if (opts.format === "json") {
4424
+ if (isMachineFormat(opts.format)) {
4341
4425
  console.log(JSON.stringify({
4342
4426
  project,
4343
4427
  type: opts.type,
@@ -4368,7 +4452,7 @@ Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} acc
4368
4452
  async function googleDisconnect(project, opts) {
4369
4453
  const client = getClient9();
4370
4454
  await client.googleDisconnect(project, opts.type);
4371
- if (opts.format === "json") {
4455
+ if (isMachineFormat(opts.format)) {
4372
4456
  console.log(JSON.stringify({ project, type: opts.type, disconnected: true }, null, 2));
4373
4457
  return;
4374
4458
  }
@@ -4380,6 +4464,9 @@ async function googleStatus(project, format) {
4380
4464
  if (format === "json") {
4381
4465
  console.log(JSON.stringify({ connections }, null, 2));
4382
4466
  return;
4467
+ } else if (format === "jsonl") {
4468
+ emitJsonl(connections.map((conn) => ({ project, ...conn })));
4469
+ return;
4383
4470
  }
4384
4471
  if (connections.length === 0) {
4385
4472
  console.log(`No Google connections for project "${project}".`);
@@ -4404,6 +4491,9 @@ async function googleProperties(project, format) {
4404
4491
  if (format === "json") {
4405
4492
  console.log(JSON.stringify({ sites }, null, 2));
4406
4493
  return;
4494
+ } else if (format === "jsonl") {
4495
+ emitJsonl(sites.map((site) => ({ project, ...site })));
4496
+ return;
4407
4497
  }
4408
4498
  if (sites.length === 0) {
4409
4499
  console.log("No verified sites found for this Google account.");
@@ -4422,7 +4512,7 @@ Use "canonry google set-property <project> <siteUrl>" to select a property.`);
4422
4512
  async function googleSetProperty(project, propertyUrl, format) {
4423
4513
  const client = getClient9();
4424
4514
  await client.googleSetProperty(project, "gsc", propertyUrl);
4425
- if (format === "json") {
4515
+ if (isMachineFormat(format)) {
4426
4516
  console.log(JSON.stringify({ project, type: "gsc", propertyUrl }, null, 2));
4427
4517
  return;
4428
4518
  }
@@ -4431,11 +4521,11 @@ async function googleSetProperty(project, propertyUrl, format) {
4431
4521
  async function googleSync(project, opts) {
4432
4522
  const client = getClient9();
4433
4523
  const run = await client.gscSync(project, { days: opts.days, full: opts.full });
4434
- if (!opts.wait && opts.format === "json") {
4524
+ if (!opts.wait && isMachineFormat(opts.format)) {
4435
4525
  console.log(JSON.stringify(run, null, 2));
4436
4526
  return;
4437
4527
  }
4438
- if (opts.format !== "json") {
4528
+ if (!isMachineFormat(opts.format)) {
4439
4529
  console.log(`GSC sync started (run ${run.id})`);
4440
4530
  }
4441
4531
  if (opts.wait) {
@@ -4451,7 +4541,7 @@ async function googleSync(project, opts) {
4451
4541
  failureMessage: "GSC sync failed.",
4452
4542
  details: { project }
4453
4543
  });
4454
- if (opts.format === "json") {
4544
+ if (isMachineFormat(opts.format)) {
4455
4545
  console.log(JSON.stringify({ ...run, status: current.status }, null, 2));
4456
4546
  return;
4457
4547
  }
@@ -4468,6 +4558,9 @@ async function googlePerformanceDaily(project, opts) {
4468
4558
  if (opts.format === "json") {
4469
4559
  console.log(JSON.stringify(data, null, 2));
4470
4560
  return;
4561
+ } else if (opts.format === "jsonl") {
4562
+ emitJsonl(data.daily.map((row) => ({ project, ...row })));
4563
+ return;
4471
4564
  }
4472
4565
  if (data.daily.length === 0) {
4473
4566
  console.log('No GSC data found in this window. Run "canonry google sync" first.');
@@ -4504,6 +4597,9 @@ async function googlePerformance(project, opts) {
4504
4597
  if (opts.format === "json") {
4505
4598
  console.log(JSON.stringify(rows, null, 2));
4506
4599
  return;
4600
+ } else if (opts.format === "jsonl") {
4601
+ emitJsonl(rows.map((row) => ({ project, ...row })));
4602
+ return;
4507
4603
  }
4508
4604
  if (rows.length === 0) {
4509
4605
  console.log('No GSC data found. Run "canonry google sync" first.');
@@ -4527,7 +4623,7 @@ async function googlePerformance(project, opts) {
4527
4623
  async function googleInspect(project, url, format) {
4528
4624
  const client = getClient9();
4529
4625
  const result = await client.gscInspect(project, url);
4530
- if (format === "json") {
4626
+ if (isMachineFormat(format)) {
4531
4627
  console.log(JSON.stringify(result, null, 2));
4532
4628
  return;
4533
4629
  }
@@ -4552,6 +4648,9 @@ async function googleInspections(project, opts) {
4552
4648
  if (opts.format === "json") {
4553
4649
  console.log(JSON.stringify(rows, null, 2));
4554
4650
  return;
4651
+ } else if (opts.format === "jsonl") {
4652
+ emitJsonl(rows.map((row) => ({ project, ...row })));
4653
+ return;
4555
4654
  }
4556
4655
  if (rows.length === 0) {
4557
4656
  console.log("No URL inspections found.");
@@ -4572,7 +4671,7 @@ async function googleInspections(project, opts) {
4572
4671
  async function googleCoverage(project, format) {
4573
4672
  const client = getClient9();
4574
4673
  const result = await client.gscCoverage(project);
4575
- if (format === "json") {
4674
+ if (isMachineFormat(format)) {
4576
4675
  console.log(JSON.stringify(result, null, 2));
4577
4676
  return;
4578
4677
  }
@@ -4618,7 +4717,7 @@ Index Coverage for "${project}"
4618
4717
  async function googleSetSitemap(project, sitemapUrl, format) {
4619
4718
  const client = getClient9();
4620
4719
  await client.googleSetSitemap(project, "gsc", sitemapUrl);
4621
- if (format === "json") {
4720
+ if (isMachineFormat(format)) {
4622
4721
  console.log(JSON.stringify({ project, type: "gsc", sitemapUrl }, null, 2));
4623
4722
  return;
4624
4723
  }
@@ -4630,6 +4729,9 @@ async function googleListSitemaps(project, opts) {
4630
4729
  if (opts.format === "json") {
4631
4730
  console.log(JSON.stringify(result, null, 2));
4632
4731
  return;
4732
+ } else if (opts.format === "jsonl") {
4733
+ emitJsonl(result.sitemaps.map((s) => ({ project, ...s })));
4734
+ return;
4633
4735
  }
4634
4736
  if (result.sitemaps.length === 0) {
4635
4737
  console.log(`No sitemaps found for project "${project}". Submit a sitemap in Google Search Console first.`);
@@ -4651,11 +4753,11 @@ async function googleInspectSitemap(project, opts) {
4651
4753
  const run = await client.gscInspectSitemap(project, {
4652
4754
  sitemapUrl: opts.sitemapUrl
4653
4755
  });
4654
- if (!opts.wait && opts.format === "json") {
4756
+ if (!opts.wait && isMachineFormat(opts.format)) {
4655
4757
  console.log(JSON.stringify(run, null, 2));
4656
4758
  return;
4657
4759
  }
4658
- if (opts.format !== "json") {
4760
+ if (!isMachineFormat(opts.format)) {
4659
4761
  console.log(`Sitemap inspection started (run ${run.id})`);
4660
4762
  }
4661
4763
  if (opts.wait) {
@@ -4671,7 +4773,7 @@ async function googleInspectSitemap(project, opts) {
4671
4773
  failureMessage: "Sitemap inspection failed.",
4672
4774
  details: { project }
4673
4775
  });
4674
- if (opts.format === "json") {
4776
+ if (isMachineFormat(opts.format)) {
4675
4777
  console.log(JSON.stringify({ ...run, status: current.status }, null, 2));
4676
4778
  return;
4677
4779
  }
@@ -4688,6 +4790,9 @@ async function googleCoverageHistory(project, opts) {
4688
4790
  if (opts.format === "json") {
4689
4791
  console.log(JSON.stringify(rows, null, 2));
4690
4792
  return;
4793
+ } else if (opts.format === "jsonl") {
4794
+ emitJsonl(rows.map((row) => ({ project, ...row })));
4795
+ return;
4691
4796
  }
4692
4797
  if (rows.length === 0) {
4693
4798
  console.log("No coverage history found. Run a GSC sync or sitemap inspection first.");
@@ -4707,11 +4812,11 @@ GSC Coverage History for "${project}" (${rows.length} snapshots):
4707
4812
  async function googleDiscoverSitemaps(project, opts) {
4708
4813
  const client = getClient9();
4709
4814
  const result = await client.gscDiscoverSitemaps(project);
4710
- if (!opts.wait && opts.format === "json") {
4815
+ if (!opts.wait && isMachineFormat(opts.format)) {
4711
4816
  console.log(JSON.stringify(result, null, 2));
4712
4817
  return;
4713
4818
  }
4714
- if (opts.format !== "json") {
4819
+ if (!isMachineFormat(opts.format)) {
4715
4820
  console.log(`
4716
4821
  Discovered ${result.sitemaps.length} sitemap(s) for project "${project}":
4717
4822
  `);
@@ -4739,7 +4844,7 @@ Primary sitemap: ${result.primarySitemapUrl}`);
4739
4844
  failureMessage: "Sitemap inspection failed.",
4740
4845
  details: { project }
4741
4846
  });
4742
- if (opts.format === "json") {
4847
+ if (isMachineFormat(opts.format)) {
4743
4848
  console.log(JSON.stringify({
4744
4849
  ...result,
4745
4850
  run: {
@@ -4771,7 +4876,7 @@ async function googleRequestIndexing(project, opts) {
4771
4876
  details: { command: "google.request-indexing" }
4772
4877
  });
4773
4878
  }
4774
- if (opts.format !== "json") {
4879
+ if (!isMachineFormat(opts.format)) {
4775
4880
  console.error(INDEXING_API_SCOPE_NOTICE);
4776
4881
  console.error();
4777
4882
  }
@@ -4828,7 +4933,7 @@ async function googleRequestIndexing(project, opts) {
4828
4933
  });
4829
4934
  }
4830
4935
  }
4831
- if (opts.format === "json") {
4936
+ if (isMachineFormat(opts.format)) {
4832
4937
  console.log(JSON.stringify({ ...result, ...opts.wait ? { indexingConfirmed } : {} }, null, 2));
4833
4938
  return;
4834
4939
  }
@@ -4854,7 +4959,7 @@ async function googleRequestIndexing(project, opts) {
4854
4959
  async function googleRefresh(project, format) {
4855
4960
  const client = getClient9();
4856
4961
  const run = await client.gscSync(project, {});
4857
- if (format !== "json") {
4962
+ if (!isMachineFormat(format)) {
4858
4963
  process.stderr.write("Refreshing GSC coverage data");
4859
4964
  }
4860
4965
  const current = await waitForRunStatus2(client, run.id, {
@@ -4869,7 +4974,7 @@ async function googleRefresh(project, format) {
4869
4974
  failureMessage: "GSC refresh failed.",
4870
4975
  details: { project }
4871
4976
  });
4872
- if (current.status === "partial" && format !== "json") {
4977
+ if (current.status === "partial" && !isMachineFormat(format)) {
4873
4978
  process.stderr.write("Refresh completed with some errors.\n");
4874
4979
  }
4875
4980
  await googleCoverage(project, format);
@@ -4880,6 +4985,9 @@ async function googleDeindexed(project, format) {
4880
4985
  if (format === "json") {
4881
4986
  console.log(JSON.stringify(rows, null, 2));
4882
4987
  return;
4988
+ } else if (format === "jsonl") {
4989
+ emitJsonl(rows.map((row) => ({ project, ...row })));
4990
+ return;
4883
4991
  }
4884
4992
  if (rows.length === 0) {
4885
4993
  console.log("No deindexed pages detected.");
@@ -5188,7 +5296,7 @@ function getClient10() {
5188
5296
  async function addKeywords(project, keywords, format) {
5189
5297
  const client = getClient10();
5190
5298
  await client.appendKeywords(project, keywords);
5191
- if (format === "json") {
5299
+ if (isMachineFormat(format)) {
5192
5300
  console.log(JSON.stringify({
5193
5301
  project,
5194
5302
  keywords,
@@ -5201,7 +5309,7 @@ async function addKeywords(project, keywords, format) {
5201
5309
  async function replaceKeywords(project, keywords, format) {
5202
5310
  const client = getClient10();
5203
5311
  await client.putKeywords(project, keywords);
5204
- if (format === "json") {
5312
+ if (isMachineFormat(format)) {
5205
5313
  console.log(JSON.stringify({
5206
5314
  project,
5207
5315
  keywords,
@@ -5217,7 +5325,7 @@ async function removeKeywords(project, keywords, format) {
5217
5325
  const existingSet = new Set(existing.map((k) => k.keyword));
5218
5326
  const removedKeywords = keywords.filter((k) => existingSet.has(k));
5219
5327
  await client.deleteKeywords(project, keywords);
5220
- if (format === "json") {
5328
+ if (isMachineFormat(format)) {
5221
5329
  console.log(JSON.stringify({
5222
5330
  project,
5223
5331
  keywords,
@@ -5234,6 +5342,9 @@ async function listKeywords(project, format) {
5234
5342
  if (format === "json") {
5235
5343
  console.log(JSON.stringify(kws, null, 2));
5236
5344
  return;
5345
+ } else if (format === "jsonl") {
5346
+ emitJsonl(kws.map((kw) => ({ project, ...kw })));
5347
+ return;
5237
5348
  }
5238
5349
  if (kws.length === 0) {
5239
5350
  console.log(`No key phrases found for "${project}".`);
@@ -5260,7 +5371,7 @@ async function importKeywords(project, filePath, format) {
5260
5371
  const content = fs2.readFileSync(filePath, "utf-8");
5261
5372
  const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5262
5373
  if (keywords.length === 0) {
5263
- if (format === "json") {
5374
+ if (isMachineFormat(format)) {
5264
5375
  console.log(JSON.stringify({
5265
5376
  project,
5266
5377
  filePath,
@@ -5274,7 +5385,7 @@ async function importKeywords(project, filePath, format) {
5274
5385
  }
5275
5386
  const client = getClient10();
5276
5387
  await client.appendKeywords(project, keywords);
5277
- if (format === "json") {
5388
+ if (isMachineFormat(format)) {
5278
5389
  console.log(JSON.stringify({
5279
5390
  project,
5280
5391
  filePath,
@@ -5289,7 +5400,7 @@ async function generateKeywords(project, provider, opts) {
5289
5400
  const client = getClient10();
5290
5401
  const result = await client.generateKeywords(project, provider, opts.count);
5291
5402
  const saved = Boolean(opts.save && result.keywords.length > 0);
5292
- if (opts.format !== "json") {
5403
+ if (!isMachineFormat(opts.format)) {
5293
5404
  console.log(`Generated ${result.keywords.length} key phrase(s) using ${result.provider}:
5294
5405
  `);
5295
5406
  for (const kw of result.keywords) {
@@ -5302,12 +5413,12 @@ To add these, run: canonry keyword add ${project} <phrase>...`);
5302
5413
  }
5303
5414
  if (saved) {
5304
5415
  await client.appendKeywords(project, result.keywords);
5305
- if (opts.format !== "json") {
5416
+ if (!isMachineFormat(opts.format)) {
5306
5417
  console.log(`
5307
5418
  Saved ${result.keywords.length} key phrase(s) to "${project}".`);
5308
5419
  }
5309
5420
  }
5310
- if (opts.format === "json") {
5421
+ if (isMachineFormat(opts.format)) {
5311
5422
  console.log(JSON.stringify({
5312
5423
  project,
5313
5424
  provider: result.provider,
@@ -5465,7 +5576,7 @@ function getClient11() {
5465
5576
  async function addQueries(project, queries2, format) {
5466
5577
  const client = getClient11();
5467
5578
  await client.appendQueries(project, queries2);
5468
- if (format === "json") {
5579
+ if (isMachineFormat(format)) {
5469
5580
  console.log(JSON.stringify({
5470
5581
  project,
5471
5582
  queries: queries2,
@@ -5477,7 +5588,7 @@ async function addQueries(project, queries2, format) {
5477
5588
  }
5478
5589
  async function replaceQueries(project, queries2, opts) {
5479
5590
  const client = getClient11();
5480
- const isJson = opts?.format === "json";
5591
+ const isJson = isMachineFormat(opts?.format);
5481
5592
  if (opts?.dryRun) {
5482
5593
  const preview = await client.previewReplaceQueries(project, queries2);
5483
5594
  if (isJson) {
@@ -5517,7 +5628,7 @@ async function removeQueries(project, queries2, format) {
5517
5628
  const existingSet = new Set(existing.map((q) => q.query));
5518
5629
  const removedQueries = queries2.filter((q) => existingSet.has(q));
5519
5630
  await client.deleteQueries(project, queries2);
5520
- if (format === "json") {
5631
+ if (isMachineFormat(format)) {
5521
5632
  console.log(JSON.stringify({
5522
5633
  project,
5523
5634
  queries: queries2,
@@ -5534,6 +5645,9 @@ async function listQueries(project, format) {
5534
5645
  if (format === "json") {
5535
5646
  console.log(JSON.stringify(qs, null, 2));
5536
5647
  return;
5648
+ } else if (format === "jsonl") {
5649
+ emitJsonl(qs.map((q) => ({ project, ...q })));
5650
+ return;
5537
5651
  }
5538
5652
  if (qs.length === 0) {
5539
5653
  console.log(`No queries found for "${project}".`);
@@ -5560,7 +5674,7 @@ async function importQueries(project, filePath, format) {
5560
5674
  const content = fs3.readFileSync(filePath, "utf-8");
5561
5675
  const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
5562
5676
  if (queries2.length === 0) {
5563
- if (format === "json") {
5677
+ if (isMachineFormat(format)) {
5564
5678
  console.log(JSON.stringify({
5565
5679
  project,
5566
5680
  filePath,
@@ -5574,7 +5688,7 @@ async function importQueries(project, filePath, format) {
5574
5688
  }
5575
5689
  const client = getClient11();
5576
5690
  await client.appendQueries(project, queries2);
5577
- if (format === "json") {
5691
+ if (isMachineFormat(format)) {
5578
5692
  console.log(JSON.stringify({
5579
5693
  project,
5580
5694
  filePath,
@@ -5589,7 +5703,7 @@ async function generateQueries(project, provider, opts) {
5589
5703
  const client = getClient11();
5590
5704
  const result = await client.generateQueries(project, provider, opts.count);
5591
5705
  const saved = Boolean(opts.save && result.queries.length > 0);
5592
- if (opts.format !== "json") {
5706
+ if (!isMachineFormat(opts.format)) {
5593
5707
  console.log(`Generated ${result.queries.length} ${result.queries.length === 1 ? "query" : "queries"} using ${result.provider}:
5594
5708
  `);
5595
5709
  for (const q of result.queries) {
@@ -5602,12 +5716,12 @@ To add these, run: canonry query add ${project} <query>...`);
5602
5716
  }
5603
5717
  if (saved) {
5604
5718
  await client.appendQueries(project, result.queries);
5605
- if (opts.format !== "json") {
5719
+ if (!isMachineFormat(opts.format)) {
5606
5720
  console.log(`
5607
5721
  Saved ${result.queries.length} ${result.queries.length === 1 ? "query" : "queries"} to "${project}".`);
5608
5722
  }
5609
5723
  }
5610
- if (opts.format === "json") {
5724
+ if (isMachineFormat(opts.format)) {
5611
5725
  console.log(JSON.stringify({
5612
5726
  project,
5613
5727
  provider: result.provider,
@@ -5986,7 +6100,7 @@ async function printMcpConfig(opts) {
5986
6100
  const serverName = opts.name?.trim() || "canonry";
5987
6101
  const entry = buildEntry({ binPath: opts.binPath, readOnly: opts.readOnly, platform: opts.platform });
5988
6102
  const snippet = renderClientSnippet(client, serverName, entry);
5989
- if (opts.format === "json") {
6103
+ if (isMachineFormat(opts.format)) {
5990
6104
  console.log(JSON.stringify({
5991
6105
  client: client.id,
5992
6106
  configPath: client.configPath(),
@@ -6000,7 +6114,7 @@ async function printMcpConfig(opts) {
6000
6114
  console.log(snippet);
6001
6115
  }
6002
6116
  function emitInstallResult(result, format) {
6003
- if (format === "json") {
6117
+ if (isMachineFormat(format)) {
6004
6118
  console.log(JSON.stringify(result, null, 2));
6005
6119
  return;
6006
6120
  }
@@ -6091,7 +6205,7 @@ async function addNotification(project, opts) {
6091
6205
  url: opts.webhook,
6092
6206
  events: opts.events
6093
6207
  });
6094
- if (opts.format === "json") {
6208
+ if (isMachineFormat(opts.format)) {
6095
6209
  console.log(JSON.stringify(result, null, 2));
6096
6210
  return;
6097
6211
  }
@@ -6104,6 +6218,9 @@ async function listNotifications(project, format) {
6104
6218
  if (format === "json") {
6105
6219
  console.log(JSON.stringify(results, null, 2));
6106
6220
  return;
6221
+ } else if (format === "jsonl") {
6222
+ emitJsonl(results);
6223
+ return;
6107
6224
  }
6108
6225
  if (results.length === 0) {
6109
6226
  console.log(`No notifications configured for "${project}"`);
@@ -6119,7 +6236,7 @@ async function listNotifications(project, format) {
6119
6236
  async function removeNotification(project, id, format) {
6120
6237
  const client = getClient12();
6121
6238
  await client.deleteNotification(project, id);
6122
- if (format === "json") {
6239
+ if (isMachineFormat(format)) {
6123
6240
  console.log(JSON.stringify({ project, id, removed: true }, null, 2));
6124
6241
  return;
6125
6242
  }
@@ -6128,7 +6245,7 @@ async function removeNotification(project, id, format) {
6128
6245
  async function testNotification(project, id, format) {
6129
6246
  const client = getClient12();
6130
6247
  const result = await client.testNotification(project, id);
6131
- if (format === "json") {
6248
+ if (isMachineFormat(format)) {
6132
6249
  console.log(JSON.stringify({ project, id, ...result }, null, 2));
6133
6250
  return;
6134
6251
  }
@@ -6148,8 +6265,12 @@ var EVENT_DESCRIPTIONS = {
6148
6265
  };
6149
6266
  function listEvents(format) {
6150
6267
  const events = notificationEventSchema.options;
6268
+ const catalog = events.map((e) => ({ event: e, description: EVENT_DESCRIPTIONS[e] ?? "" }));
6151
6269
  if (format === "json") {
6152
- console.log(JSON.stringify(events.map((e) => ({ event: e, description: EVENT_DESCRIPTIONS[e] ?? "" })), null, 2));
6270
+ console.log(JSON.stringify(catalog, null, 2));
6271
+ return;
6272
+ } else if (format === "jsonl") {
6273
+ emitJsonl(catalog);
6153
6274
  return;
6154
6275
  }
6155
6276
  console.log("Available notification events:\n");
@@ -6292,7 +6413,7 @@ async function applyConfigs(filePaths, format) {
6292
6413
  };
6293
6414
  }
6294
6415
  files.push(result);
6295
- if (format !== "json") {
6416
+ if (!isMachineFormat(format)) {
6296
6417
  for (const applied of result.applied) {
6297
6418
  console.log(`Applied config for "${applied.name}" (revision ${applied.configRevision})`);
6298
6419
  }
@@ -6311,7 +6432,7 @@ async function applyConfigs(filePaths, format) {
6311
6432
  details: summary
6312
6433
  });
6313
6434
  }
6314
- if (format === "json") {
6435
+ if (isMachineFormat(format)) {
6315
6436
  console.log(JSON.stringify(summary, null, 2));
6316
6437
  }
6317
6438
  }
@@ -6329,19 +6450,19 @@ async function showAnalytics(project, options) {
6329
6450
  case "metrics": {
6330
6451
  const data = await client.getAnalyticsMetrics(project, options.window);
6331
6452
  results.metrics = data;
6332
- if (options.format !== "json") printMetrics(data);
6453
+ if (!isMachineFormat(options.format)) printMetrics(data);
6333
6454
  break;
6334
6455
  }
6335
6456
  case "gaps": {
6336
6457
  const data = await client.getAnalyticsGaps(project, options.window);
6337
6458
  results.gaps = data;
6338
- if (options.format !== "json") printGaps(data);
6459
+ if (!isMachineFormat(options.format)) printGaps(data);
6339
6460
  break;
6340
6461
  }
6341
6462
  case "sources": {
6342
6463
  const data = await client.getAnalyticsSources(project, options.window);
6343
6464
  results.sources = data;
6344
- if (options.format !== "json") printSources(data);
6465
+ if (!isMachineFormat(options.format)) printSources(data);
6345
6466
  break;
6346
6467
  }
6347
6468
  default:
@@ -6356,7 +6477,7 @@ async function showAnalytics(project, options) {
6356
6477
  });
6357
6478
  }
6358
6479
  }
6359
- if (options.format === "json") {
6480
+ if (isMachineFormat(options.format)) {
6360
6481
  console.log(JSON.stringify(results, null, 2));
6361
6482
  }
6362
6483
  }
@@ -6383,6 +6504,21 @@ Citation Rate Trends (${data.window})`);
6383
6504
  console.log(` ${start} ${pct2(bucket.citationRate).padStart(6)} ${bar}`);
6384
6505
  }
6385
6506
  }
6507
+ const providersInBuckets = [...new Set(data.buckets.flatMap((b) => Object.keys(b.byProvider ?? {})))].sort();
6508
+ if (data.buckets.length > 0 && providersInBuckets.length > 0) {
6509
+ console.log(`
6510
+ By Provider Timeline:`);
6511
+ for (const provider of providersInBuckets) {
6512
+ console.log(` ${provider}:`);
6513
+ for (const bucket of data.buckets) {
6514
+ const metric = bucket.byProvider?.[provider];
6515
+ if (!metric) continue;
6516
+ const start = bucket.startDate.slice(0, 10);
6517
+ const bar = metric.total > 0 ? "\u2588".repeat(Math.round(metric.citationRate * 20)) : "";
6518
+ console.log(` ${start} ${pct2(metric.citationRate).padStart(6)} ${bar}`);
6519
+ }
6520
+ }
6521
+ }
6386
6522
  }
6387
6523
  function printGaps(data) {
6388
6524
  console.log(`
@@ -6437,6 +6573,13 @@ async function showEvidence(project, format) {
6437
6573
  }));
6438
6574
  console.log(JSON.stringify(enriched, null, 2));
6439
6575
  return;
6576
+ } else if (format === "jsonl") {
6577
+ emitJsonl(timeline.map((entry) => ({
6578
+ project,
6579
+ ...entry,
6580
+ cited: entry.runs[entry.runs.length - 1]?.citationState === CitationStates.cited
6581
+ })));
6582
+ return;
6440
6583
  }
6441
6584
  if (timeline.length === 0) {
6442
6585
  console.log('No query evidence yet. Trigger a run first with "canonry run".');
@@ -6466,7 +6609,7 @@ async function exportProject(project, opts) {
6466
6609
  const results = await loadLatestRunForExport(client, project);
6467
6610
  if (results) data.results = results;
6468
6611
  }
6469
- if (opts.format === "json") {
6612
+ if (isMachineFormat(opts.format)) {
6470
6613
  console.log(JSON.stringify(data, null, 2));
6471
6614
  return;
6472
6615
  }
@@ -6499,6 +6642,10 @@ async function showHistory(project, format) {
6499
6642
  console.log(JSON.stringify(entries, null, 2));
6500
6643
  return;
6501
6644
  }
6645
+ if (format === "jsonl") {
6646
+ emitJsonl(entries.map((entry) => ({ project, ...entry })));
6647
+ return;
6648
+ }
6502
6649
  if (entries.length === 0) {
6503
6650
  console.log(`No audit history for "${project}".`);
6504
6651
  return;
@@ -6534,7 +6681,7 @@ async function showStatus(project, format) {
6534
6681
  const client = getClient16();
6535
6682
  const projectData = await client.getProject(project);
6536
6683
  const latest = await getLatestRunSummary(client, project);
6537
- if (format === "json") {
6684
+ if (isMachineFormat(format)) {
6538
6685
  let runs = [];
6539
6686
  try {
6540
6687
  runs = await client.listRuns(project);
@@ -6675,7 +6822,7 @@ async function createProject(name, opts) {
6675
6822
  country: opts.country,
6676
6823
  language: opts.language
6677
6824
  });
6678
- if (opts.format === "json") {
6825
+ if (isMachineFormat(opts.format)) {
6679
6826
  console.log(JSON.stringify(result, null, 2));
6680
6827
  return;
6681
6828
  }
@@ -6688,6 +6835,10 @@ async function listProjects(format) {
6688
6835
  console.log(JSON.stringify(projects2, null, 2));
6689
6836
  return;
6690
6837
  }
6838
+ if (format === "jsonl") {
6839
+ emitJsonl(projects2);
6840
+ return;
6841
+ }
6691
6842
  if (projects2.length === 0) {
6692
6843
  console.log("No projects found.");
6693
6844
  return;
@@ -6712,7 +6863,7 @@ async function listProjects(format) {
6712
6863
  async function showProject(name, format) {
6713
6864
  const client = getClient17();
6714
6865
  const project = await client.getProject(name);
6715
- if (format === "json") {
6866
+ if (isMachineFormat(format)) {
6716
6867
  console.log(JSON.stringify(project, null, 2));
6717
6868
  return;
6718
6869
  }
@@ -6769,7 +6920,7 @@ async function updateProjectSettings(name, opts) {
6769
6920
  country: opts.country ?? project.country,
6770
6921
  language: opts.language ?? project.language
6771
6922
  });
6772
- if (opts.format === "json") {
6923
+ if (isMachineFormat(opts.format)) {
6773
6924
  console.log(JSON.stringify(result, null, 2));
6774
6925
  return;
6775
6926
  }
@@ -6777,7 +6928,7 @@ async function updateProjectSettings(name, opts) {
6777
6928
  }
6778
6929
  async function deleteProject(name, opts) {
6779
6930
  const client = getClient17();
6780
- const isJson = opts?.format === "json";
6931
+ const isJson = isMachineFormat(opts?.format);
6781
6932
  if (opts?.dryRun) {
6782
6933
  const preview = await client.previewProjectDelete(name);
6783
6934
  if (isJson) {
@@ -6814,7 +6965,7 @@ async function addLocation(project, opts) {
6814
6965
  country: opts.country,
6815
6966
  timezone: opts.timezone
6816
6967
  });
6817
- if (opts.format === "json") {
6968
+ if (isMachineFormat(opts.format)) {
6818
6969
  console.log(JSON.stringify(location, null, 2));
6819
6970
  return;
6820
6971
  }
@@ -6827,6 +6978,14 @@ async function listLocations(project, format) {
6827
6978
  console.log(JSON.stringify(result, null, 2));
6828
6979
  return;
6829
6980
  }
6981
+ if (format === "jsonl") {
6982
+ emitJsonl(result.locations.map((loc) => ({
6983
+ project,
6984
+ isDefault: loc.label === result.defaultLocation,
6985
+ ...loc
6986
+ })));
6987
+ return;
6988
+ }
6830
6989
  if (result.locations.length === 0) {
6831
6990
  console.log(`No locations configured for "${project}".`);
6832
6991
  return;
@@ -6849,7 +7008,7 @@ async function listLocations(project, format) {
6849
7008
  async function removeLocation(project, label, format) {
6850
7009
  const client = getClient17();
6851
7010
  await client.removeLocation(project, label);
6852
- if (format === "json") {
7011
+ if (isMachineFormat(format)) {
6853
7012
  console.log(JSON.stringify({ project, label, removed: true }, null, 2));
6854
7013
  return;
6855
7014
  }
@@ -6858,7 +7017,7 @@ async function removeLocation(project, label, format) {
6858
7017
  async function setDefaultLocation(project, label, format) {
6859
7018
  const client = getClient17();
6860
7019
  const result = await client.setDefaultLocation(project, label);
6861
- if (format === "json") {
7020
+ if (isMachineFormat(format)) {
6862
7021
  console.log(JSON.stringify({ project, ...result }, null, 2));
6863
7022
  return;
6864
7023
  }
@@ -7054,7 +7213,7 @@ async function runReportCommand(project, opts = {}) {
7054
7213
  const client = createApiClient();
7055
7214
  const report = await client.getReport(project);
7056
7215
  const audience = opts.audience ?? "agency";
7057
- if (opts.format === "json") {
7216
+ if (isMachineFormat(opts.format)) {
7058
7217
  console.log(JSON.stringify(report, null, 2));
7059
7218
  return;
7060
7219
  }
@@ -7127,7 +7286,7 @@ async function triggerRun(project, opts) {
7127
7286
  const response = await client.triggerRun(project, body);
7128
7287
  if (Array.isArray(response)) {
7129
7288
  const locationRuns = response;
7130
- if (opts?.format === "json") {
7289
+ if (isMachineFormat(opts?.format)) {
7131
7290
  if (opts?.wait) {
7132
7291
  const settled = await Promise.all(
7133
7292
  locationRuns.map(async (r) => {
@@ -7175,7 +7334,7 @@ async function triggerRun(project, opts) {
7175
7334
  if (opts?.wait && run.id && !TERMINAL_STATUSES.has(run.status)) {
7176
7335
  process.stderr.write(`Run ${run.id} started`);
7177
7336
  const result = await pollRun2(client, run.id);
7178
- if (opts?.format === "json") {
7337
+ if (isMachineFormat(opts?.format)) {
7179
7338
  console.log(JSON.stringify(result, null, 2));
7180
7339
  } else {
7181
7340
  process.stderr.write("\n");
@@ -7185,14 +7344,14 @@ async function triggerRun(project, opts) {
7185
7344
  }
7186
7345
  if (opts?.wait && (TERMINAL_STATUSES.has(run.status) || !run.id)) {
7187
7346
  const result = run.id ? await client.getRun(run.id) : run;
7188
- if (opts?.format === "json") {
7347
+ if (isMachineFormat(opts?.format)) {
7189
7348
  console.log(JSON.stringify(result, null, 2));
7190
7349
  } else {
7191
7350
  printRunDetail(result);
7192
7351
  }
7193
7352
  return;
7194
7353
  }
7195
- if (opts?.format === "json") {
7354
+ if (isMachineFormat(opts?.format)) {
7196
7355
  console.log(JSON.stringify(run, null, 2));
7197
7356
  return;
7198
7357
  }
@@ -7207,7 +7366,7 @@ async function triggerRunAll(opts) {
7207
7366
  const client = getClient18();
7208
7367
  const projects2 = await client.listProjects();
7209
7368
  if (projects2.length === 0) {
7210
- if (opts?.format === "json") {
7369
+ if (isMachineFormat(opts?.format)) {
7211
7370
  console.log("[]");
7212
7371
  } else {
7213
7372
  console.log("No projects found.");
@@ -7259,7 +7418,7 @@ async function triggerRunAll(opts) {
7259
7418
  process.stderr.write("\n");
7260
7419
  }
7261
7420
  }
7262
- if (opts?.format === "json") {
7421
+ if (isMachineFormat(opts?.format)) {
7263
7422
  console.log(JSON.stringify(results, null, 2));
7264
7423
  return;
7265
7424
  }
@@ -7312,7 +7471,7 @@ To cancel by ID : canonry run cancel ${project} <run-id>`,
7312
7471
  targetId = active.id;
7313
7472
  }
7314
7473
  const result = await client.cancelRun(targetId);
7315
- if (format === "json") {
7474
+ if (isMachineFormat(format)) {
7316
7475
  console.log(JSON.stringify(result, null, 2));
7317
7476
  return;
7318
7477
  }
@@ -7321,7 +7480,7 @@ To cancel by ID : canonry run cancel ${project} <run-id>`,
7321
7480
  async function showRun(id, format) {
7322
7481
  const client = getClient18();
7323
7482
  const run = await client.getRun(id);
7324
- if (format === "json") {
7483
+ if (isMachineFormat(format)) {
7325
7484
  console.log(JSON.stringify(run, null, 2));
7326
7485
  return;
7327
7486
  }
@@ -7333,6 +7492,9 @@ async function listRuns(project, opts) {
7333
7492
  if (opts?.format === "json") {
7334
7493
  console.log(JSON.stringify(runs, null, 2));
7335
7494
  return;
7495
+ } else if (opts?.format === "jsonl") {
7496
+ emitJsonl(runs.map((run) => ({ project, ...run })));
7497
+ return;
7336
7498
  }
7337
7499
  if (runs.length === 0) {
7338
7500
  console.log(`No runs found for "${project}".`);
@@ -7513,7 +7675,7 @@ async function setSchedule(project, opts) {
7513
7675
  if (opts.timezone) body.timezone = opts.timezone;
7514
7676
  if (opts.providers?.length) body.providers = opts.providers;
7515
7677
  const result = await client.putSchedule(project, body);
7516
- if (opts.format === "json") {
7678
+ if (isMachineFormat(opts.format)) {
7517
7679
  console.log(JSON.stringify(result, null, 2));
7518
7680
  return;
7519
7681
  }
@@ -7523,7 +7685,7 @@ async function setSchedule(project, opts) {
7523
7685
  async function showSchedule(project, format, kind) {
7524
7686
  const client = getClient19();
7525
7687
  const result = await client.getSchedule(project, kind);
7526
- if (format === "json") {
7688
+ if (isMachineFormat(format)) {
7527
7689
  console.log(JSON.stringify(result, null, 2));
7528
7690
  return;
7529
7691
  }
@@ -7538,7 +7700,7 @@ async function enableSchedule(project, format, kind) {
7538
7700
  if (current.providers.length) body.providers = current.providers;
7539
7701
  if (current.sourceId) body.sourceId = current.sourceId;
7540
7702
  const result = await client.putSchedule(project, body);
7541
- if (format === "json") {
7703
+ if (isMachineFormat(format)) {
7542
7704
  console.log(JSON.stringify(result, null, 2));
7543
7705
  return;
7544
7706
  }
@@ -7553,7 +7715,7 @@ async function disableSchedule(project, format, kind) {
7553
7715
  if (current.providers.length) body.providers = current.providers;
7554
7716
  if (current.sourceId) body.sourceId = current.sourceId;
7555
7717
  const result = await client.putSchedule(project, body);
7556
- if (format === "json") {
7718
+ if (isMachineFormat(format)) {
7557
7719
  console.log(JSON.stringify(result, null, 2));
7558
7720
  return;
7559
7721
  }
@@ -7563,7 +7725,7 @@ async function removeSchedule(project, format, kind) {
7563
7725
  const client = getClient19();
7564
7726
  await client.deleteSchedule(project, kind);
7565
7727
  const resolvedKind = kind ?? "answer-visibility";
7566
- if (format === "json") {
7728
+ if (isMachineFormat(format)) {
7567
7729
  console.log(JSON.stringify({ project, kind: resolvedKind, removed: true }, null, 2));
7568
7730
  return;
7569
7731
  }
@@ -7595,7 +7757,7 @@ function printSchedule(s) {
7595
7757
  var SCHEDULE_CLI_COMMANDS = [
7596
7758
  {
7597
7759
  path: ["schedule", "set"],
7598
- usage: "canonry schedule set <project> (--preset <preset> | --cron <expr>) [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--source <id>] [--timezone <tz>] [--provider <name>...] [--format json]",
7760
+ usage: "canonry schedule set <project> (--preset <preset> | --cron <expr>) [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--source <id>] [--timezone <tz>] [--provider <name>...] [--format json]",
7599
7761
  options: {
7600
7762
  preset: stringOption(),
7601
7763
  cron: stringOption(),
@@ -7605,7 +7767,7 @@ var SCHEDULE_CLI_COMMANDS = [
7605
7767
  provider: multiStringOption()
7606
7768
  },
7607
7769
  run: async (input) => {
7608
- const usage = "canonry schedule set <project> (--preset <preset> | --cron <expr>) [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--source <id>] [--timezone <tz>] [--provider <name>...] [--format json]";
7770
+ const usage = "canonry schedule set <project> (--preset <preset> | --cron <expr>) [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--source <id>] [--timezone <tz>] [--provider <name>...] [--format json]";
7609
7771
  const project = requireProject(input, "schedule.set", usage);
7610
7772
  if (!getString(input.values, "preset") && !getString(input.values, "cron")) {
7611
7773
  throw usageError("Error: --preset or --cron is required", {
@@ -7630,7 +7792,7 @@ var SCHEDULE_CLI_COMMANDS = [
7630
7792
  },
7631
7793
  {
7632
7794
  path: ["schedule", "show"],
7633
- usage: "canonry schedule show <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--format json]",
7795
+ usage: "canonry schedule show <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--format json]",
7634
7796
  options: { kind: stringOption() },
7635
7797
  run: async (input) => {
7636
7798
  const project = requireProject(input, "schedule.show", "canonry schedule show <project> [--kind ...]");
@@ -7639,7 +7801,7 @@ var SCHEDULE_CLI_COMMANDS = [
7639
7801
  },
7640
7802
  {
7641
7803
  path: ["schedule", "enable"],
7642
- usage: "canonry schedule enable <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--format json]",
7804
+ usage: "canonry schedule enable <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--format json]",
7643
7805
  options: { kind: stringOption() },
7644
7806
  run: async (input) => {
7645
7807
  const project = requireProject(input, "schedule.enable", "canonry schedule enable <project> [--kind ...]");
@@ -7648,7 +7810,7 @@ var SCHEDULE_CLI_COMMANDS = [
7648
7810
  },
7649
7811
  {
7650
7812
  path: ["schedule", "disable"],
7651
- usage: "canonry schedule disable <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--format json]",
7813
+ usage: "canonry schedule disable <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--format json]",
7652
7814
  options: { kind: stringOption() },
7653
7815
  run: async (input) => {
7654
7816
  const project = requireProject(input, "schedule.disable", "canonry schedule disable <project> [--kind ...]");
@@ -7657,7 +7819,7 @@ var SCHEDULE_CLI_COMMANDS = [
7657
7819
  },
7658
7820
  {
7659
7821
  path: ["schedule", "remove"],
7660
- usage: "canonry schedule remove <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh] [--format json]",
7822
+ usage: "canonry schedule remove <project> [--kind answer-visibility|traffic-sync|gbp-sync|data-refresh|backlinks-sync] [--format json]",
7661
7823
  options: { kind: stringOption() },
7662
7824
  run: async (input) => {
7663
7825
  const project = requireProject(input, "schedule.remove", "canonry schedule remove <project> [--kind ...]");
@@ -7685,7 +7847,7 @@ async function setProvider(name, opts) {
7685
7847
  const client = getClient20();
7686
7848
  const { format, ...payload } = opts;
7687
7849
  const result = await client.updateProvider(name, payload);
7688
- if (format === "json") {
7850
+ if (isMachineFormat(format)) {
7689
7851
  console.log(JSON.stringify(result, null, 2));
7690
7852
  return;
7691
7853
  }
@@ -7701,7 +7863,7 @@ async function showSettings(format) {
7701
7863
  const client = getClient20();
7702
7864
  const config = loadConfig();
7703
7865
  const settings = await client.getSettings();
7704
- if (format === "json") {
7866
+ if (isMachineFormat(format)) {
7705
7867
  console.log(JSON.stringify({
7706
7868
  ...settings,
7707
7869
  google: {
@@ -7732,7 +7894,7 @@ function setGoogleAuth(opts) {
7732
7894
  clientSecret: opts.clientSecret
7733
7895
  });
7734
7896
  saveConfigPatch(config);
7735
- if (opts.format === "json") {
7897
+ if (isMachineFormat(opts.format)) {
7736
7898
  console.log(JSON.stringify({
7737
7899
  configured: true,
7738
7900
  configPath: getConfigPath(),
@@ -8273,7 +8435,7 @@ async function createSnapshotReport(companyName, opts) {
8273
8435
  const pdfPath = opts.outputPath && !opts.md ? opts.outputPath : autoOutputPath(companyName, "pdf");
8274
8436
  savedPdfPath = await writeSnapshotPdf(report, pdfPath);
8275
8437
  }
8276
- if (opts.format === "json") {
8438
+ if (isMachineFormat(opts.format)) {
8277
8439
  console.log(JSON.stringify(report, null, 2));
8278
8440
  if (savedMdPath) process.stderr.write(`Saved markdown: ${savedMdPath}
8279
8441
  `);
@@ -8481,6 +8643,10 @@ async function listInsights(project, opts) {
8481
8643
  console.log(JSON.stringify(insights, null, 2));
8482
8644
  return;
8483
8645
  }
8646
+ if (opts.format === "jsonl") {
8647
+ emitJsonl(insights.map((insight) => ({ project, ...insight })));
8648
+ return;
8649
+ }
8484
8650
  if (insights.length === 0) {
8485
8651
  console.log("No insights found.");
8486
8652
  return;
@@ -8502,7 +8668,7 @@ async function listInsights(project, opts) {
8502
8668
  async function dismissInsight(project, id, opts) {
8503
8669
  const client = createApiClient();
8504
8670
  const result = await client.dismissInsight(project, id);
8505
- if (opts.format === "json") {
8671
+ if (isMachineFormat(opts.format)) {
8506
8672
  console.log(JSON.stringify(result, null, 2));
8507
8673
  return;
8508
8674
  }
@@ -8517,6 +8683,9 @@ async function showHealth(project, opts) {
8517
8683
  if (opts.format === "json") {
8518
8684
  console.log(JSON.stringify(snapshots, null, 2));
8519
8685
  return;
8686
+ } else if (opts.format === "jsonl") {
8687
+ emitJsonl(snapshots.map((snap) => ({ project, ...snap })));
8688
+ return;
8520
8689
  }
8521
8690
  if (snapshots.length === 0) {
8522
8691
  console.log("No health history available.");
@@ -8533,7 +8702,7 @@ async function showHealth(project, opts) {
8533
8702
  return;
8534
8703
  }
8535
8704
  const health = await client.getHealth(project);
8536
- if (opts.format === "json") {
8705
+ if (opts.format === "json" || opts.format === "jsonl") {
8537
8706
  console.log(JSON.stringify(health, null, 2));
8538
8707
  return;
8539
8708
  }
@@ -8560,7 +8729,7 @@ async function showOverview(project, opts) {
8560
8729
  location: opts.location,
8561
8730
  since: opts.since
8562
8731
  });
8563
- if (opts.format === "json") {
8732
+ if (isMachineFormat(opts.format)) {
8564
8733
  console.log(JSON.stringify(overview, null, 2));
8565
8734
  return;
8566
8735
  }
@@ -8570,7 +8739,7 @@ async function showAllOverviews(opts) {
8570
8739
  const client = createApiClient();
8571
8740
  const projects2 = await client.listProjects();
8572
8741
  if (projects2.length === 0) {
8573
- if (opts.format === "json") {
8742
+ if (isMachineFormat(opts.format)) {
8574
8743
  console.log("[]");
8575
8744
  return;
8576
8745
  }
@@ -8582,7 +8751,7 @@ async function showAllOverviews(opts) {
8582
8751
  (p) => client.getProjectOverview(p.name, { location: opts.location, since: opts.since })
8583
8752
  )
8584
8753
  );
8585
- if (opts.format === "json") {
8754
+ if (isMachineFormat(opts.format)) {
8586
8755
  console.log(JSON.stringify(overviews, null, 2));
8587
8756
  return;
8588
8757
  }
@@ -8739,7 +8908,7 @@ function pct(value) {
8739
8908
  async function searchProject(project, opts) {
8740
8909
  const client = createApiClient();
8741
8910
  const result = await client.searchProject(project, { q: opts.query, limit: opts.limit });
8742
- if (opts.format === "json") {
8911
+ if (isMachineFormat(opts.format)) {
8743
8912
  console.log(JSON.stringify(result, null, 2));
8744
8913
  return;
8745
8914
  }
@@ -8969,6 +9138,16 @@ async function listContentTargets(project, opts) {
8969
9138
  console.log(JSON.stringify(response, null, 2));
8970
9139
  return;
8971
9140
  }
9141
+ if (opts.format === "jsonl") {
9142
+ emitJsonl(
9143
+ response.targets.map((target) => ({
9144
+ project,
9145
+ latestRunId: response.contextMetrics.latestRunId,
9146
+ ...target
9147
+ }))
9148
+ );
9149
+ return;
9150
+ }
8972
9151
  if (response.targets.length === 0) {
8973
9152
  console.log("No content targets surfaced. (Run `canonry run` to generate fresh signal.)");
8974
9153
  return;
@@ -9005,6 +9184,10 @@ async function listContentSources(project, opts) {
9005
9184
  console.log(JSON.stringify(response, null, 2));
9006
9185
  return;
9007
9186
  }
9187
+ if (opts.format === "jsonl") {
9188
+ emitJsonl(response.sources.map((row) => ({ project, ...row })));
9189
+ return;
9190
+ }
9008
9191
  if (response.sources.length === 0) {
9009
9192
  console.log("No grounding sources captured yet.");
9010
9193
  return;
@@ -9029,6 +9212,10 @@ async function listContentGaps(project, opts) {
9029
9212
  console.log(JSON.stringify(response, null, 2));
9030
9213
  return;
9031
9214
  }
9215
+ if (opts.format === "jsonl") {
9216
+ emitJsonl(response.gaps.map((gap) => ({ project, ...gap })));
9217
+ return;
9218
+ }
9032
9219
  if (response.gaps.length === 0) {
9033
9220
  console.log("No competitor-only-cited queries detected.");
9034
9221
  return;
@@ -9381,7 +9568,7 @@ async function startDaemon(opts) {
9381
9568
  fs9.writeFileSync(pidPath, String(child.pid), "utf-8");
9382
9569
  const port = opts.port ?? "4100";
9383
9570
  const host = opts.host ?? "127.0.0.1";
9384
- if (format !== "json") {
9571
+ if (!isMachineFormat(format)) {
9385
9572
  process.stderr.write("Waiting for server to start...");
9386
9573
  }
9387
9574
  const ready = await waitForReady(host, port);
@@ -9401,11 +9588,11 @@ async function startDaemon(opts) {
9401
9588
  }
9402
9589
  });
9403
9590
  }
9404
- if (format !== "json") {
9591
+ if (!isMachineFormat(format)) {
9405
9592
  process.stderr.write("\n");
9406
9593
  }
9407
9594
  const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
9408
- if (format === "json") {
9595
+ if (isMachineFormat(format)) {
9409
9596
  console.log(JSON.stringify({
9410
9597
  started: true,
9411
9598
  pid: child.pid,
@@ -9420,7 +9607,7 @@ async function startDaemon(opts) {
9420
9607
  function stopDaemon(format = "text") {
9421
9608
  const pidPath = getPidPath();
9422
9609
  if (!fs9.existsSync(pidPath)) {
9423
- if (format === "json") {
9610
+ if (isMachineFormat(format)) {
9424
9611
  console.log(JSON.stringify({
9425
9612
  stopped: false,
9426
9613
  reason: "not_running"
@@ -9432,7 +9619,7 @@ function stopDaemon(format = "text") {
9432
9619
  }
9433
9620
  const pid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
9434
9621
  if (isNaN(pid)) {
9435
- if (format === "json") {
9622
+ if (isMachineFormat(format)) {
9436
9623
  console.log(JSON.stringify({
9437
9624
  stopped: false,
9438
9625
  reason: "invalid_pid",
@@ -9445,7 +9632,7 @@ function stopDaemon(format = "text") {
9445
9632
  return;
9446
9633
  }
9447
9634
  if (!isProcessAlive(pid)) {
9448
- if (format === "json") {
9635
+ if (isMachineFormat(format)) {
9449
9636
  console.log(JSON.stringify({
9450
9637
  stopped: false,
9451
9638
  reason: "stale_pid",
@@ -9461,7 +9648,7 @@ function stopDaemon(format = "text") {
9461
9648
  try {
9462
9649
  process.kill(pid, "SIGTERM");
9463
9650
  fs9.unlinkSync(pidPath);
9464
- if (format === "json") {
9651
+ if (isMachineFormat(format)) {
9465
9652
  console.log(JSON.stringify({
9466
9653
  stopped: true,
9467
9654
  pid
@@ -9521,11 +9708,11 @@ var DEFAULT_AGENT_MODELS = {
9521
9708
  };
9522
9709
  async function initCommand(opts) {
9523
9710
  const format = opts?.format ?? "text";
9524
- if (format !== "json") {
9711
+ if (!isMachineFormat(format)) {
9525
9712
  console.log("Initializing canonry...\n");
9526
9713
  }
9527
9714
  if (configExists() && !opts?.force) {
9528
- if (format === "json") {
9715
+ if (isMachineFormat(format)) {
9529
9716
  console.log(JSON.stringify({
9530
9717
  initialized: false,
9531
9718
  reason: "config_exists",
@@ -9567,7 +9754,7 @@ async function initCommand(opts) {
9567
9754
  const nonInteractive = !!(envProviders.gemini || envProviders.openai || envProviders.claude || envProviders.perplexity || envProviders.local || envGoogleConfigured);
9568
9755
  const providers = {};
9569
9756
  let google;
9570
- if (format === "json" && !nonInteractive) {
9757
+ if (isMachineFormat(format) && !nonInteractive) {
9571
9758
  throw new CliError({
9572
9759
  code: "INIT_JSON_REQUIRES_NON_INTERACTIVE",
9573
9760
  message: "--format json requires non-interactive provider configuration via flags or environment variables.",
@@ -9708,7 +9895,7 @@ async function initCommand(opts) {
9708
9895
  }
9709
9896
  }
9710
9897
  const nextSteps = buildNextSteps();
9711
- if (format === "json") {
9898
+ if (isMachineFormat(format)) {
9712
9899
  console.log(JSON.stringify({
9713
9900
  initialized: true,
9714
9901
  configPath: getConfigPath(),
@@ -9767,7 +9954,7 @@ ${mcpTip}`);
9767
9954
  agentLLM = { provider, key, model };
9768
9955
  }
9769
9956
  }
9770
- if (format !== "json") {
9957
+ if (!isMachineFormat(format)) {
9771
9958
  showFirstRunNotice();
9772
9959
  console.log("\nNext steps:");
9773
9960
  for (const line of nextSteps) {
@@ -9871,7 +10058,7 @@ Received ${signal}, stopping server...`);
9871
10058
  try {
9872
10059
  await app.listen({ host, port });
9873
10060
  const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
9874
- if (format === "json") {
10061
+ if (isMachineFormat(format)) {
9875
10062
  console.log(JSON.stringify({
9876
10063
  started: true,
9877
10064
  host,
@@ -9919,7 +10106,7 @@ function telemetryCommand(subcommand, format = "text") {
9919
10106
  if (process.env.CANONRY_TELEMETRY_DISABLED === "1") {
9920
10107
  payload.enabled = false;
9921
10108
  payload.reason = "CANONRY_TELEMETRY_DISABLED";
9922
- if (format === "json") {
10109
+ if (isMachineFormat(format)) {
9923
10110
  console.log(JSON.stringify(payload, null, 2));
9924
10111
  return;
9925
10112
  }
@@ -9929,7 +10116,7 @@ function telemetryCommand(subcommand, format = "text") {
9929
10116
  if (process.env.DO_NOT_TRACK === "1") {
9930
10117
  payload.enabled = false;
9931
10118
  payload.reason = "DO_NOT_TRACK";
9932
- if (format === "json") {
10119
+ if (isMachineFormat(format)) {
9933
10120
  console.log(JSON.stringify(payload, null, 2));
9934
10121
  return;
9935
10122
  }
@@ -9939,7 +10126,7 @@ function telemetryCommand(subcommand, format = "text") {
9939
10126
  if (process.env.CI) {
9940
10127
  payload.enabled = false;
9941
10128
  payload.reason = "CI";
9942
- if (format === "json") {
10129
+ if (isMachineFormat(format)) {
9943
10130
  console.log(JSON.stringify(payload, null, 2));
9944
10131
  return;
9945
10132
  }
@@ -9948,7 +10135,7 @@ function telemetryCommand(subcommand, format = "text") {
9948
10135
  }
9949
10136
  if (!configExists()) {
9950
10137
  payload.reason = "NO_CONFIG";
9951
- if (format === "json") {
10138
+ if (isMachineFormat(format)) {
9952
10139
  console.log(JSON.stringify(payload, null, 2));
9953
10140
  return;
9954
10141
  }
@@ -9961,7 +10148,7 @@ function telemetryCommand(subcommand, format = "text") {
9961
10148
  if (config.anonymousId) {
9962
10149
  payload.anonymousIdMasked = config.anonymousId.slice(0, 8) + "...";
9963
10150
  }
9964
- if (format === "json") {
10151
+ if (isMachineFormat(format)) {
9965
10152
  console.log(JSON.stringify(payload, null, 2));
9966
10153
  return;
9967
10154
  }
@@ -9987,7 +10174,7 @@ function telemetryCommand(subcommand, format = "text") {
9987
10174
  const config = loadConfig();
9988
10175
  config.telemetry = true;
9989
10176
  saveConfigPatch(config);
9990
- if (format === "json") {
10177
+ if (isMachineFormat(format)) {
9991
10178
  console.log(JSON.stringify({
9992
10179
  enabled: true,
9993
10180
  configPath: getConfigPath()
@@ -10011,7 +10198,7 @@ function telemetryCommand(subcommand, format = "text") {
10011
10198
  const config = loadConfig();
10012
10199
  config.telemetry = false;
10013
10200
  saveConfigPatch(config);
10014
- if (format === "json") {
10201
+ if (isMachineFormat(format)) {
10015
10202
  console.log(JSON.stringify({
10016
10203
  enabled: false,
10017
10204
  configPath: getConfigPath()
@@ -10335,7 +10522,7 @@ async function wordpressConnect(project, opts) {
10335
10522
  appPassword,
10336
10523
  defaultEnv: opts.defaultEnv
10337
10524
  });
10338
- if (opts.format === "json") {
10525
+ if (isMachineFormat(opts.format)) {
10339
10526
  printJson2(result);
10340
10527
  return;
10341
10528
  }
@@ -10346,7 +10533,7 @@ async function wordpressConnect(project, opts) {
10346
10533
  async function wordpressDisconnect(project, format) {
10347
10534
  const client = getClient22();
10348
10535
  await client.wordpressDisconnect(project);
10349
- if (format === "json") {
10536
+ if (isMachineFormat(format)) {
10350
10537
  printJson2({ project, disconnected: true });
10351
10538
  return;
10352
10539
  }
@@ -10355,7 +10542,7 @@ async function wordpressDisconnect(project, format) {
10355
10542
  async function wordpressStatus(project, format) {
10356
10543
  const client = getClient22();
10357
10544
  const result = await client.wordpressStatus(project);
10358
- if (format === "json") {
10545
+ if (isMachineFormat(format)) {
10359
10546
  printJson2(result);
10360
10547
  return;
10361
10548
  }
@@ -10364,7 +10551,7 @@ async function wordpressStatus(project, format) {
10364
10551
  async function wordpressPages(project, opts) {
10365
10552
  const client = getClient22();
10366
10553
  const result = await client.wordpressPages(project, opts.env);
10367
- if (opts.format === "json") {
10554
+ if (isMachineFormat(opts.format)) {
10368
10555
  printJson2(result);
10369
10556
  return;
10370
10557
  }
@@ -10373,7 +10560,7 @@ async function wordpressPages(project, opts) {
10373
10560
  async function wordpressPage(project, slug, opts) {
10374
10561
  const client = getClient22();
10375
10562
  const result = await client.wordpressPage(project, slug, opts.env);
10376
- if (opts.format === "json") {
10563
+ if (isMachineFormat(opts.format)) {
10377
10564
  printJson2(result);
10378
10565
  return;
10379
10566
  }
@@ -10382,7 +10569,7 @@ async function wordpressPage(project, slug, opts) {
10382
10569
  async function wordpressCreatePage(project, body) {
10383
10570
  const client = getClient22();
10384
10571
  const result = await client.wordpressCreatePage(project, body);
10385
- if (body.format === "json") {
10572
+ if (isMachineFormat(body.format)) {
10386
10573
  printJson2(result);
10387
10574
  return;
10388
10575
  }
@@ -10393,7 +10580,7 @@ async function wordpressCreatePage(project, body) {
10393
10580
  async function wordpressUpdatePage(project, body) {
10394
10581
  const client = getClient22();
10395
10582
  const result = await client.wordpressUpdatePage(project, body);
10396
- if (body.format === "json") {
10583
+ if (isMachineFormat(body.format)) {
10397
10584
  printJson2(result);
10398
10585
  return;
10399
10586
  }
@@ -10404,7 +10591,7 @@ async function wordpressUpdatePage(project, body) {
10404
10591
  async function wordpressSetMeta(project, body) {
10405
10592
  const client = getClient22();
10406
10593
  const result = await client.wordpressSetMeta(project, body);
10407
- if (body.format === "json") {
10594
+ if (isMachineFormat(body.format)) {
10408
10595
  printJson2(result);
10409
10596
  return;
10410
10597
  }
@@ -10454,7 +10641,7 @@ async function wordpressBulkSetMeta(project, opts) {
10454
10641
  }
10455
10642
  const client = getClient22();
10456
10643
  const result = await client.wordpressBulkSetMeta(project, { entries, env: opts.env });
10457
- if (opts.format === "json") {
10644
+ if (isMachineFormat(opts.format)) {
10458
10645
  printJson2(result);
10459
10646
  return;
10460
10647
  }
@@ -10497,7 +10684,7 @@ Total: ${applied.length} applied, ${skipped.length} skipped, ${manual.length} ma
10497
10684
  async function wordpressSchema(project, slug, opts) {
10498
10685
  const client = getClient22();
10499
10686
  const result = await client.wordpressSchema(project, slug, opts.env);
10500
- if (opts.format === "json") {
10687
+ if (isMachineFormat(opts.format)) {
10501
10688
  printJson2(result);
10502
10689
  return;
10503
10690
  }
@@ -10508,7 +10695,7 @@ async function wordpressSchema(project, slug, opts) {
10508
10695
  async function wordpressSetSchema(project, body) {
10509
10696
  const client = getClient22();
10510
10697
  const result = await client.wordpressSetSchema(project, body);
10511
- if (body.format === "json") {
10698
+ if (isMachineFormat(body.format)) {
10512
10699
  printJson2(result);
10513
10700
  return;
10514
10701
  }
@@ -10556,7 +10743,7 @@ async function wordpressSchemaDeploy(project, opts) {
10556
10743
  }
10557
10744
  const client = getClient22();
10558
10745
  const result = await client.wordpressSchemaDeploy(project, { profile: parsed, env: opts.env });
10559
- if (opts.format === "json") {
10746
+ if (isMachineFormat(opts.format)) {
10560
10747
  printJson2(result);
10561
10748
  return;
10562
10749
  }
@@ -10595,7 +10782,7 @@ Total: ${deployed} deployed, ${stripped} stripped, ${skipped} skipped, ${failed}
10595
10782
  async function wordpressSchemaStatus(project, opts) {
10596
10783
  const client = getClient22();
10597
10784
  const result = await client.wordpressSchemaStatus(project, opts.env);
10598
- if (opts.format === "json") {
10785
+ if (isMachineFormat(opts.format)) {
10599
10786
  printJson2(result);
10600
10787
  return;
10601
10788
  }
@@ -10663,7 +10850,7 @@ async function wordpressOnboard(project, opts) {
10663
10850
  skipSchema: opts.skipSchema,
10664
10851
  skipSubmit: opts.skipSubmit
10665
10852
  });
10666
- if (opts.format === "json") {
10853
+ if (isMachineFormat(opts.format)) {
10667
10854
  printJson2(result);
10668
10855
  return;
10669
10856
  }
@@ -10679,7 +10866,7 @@ async function wordpressOnboard(project, opts) {
10679
10866
  async function wordpressLlmsTxt(project, opts) {
10680
10867
  const client = getClient22();
10681
10868
  const result = await client.wordpressLlmsTxt(project, opts.env);
10682
- if (opts.format === "json") {
10869
+ if (isMachineFormat(opts.format)) {
10683
10870
  printJson2(result);
10684
10871
  return;
10685
10872
  }
@@ -10690,7 +10877,7 @@ async function wordpressLlmsTxt(project, opts) {
10690
10877
  async function wordpressSetLlmsTxt(project, body) {
10691
10878
  const client = getClient22();
10692
10879
  const result = await client.wordpressSetLlmsTxt(project, body);
10693
- if (body.format === "json") {
10880
+ if (isMachineFormat(body.format)) {
10694
10881
  printJson2(result);
10695
10882
  return;
10696
10883
  }
@@ -10699,7 +10886,7 @@ async function wordpressSetLlmsTxt(project, body) {
10699
10886
  async function wordpressAudit(project, opts) {
10700
10887
  const client = getClient22();
10701
10888
  const result = await client.wordpressAudit(project, opts.env);
10702
- if (opts.format === "json") {
10889
+ if (isMachineFormat(opts.format)) {
10703
10890
  printJson2(result);
10704
10891
  return;
10705
10892
  }
@@ -10713,7 +10900,7 @@ async function wordpressAudit(project, opts) {
10713
10900
  async function wordpressDiff(project, slug, format) {
10714
10901
  const client = getClient22();
10715
10902
  const result = await client.wordpressDiff(project, slug);
10716
- if (format === "json") {
10903
+ if (isMachineFormat(format)) {
10717
10904
  printJson2(result);
10718
10905
  return;
10719
10906
  }
@@ -10722,7 +10909,7 @@ async function wordpressDiff(project, slug, format) {
10722
10909
  async function wordpressStagingStatus(project, format) {
10723
10910
  const client = getClient22();
10724
10911
  const result = await client.wordpressStagingStatus(project);
10725
- if (format === "json") {
10912
+ if (isMachineFormat(format)) {
10726
10913
  printJson2(result);
10727
10914
  return;
10728
10915
  }
@@ -10736,7 +10923,7 @@ async function wordpressStagingStatus(project, format) {
10736
10923
  async function wordpressStagingPush(project, format) {
10737
10924
  const client = getClient22();
10738
10925
  const result = await client.wordpressStagingPush(project);
10739
- if (format === "json") {
10926
+ if (isMachineFormat(format)) {
10740
10927
  printJson2(result);
10741
10928
  return;
10742
10929
  }
@@ -11222,7 +11409,7 @@ async function agentAttach(opts) {
11222
11409
  const existing = await client.listNotifications(opts.project);
11223
11410
  const hasAgent = existing.some((n) => n.source === "agent");
11224
11411
  if (hasAgent) {
11225
- if (opts.format === "json") {
11412
+ if (isMachineFormat(opts.format)) {
11226
11413
  console.log(JSON.stringify({ status: "already-attached", project: opts.project }));
11227
11414
  } else {
11228
11415
  console.log(`Agent webhook already attached to "${opts.project}"`);
@@ -11235,7 +11422,7 @@ async function agentAttach(opts) {
11235
11422
  events: [...AGENT_WEBHOOK_EVENTS],
11236
11423
  source: "agent"
11237
11424
  });
11238
- if (opts.format === "json") {
11425
+ if (isMachineFormat(opts.format)) {
11239
11426
  console.log(JSON.stringify({ status: "attached", project: opts.project, notificationId: result.id }));
11240
11427
  } else {
11241
11428
  console.log(`Agent webhook attached to "${opts.project}" (${opts.url})`);
@@ -11246,7 +11433,7 @@ async function agentDetach(opts) {
11246
11433
  const existing = await client.listNotifications(opts.project);
11247
11434
  const agentNotif = existing.find((n) => n.source === "agent");
11248
11435
  if (!agentNotif) {
11249
- if (opts.format === "json") {
11436
+ if (isMachineFormat(opts.format)) {
11250
11437
  console.log(JSON.stringify({ status: "not-attached", project: opts.project }));
11251
11438
  } else {
11252
11439
  console.log(`No agent webhook found on "${opts.project}"`);
@@ -11254,7 +11441,7 @@ async function agentDetach(opts) {
11254
11441
  return;
11255
11442
  }
11256
11443
  await client.deleteNotification(opts.project, agentNotif.id);
11257
- if (opts.format === "json") {
11444
+ if (isMachineFormat(opts.format)) {
11258
11445
  console.log(JSON.stringify({ status: "detached", project: opts.project }));
11259
11446
  } else {
11260
11447
  console.log(`Agent webhook detached from "${opts.project}"`);
@@ -11262,9 +11449,14 @@ async function agentDetach(opts) {
11262
11449
  }
11263
11450
 
11264
11451
  // src/commands/agent-ask.ts
11452
+ function toFormat2(raw) {
11453
+ if (raw === "json") return "json";
11454
+ if (raw === "jsonl") return "jsonl";
11455
+ return "text";
11456
+ }
11265
11457
  async function agentAsk(opts) {
11266
- const format = opts.format === "json" ? "json" : "text";
11267
- const isJson = format === "json";
11458
+ const format = toFormat2(opts.format);
11459
+ const isJson = isMachineFormat(format);
11268
11460
  const controller = new AbortController();
11269
11461
  const onSigint = () => controller.abort();
11270
11462
  process.on("SIGINT", onSigint);
@@ -11369,13 +11561,22 @@ function renderEvent(event, isJson) {
11369
11561
 
11370
11562
  // src/commands/agent-providers.ts
11371
11563
  async function agentProviders(opts) {
11372
- const format = opts.format === "json" ? "json" : "text";
11564
+ const format = opts.format === "json" ? "json" : opts.format === "jsonl" ? "jsonl" : "text";
11373
11565
  try {
11374
11566
  const client = createApiClient();
11375
11567
  const res = await client.listAgentProviders(opts.project);
11376
11568
  if (format === "json") {
11377
11569
  console.log(JSON.stringify(res, null, 2));
11378
11570
  return;
11571
+ } else if (format === "jsonl") {
11572
+ emitJsonl(
11573
+ res.providers.map((provider) => ({
11574
+ project: opts.project,
11575
+ defaultProvider: res.defaultProvider,
11576
+ ...provider
11577
+ }))
11578
+ );
11579
+ return;
11379
11580
  }
11380
11581
  console.log(
11381
11582
  `Aero providers for ${opts.project} (default: ${res.defaultProvider ?? "none configured"})
@@ -11397,12 +11598,17 @@ async function agentProviders(opts) {
11397
11598
  }
11398
11599
 
11399
11600
  // src/commands/agent-transcript.ts
11601
+ function toFormat3(raw) {
11602
+ if (raw === "json") return "json";
11603
+ if (raw === "jsonl") return "jsonl";
11604
+ return "text";
11605
+ }
11400
11606
  async function agentTranscript(opts) {
11401
- const format = opts.format === "json" ? "json" : "text";
11607
+ const format = toFormat3(opts.format);
11402
11608
  try {
11403
11609
  const client = createApiClient();
11404
11610
  const transcript = await client.getAgentTranscript(opts.project);
11405
- if (format === "json") {
11611
+ if (isMachineFormat(format)) {
11406
11612
  console.log(JSON.stringify(transcript, null, 2));
11407
11613
  return;
11408
11614
  }
@@ -11436,11 +11642,11 @@ async function agentTranscript(opts) {
11436
11642
  }
11437
11643
  }
11438
11644
  async function agentTranscriptReset(opts) {
11439
- const format = opts.format === "json" ? "json" : "text";
11645
+ const format = toFormat3(opts.format);
11440
11646
  try {
11441
11647
  const client = createApiClient();
11442
11648
  await client.resetAgentTranscript(opts.project);
11443
- if (format === "json") {
11649
+ if (isMachineFormat(format)) {
11444
11650
  console.log(JSON.stringify({ status: "reset", project: opts.project }));
11445
11651
  } else {
11446
11652
  console.log(`Aero conversation reset for "${opts.project}".`);
@@ -11453,17 +11659,22 @@ async function agentTranscriptReset(opts) {
11453
11659
  }
11454
11660
 
11455
11661
  // src/commands/agent-memory.ts
11456
- function toFormat2(raw) {
11457
- return raw === "json" ? "json" : "text";
11662
+ function toFormat4(raw) {
11663
+ if (raw === "json") return "json";
11664
+ if (raw === "jsonl") return "jsonl";
11665
+ return "text";
11458
11666
  }
11459
11667
  async function agentMemoryList(opts) {
11460
- const format = toFormat2(opts.format);
11668
+ const format = toFormat4(opts.format);
11461
11669
  try {
11462
11670
  const client = createApiClient();
11463
11671
  const result = await client.listAgentMemory(opts.project);
11464
11672
  if (format === "json") {
11465
11673
  console.log(JSON.stringify(result, null, 2));
11466
11674
  return;
11675
+ } else if (format === "jsonl") {
11676
+ emitJsonl(result.entries.map((entry) => ({ project: opts.project, ...entry })));
11677
+ return;
11467
11678
  }
11468
11679
  if (result.entries.length === 0) {
11469
11680
  console.log(`No Aero memory notes for "${opts.project}".`);
@@ -11482,14 +11693,14 @@ async function agentMemoryList(opts) {
11482
11693
  }
11483
11694
  }
11484
11695
  async function agentMemorySet(opts) {
11485
- const format = toFormat2(opts.format);
11696
+ const format = toFormat4(opts.format);
11486
11697
  try {
11487
11698
  const client = createApiClient();
11488
11699
  const result = await client.setAgentMemory(opts.project, {
11489
11700
  key: opts.key,
11490
11701
  value: opts.value
11491
11702
  });
11492
- if (format === "json") {
11703
+ if (isMachineFormat(format)) {
11493
11704
  console.log(JSON.stringify(result, null, 2));
11494
11705
  return;
11495
11706
  }
@@ -11500,11 +11711,11 @@ async function agentMemorySet(opts) {
11500
11711
  }
11501
11712
  }
11502
11713
  async function agentMemoryForget(opts) {
11503
- const format = toFormat2(opts.format);
11714
+ const format = toFormat4(opts.format);
11504
11715
  try {
11505
11716
  const client = createApiClient();
11506
11717
  const result = await client.forgetAgentMemory(opts.project, opts.key);
11507
- if (format === "json") {
11718
+ if (isMachineFormat(format)) {
11508
11719
  console.log(JSON.stringify(result, null, 2));
11509
11720
  return;
11510
11721
  }
@@ -11788,7 +11999,8 @@ Admin:
11788
11999
  history <project> Show audit trail
11789
12000
 
11790
12001
  Global options:
11791
- --format json Machine-readable output (all commands)
12002
+ --format json Machine-readable output: one JSON document (all commands)
12003
+ --format jsonl Machine-readable output: one record per line, no jq needed
11792
12004
  --help, -h Show help (use with any command group)
11793
12005
  --version, -v Show version
11794
12006
 
@@ -11798,7 +12010,9 @@ var _require2 = createRequire2(import.meta.url);
11798
12010
  var { version: VERSION } = _require2("../package.json");
11799
12011
  function extractFormat(cmdArgs) {
11800
12012
  const idx = cmdArgs.indexOf("--format");
11801
- if (idx !== -1 && cmdArgs[idx + 1] === "json") return "json";
12013
+ const value = idx !== -1 ? cmdArgs[idx + 1] : void 0;
12014
+ if (value === "json") return "json";
12015
+ if (value === "jsonl") return "jsonl";
11802
12016
  return "text";
11803
12017
  }
11804
12018
  async function runCli(args = process.argv.slice(2)) {
@@ -11838,7 +12052,7 @@ async function runCli(args = process.argv.slice(2)) {
11838
12052
  ...setupState ? { setup_state: setupState } : {}
11839
12053
  });
11840
12054
  }
11841
- if (!isHelpRequest && command !== "telemetry") {
12055
+ if (!isHelpRequest && command !== "telemetry" && process.stderr.isTTY) {
11842
12056
  void checkLatestVersionForCli().then((update) => {
11843
12057
  if (!update) return;
11844
12058
  process.stderr.write(