@ainyc/canonry 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  setGoogleAuthConfig,
20
20
  showFirstRunNotice,
21
21
  trackEvent
22
- } from "./chunk-RU7RHCWK.js";
22
+ } from "./chunk-7RIIMCVP.js";
23
23
 
24
24
  // src/cli.ts
25
25
  import { parseArgs } from "util";
@@ -673,6 +673,19 @@ var ApiClient = class {
673
673
  async gscDiscoverSitemaps(project) {
674
674
  return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/discover-sitemaps`, {});
675
675
  }
676
+ // Analytics
677
+ async getAnalyticsMetrics(project, window) {
678
+ const qs = window ? `?window=${encodeURIComponent(window)}` : "";
679
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/metrics${qs}`);
680
+ }
681
+ async getAnalyticsGaps(project, window) {
682
+ const qs = window ? `?window=${encodeURIComponent(window)}` : "";
683
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/gaps${qs}`);
684
+ }
685
+ async getAnalyticsSources(project, window) {
686
+ const qs = window ? `?window=${encodeURIComponent(window)}` : "";
687
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/analytics/sources${qs}`);
688
+ }
676
689
  // Google Indexing API
677
690
  async googleRequestIndexing(project, body) {
678
691
  return this.request("POST", `/projects/${encodeURIComponent(project)}/google/indexing/request`, body);
@@ -1226,6 +1239,107 @@ async function showHistory(project, format) {
1226
1239
  }
1227
1240
  }
1228
1241
 
1242
+ // src/commands/analytics.ts
1243
+ function getClient8() {
1244
+ const config = loadConfig();
1245
+ return new ApiClient(config.apiUrl, config.apiKey);
1246
+ }
1247
+ async function showAnalytics(project, options) {
1248
+ const client = getClient8();
1249
+ const features = options.feature ? [options.feature] : ["metrics", "gaps", "sources"];
1250
+ const results = {};
1251
+ for (const feature of features) {
1252
+ switch (feature) {
1253
+ case "metrics": {
1254
+ const data = await client.getAnalyticsMetrics(project, options.window);
1255
+ results.metrics = data;
1256
+ if (options.format !== "json") printMetrics(data);
1257
+ break;
1258
+ }
1259
+ case "gaps": {
1260
+ const data = await client.getAnalyticsGaps(project, options.window);
1261
+ results.gaps = data;
1262
+ if (options.format !== "json") printGaps(data);
1263
+ break;
1264
+ }
1265
+ case "sources": {
1266
+ const data = await client.getAnalyticsSources(project, options.window);
1267
+ results.sources = data;
1268
+ if (options.format !== "json") printSources(data);
1269
+ break;
1270
+ }
1271
+ default:
1272
+ console.error(`Unknown feature: ${feature}. Use: metrics, gaps, sources`);
1273
+ process.exit(1);
1274
+ }
1275
+ }
1276
+ if (options.format === "json") {
1277
+ console.log(JSON.stringify(results, null, 2));
1278
+ }
1279
+ }
1280
+ function printMetrics(data) {
1281
+ console.log(`
1282
+ Citation Rate Trends (${data.window})`);
1283
+ console.log("\u2500".repeat(50));
1284
+ const pct = (n) => `${(n * 100).toFixed(1)}%`;
1285
+ console.log(` Overall: ${pct(data.overall.citationRate)} (${data.overall.cited}/${data.overall.total})`);
1286
+ console.log(` Trend: ${data.trend}`);
1287
+ if (Object.keys(data.byProvider).length > 0) {
1288
+ console.log(`
1289
+ By Provider:`);
1290
+ for (const [provider, metric] of Object.entries(data.byProvider)) {
1291
+ console.log(` ${provider.padEnd(10)} ${pct(metric.citationRate).padStart(6)} (${metric.cited}/${metric.total})`);
1292
+ }
1293
+ }
1294
+ if (data.buckets.length > 0) {
1295
+ console.log(`
1296
+ Timeline:`);
1297
+ for (const bucket of data.buckets) {
1298
+ const start = bucket.startDate.slice(0, 10);
1299
+ const bar = bucket.total > 0 ? "\u2588".repeat(Math.round(bucket.citationRate * 20)) : "";
1300
+ console.log(` ${start} ${pct(bucket.citationRate).padStart(6)} ${bar}`);
1301
+ }
1302
+ }
1303
+ }
1304
+ function printGaps(data) {
1305
+ console.log(`
1306
+ Brand Gap Analysis`);
1307
+ console.log("\u2500".repeat(50));
1308
+ console.log(` Cited: ${data.cited.length} | Gap: ${data.gap.length} | Uncited: ${data.uncited.length}`);
1309
+ if (data.gap.length > 0) {
1310
+ console.log(`
1311
+ Opportunity Gaps (competitors cited, you're not):`);
1312
+ for (const kw of data.gap) {
1313
+ const competitors = kw.competitorsCiting.join(", ");
1314
+ const cons = kw.consistency.totalRuns > 0 ? ` [cited ${kw.consistency.citedRuns}/${kw.consistency.totalRuns} runs]` : "";
1315
+ console.log(` \u2022 ${kw.keyword}${cons}`);
1316
+ console.log(` Competitors: ${competitors}`);
1317
+ }
1318
+ }
1319
+ if (data.cited.length > 0) {
1320
+ console.log(`
1321
+ Cited Keywords:`);
1322
+ for (const kw of data.cited) {
1323
+ const cons = kw.consistency.totalRuns > 0 ? ` [${kw.consistency.citedRuns}/${kw.consistency.totalRuns} runs]` : "";
1324
+ console.log(` \u2713 ${kw.keyword} (${kw.providers.join(", ")})${cons}`);
1325
+ }
1326
+ }
1327
+ }
1328
+ function printSources(data) {
1329
+ console.log(`
1330
+ Source Origin Breakdown`);
1331
+ console.log("\u2500".repeat(50));
1332
+ if (data.overall.length === 0) {
1333
+ console.log(" No source data available");
1334
+ return;
1335
+ }
1336
+ for (const cat of data.overall) {
1337
+ const pct = `${(cat.percentage * 100).toFixed(1)}%`;
1338
+ const domains = cat.topDomains.slice(0, 3).map((d) => d.domain).join(", ");
1339
+ console.log(` ${cat.label.padEnd(20)} ${pct.padStart(6)} (${cat.count}) ${domains}`);
1340
+ }
1341
+ }
1342
+
1229
1343
  // src/commands/apply.ts
1230
1344
  import fs4 from "fs";
1231
1345
  import { parseAllDocuments } from "yaml";
@@ -1280,12 +1394,12 @@ async function exportProject(project, opts) {
1280
1394
  }
1281
1395
 
1282
1396
  // src/commands/settings.ts
1283
- function getClient8() {
1397
+ function getClient9() {
1284
1398
  const config = loadConfig();
1285
1399
  return new ApiClient(config.apiUrl, config.apiKey);
1286
1400
  }
1287
1401
  async function setProvider(name, opts) {
1288
- const client = getClient8();
1402
+ const client = getClient9();
1289
1403
  const result = await client.updateProvider(name, opts);
1290
1404
  console.log(`Provider ${result.name} updated successfully.`);
1291
1405
  if (result.model) {
@@ -1296,7 +1410,7 @@ async function setProvider(name, opts) {
1296
1410
  }
1297
1411
  }
1298
1412
  async function showSettings(format) {
1299
- const client = getClient8();
1413
+ const client = getClient9();
1300
1414
  const config = loadConfig();
1301
1415
  const settings = await client.getSettings();
1302
1416
  if (format === "json") {
@@ -1334,12 +1448,12 @@ function setGoogleAuth(opts) {
1334
1448
  }
1335
1449
 
1336
1450
  // src/commands/schedule.ts
1337
- function getClient9() {
1451
+ function getClient10() {
1338
1452
  const config = loadConfig();
1339
1453
  return new ApiClient(config.apiUrl, config.apiKey);
1340
1454
  }
1341
1455
  async function setSchedule(project, opts) {
1342
- const client = getClient9();
1456
+ const client = getClient10();
1343
1457
  const body = {};
1344
1458
  if (opts.preset) body.preset = opts.preset;
1345
1459
  if (opts.cron) body.cron = opts.cron;
@@ -1350,7 +1464,7 @@ async function setSchedule(project, opts) {
1350
1464
  printSchedule(result);
1351
1465
  }
1352
1466
  async function showSchedule(project, format) {
1353
- const client = getClient9();
1467
+ const client = getClient10();
1354
1468
  const result = await client.getSchedule(project);
1355
1469
  if (format === "json") {
1356
1470
  console.log(JSON.stringify(result, null, 2));
@@ -1359,7 +1473,7 @@ async function showSchedule(project, format) {
1359
1473
  printSchedule(result);
1360
1474
  }
1361
1475
  async function enableSchedule(project) {
1362
- const client = getClient9();
1476
+ const client = getClient10();
1363
1477
  const current = await client.getSchedule(project);
1364
1478
  const body = { timezone: current.timezone };
1365
1479
  if (current.preset) body.preset = current.preset;
@@ -1369,7 +1483,7 @@ async function enableSchedule(project) {
1369
1483
  console.log(`Schedule enabled for "${project}"`);
1370
1484
  }
1371
1485
  async function disableSchedule(project) {
1372
- const client = getClient9();
1486
+ const client = getClient10();
1373
1487
  const current = await client.getSchedule(project);
1374
1488
  const body = { timezone: current.timezone, enabled: false };
1375
1489
  if (current.preset) body.preset = current.preset;
@@ -1379,7 +1493,7 @@ async function disableSchedule(project) {
1379
1493
  console.log(`Schedule disabled for "${project}"`);
1380
1494
  }
1381
1495
  async function removeSchedule(project) {
1382
- const client = getClient9();
1496
+ const client = getClient10();
1383
1497
  await client.deleteSchedule(project);
1384
1498
  console.log(`Schedule removed for "${project}"`);
1385
1499
  }
@@ -1401,12 +1515,12 @@ function printSchedule(s) {
1401
1515
  }
1402
1516
 
1403
1517
  // src/commands/notify.ts
1404
- function getClient10() {
1518
+ function getClient11() {
1405
1519
  const config = loadConfig();
1406
1520
  return new ApiClient(config.apiUrl, config.apiKey);
1407
1521
  }
1408
1522
  async function addNotification(project, opts) {
1409
- const client = getClient10();
1523
+ const client = getClient11();
1410
1524
  const result = await client.createNotification(project, {
1411
1525
  channel: "webhook",
1412
1526
  url: opts.webhook,
@@ -1416,7 +1530,7 @@ async function addNotification(project, opts) {
1416
1530
  printNotification(result);
1417
1531
  }
1418
1532
  async function listNotifications(project, format) {
1419
- const client = getClient10();
1533
+ const client = getClient11();
1420
1534
  const results = await client.listNotifications(project);
1421
1535
  if (format === "json") {
1422
1536
  console.log(JSON.stringify(results, null, 2));
@@ -1434,12 +1548,12 @@ async function listNotifications(project, format) {
1434
1548
  }
1435
1549
  }
1436
1550
  async function removeNotification(project, id) {
1437
- const client = getClient10();
1551
+ const client = getClient11();
1438
1552
  await client.deleteNotification(project, id);
1439
1553
  console.log(`Notification ${id} removed from "${project}"`);
1440
1554
  }
1441
1555
  async function testNotification(project, id) {
1442
- const client = getClient10();
1556
+ const client = getClient11();
1443
1557
  const result = await client.testNotification(project, id);
1444
1558
  if (result.ok) {
1445
1559
  console.log(`Test webhook delivered successfully (HTTP ${result.status})`);
@@ -1531,12 +1645,12 @@ function telemetryCommand(subcommand) {
1531
1645
  }
1532
1646
 
1533
1647
  // src/commands/google.ts
1534
- function getClient11() {
1648
+ function getClient12() {
1535
1649
  const config = loadConfig();
1536
1650
  return new ApiClient(config.apiUrl, config.apiKey);
1537
1651
  }
1538
1652
  async function googleConnect(project, opts) {
1539
- const client = getClient11();
1653
+ const client = getClient12();
1540
1654
  const { authUrl, redirectUri } = await client.googleConnect(project, {
1541
1655
  type: opts.type,
1542
1656
  publicUrl: opts.publicUrl
@@ -1561,12 +1675,12 @@ Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} acc
1561
1675
  }
1562
1676
  }
1563
1677
  async function googleDisconnect(project, opts) {
1564
- const client = getClient11();
1678
+ const client = getClient12();
1565
1679
  await client.googleDisconnect(project, opts.type);
1566
1680
  console.log(`Disconnected Google ${opts.type.toUpperCase()} from project "${project}".`);
1567
1681
  }
1568
1682
  async function googleStatus(project, format) {
1569
- const client = getClient11();
1683
+ const client = getClient12();
1570
1684
  const connections = await client.googleConnections(project);
1571
1685
  if (format === "json") {
1572
1686
  console.log(JSON.stringify({ connections }, null, 2));
@@ -1590,7 +1704,7 @@ async function googleStatus(project, format) {
1590
1704
  }
1591
1705
  }
1592
1706
  async function googleProperties(project, format) {
1593
- const client = getClient11();
1707
+ const client = getClient12();
1594
1708
  const { sites } = await client.googleProperties(project);
1595
1709
  if (format === "json") {
1596
1710
  console.log(JSON.stringify({ sites }, null, 2));
@@ -1611,12 +1725,12 @@ async function googleProperties(project, format) {
1611
1725
  Use "canonry google set-property <project> <siteUrl>" to select a property.`);
1612
1726
  }
1613
1727
  async function googleSetProperty(project, propertyUrl) {
1614
- const client = getClient11();
1728
+ const client = getClient12();
1615
1729
  await client.googleSetProperty(project, "gsc", propertyUrl);
1616
1730
  console.log(`GSC property set to "${propertyUrl}" for project "${project}".`);
1617
1731
  }
1618
1732
  async function googleSync(project, opts) {
1619
- const client = getClient11();
1733
+ const client = getClient12();
1620
1734
  const run = await client.gscSync(project, { days: opts.days, full: opts.full });
1621
1735
  if (opts.format === "json") {
1622
1736
  console.log(JSON.stringify(run, null, 2));
@@ -1647,7 +1761,7 @@ async function googleSync(project, opts) {
1647
1761
  }
1648
1762
  }
1649
1763
  async function googlePerformance(project, opts) {
1650
- const client = getClient11();
1764
+ const client = getClient12();
1651
1765
  const params = {};
1652
1766
  if (opts.days) {
1653
1767
  const end = /* @__PURE__ */ new Date();
@@ -1683,7 +1797,7 @@ async function googlePerformance(project, opts) {
1683
1797
  }
1684
1798
  }
1685
1799
  async function googleInspect(project, url, format) {
1686
- const client = getClient11();
1800
+ const client = getClient12();
1687
1801
  const result = await client.gscInspect(project, url);
1688
1802
  if (format === "json") {
1689
1803
  console.log(JSON.stringify(result, null, 2));
@@ -1703,7 +1817,7 @@ URL Inspection: ${result.url}
1703
1817
  console.log(` Inspected At: ${result.inspectedAt}`);
1704
1818
  }
1705
1819
  async function googleInspections(project, opts) {
1706
- const client = getClient11();
1820
+ const client = getClient12();
1707
1821
  const params = {};
1708
1822
  if (opts.url) params.url = opts.url;
1709
1823
  const rows = await client.gscInspections(project, Object.keys(params).length > 0 ? params : void 0);
@@ -1728,7 +1842,7 @@ async function googleInspections(project, opts) {
1728
1842
  }
1729
1843
  }
1730
1844
  async function googleCoverage(project, format) {
1731
- const client = getClient11();
1845
+ const client = getClient12();
1732
1846
  const result = await client.gscCoverage(project);
1733
1847
  if (format === "json") {
1734
1848
  console.log(JSON.stringify(result, null, 2));
@@ -1774,12 +1888,12 @@ Index Coverage for "${project}"
1774
1888
  }
1775
1889
  }
1776
1890
  async function googleSetSitemap(project, sitemapUrl) {
1777
- const client = getClient11();
1891
+ const client = getClient12();
1778
1892
  await client.googleSetSitemap(project, "gsc", sitemapUrl);
1779
1893
  console.log(`GSC sitemap URL set to "${sitemapUrl}" for project "${project}".`);
1780
1894
  }
1781
1895
  async function googleListSitemaps(project, opts) {
1782
- const client = getClient11();
1896
+ const client = getClient12();
1783
1897
  const result = await client.gscSitemaps(project);
1784
1898
  if (opts.format === "json") {
1785
1899
  console.log(JSON.stringify(result, null, 2));
@@ -1801,7 +1915,7 @@ Sitemaps for project "${project}":
1801
1915
  }
1802
1916
  }
1803
1917
  async function googleInspectSitemap(project, opts) {
1804
- const client = getClient11();
1918
+ const client = getClient12();
1805
1919
  const run = await client.gscInspectSitemap(project, {
1806
1920
  sitemapUrl: opts.sitemapUrl
1807
1921
  });
@@ -1836,7 +1950,7 @@ async function googleInspectSitemap(project, opts) {
1836
1950
  }
1837
1951
  }
1838
1952
  async function googleCoverageHistory(project, opts) {
1839
- const client = getClient11();
1953
+ const client = getClient12();
1840
1954
  const rows = await client.gscCoverageHistory(project, { limit: opts.limit });
1841
1955
  if (opts.format === "json") {
1842
1956
  console.log(JSON.stringify(rows, null, 2));
@@ -1858,7 +1972,7 @@ GSC Coverage History for "${project}" (${rows.length} snapshots):
1858
1972
  }
1859
1973
  }
1860
1974
  async function googleDiscoverSitemaps(project, opts) {
1861
- const client = getClient11();
1975
+ const client = getClient12();
1862
1976
  const result = await client.gscDiscoverSitemaps(project);
1863
1977
  if (opts.format === "json") {
1864
1978
  console.log(JSON.stringify(result, null, 2));
@@ -1903,7 +2017,7 @@ Primary sitemap: ${result.primarySitemapUrl}`);
1903
2017
  }
1904
2018
  }
1905
2019
  async function googleRequestIndexing(project, opts) {
1906
- const client = getClient11();
2020
+ const client = getClient12();
1907
2021
  const body = { urls: [] };
1908
2022
  if (opts.allUnindexed) {
1909
2023
  body.allUnindexed = true;
@@ -1965,7 +2079,7 @@ async function googleRequestIndexing(project, opts) {
1965
2079
  }
1966
2080
  }
1967
2081
  async function googleDeindexed(project, format) {
1968
- const client = getClient11();
2082
+ const client = getClient12();
1969
2083
  const rows = await client.gscDeindexed(project);
1970
2084
  if (format === "json") {
1971
2085
  console.log(JSON.stringify(rows, null, 2));
@@ -2023,6 +2137,7 @@ Usage:
2023
2137
  canonry runs <project> List runs for a project
2024
2138
  canonry status <project> Show project summary
2025
2139
  canonry evidence <project> Show per-phrase results
2140
+ canonry analytics <project> Show analytics (--feature metrics|gaps|sources, --window 7d|30d|90d|all)
2026
2141
  canonry history <project> Show audit trail
2027
2142
  canonry export <project> Export project as YAML
2028
2143
  canonry apply <file...> Apply declarative config (multi-doc YAML supported)
@@ -2539,6 +2654,19 @@ async function main() {
2539
2654
  await showHistory(project, format);
2540
2655
  break;
2541
2656
  }
2657
+ case "analytics": {
2658
+ const project = args[1];
2659
+ if (!project) {
2660
+ console.error("Error: project name is required");
2661
+ process.exit(1);
2662
+ }
2663
+ const featureIdx = args.indexOf("--feature");
2664
+ const feature = featureIdx !== -1 ? args[featureIdx + 1] : void 0;
2665
+ const windowIdx = args.indexOf("--window");
2666
+ const windowArg = windowIdx !== -1 ? args[windowIdx + 1] : void 0;
2667
+ await showAnalytics(project, { feature, window: windowArg, format });
2668
+ break;
2669
+ }
2542
2670
  case "export": {
2543
2671
  const project = args[1];
2544
2672
  if (!project) {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-RU7RHCWK.js";
4
+ } from "./chunk-7RIIMCVP.js";
5
5
  export {
6
6
  createServer,
7
7
  loadConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "type": "module",
5
5
  "description": "The ultimate open-source AEO monitoring tool - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -52,14 +52,14 @@
52
52
  "tsup": "^8.5.1",
53
53
  "tsx": "^4.19.0",
54
54
  "@ainyc/canonry-api-routes": "0.0.0",
55
- "@ainyc/canonry-contracts": "0.0.0",
56
- "@ainyc/canonry-db": "0.0.0",
57
55
  "@ainyc/canonry-config": "0.0.0",
58
- "@ainyc/canonry-provider-gemini": "0.0.0",
59
- "@ainyc/canonry-integration-google": "0.0.0",
56
+ "@ainyc/canonry-provider-local": "0.0.0",
57
+ "@ainyc/canonry-contracts": "0.0.0",
60
58
  "@ainyc/canonry-provider-claude": "0.0.0",
59
+ "@ainyc/canonry-integration-google": "0.0.0",
60
+ "@ainyc/canonry-provider-gemini": "0.0.0",
61
61
  "@ainyc/canonry-provider-openai": "0.0.0",
62
- "@ainyc/canonry-provider-local": "0.0.0"
62
+ "@ainyc/canonry-db": "0.0.0"
63
63
  },
64
64
  "scripts": {
65
65
  "build": "tsup && tsx build-web.ts",