@ainyc/canonry 1.12.0 → 1.13.3

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-O4HLQBL7.js";
22
+ } from "./chunk-JKIWFSYI.js";
23
23
 
24
24
  // src/cli.ts
25
25
  import { parseArgs } from "util";
@@ -319,7 +319,7 @@ async function initCommand(opts) {
319
319
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
320
320
  }).run();
321
321
  saveConfig({
322
- apiUrl: "http://localhost:4100",
322
+ apiUrl: `http://localhost:${process.env.CANONRY_PORT || "4100"}`,
323
323
  database: databasePath,
324
324
  apiKey: rawApiKey,
325
325
  providers,
@@ -344,6 +344,7 @@ async function serveCommand() {
344
344
  const config = loadConfig();
345
345
  const port = parseInt(process.env.CANONRY_PORT ?? "4100", 10);
346
346
  const host = process.env.CANONRY_HOST ?? "127.0.0.1";
347
+ config.port = port;
347
348
  const db = createClient(config.database);
348
349
  migrate(db);
349
350
  const app = await createServer({ config, db });
@@ -476,16 +477,17 @@ var ApiClient = class {
476
477
  }
477
478
  async request(method, path4, body) {
478
479
  const url = `${this.baseUrl}${path4}`;
480
+ const serializedBody = body != null ? JSON.stringify(body) : void 0;
479
481
  const headers = {
480
482
  "Authorization": `Bearer ${this.apiKey}`,
481
- "Content-Type": "application/json"
483
+ ...serializedBody != null ? { "Content-Type": "application/json" } : {}
482
484
  };
483
485
  let res;
484
486
  try {
485
487
  res = await fetch(url, {
486
488
  method,
487
489
  headers,
488
- body: body != null ? JSON.stringify(body) : void 0
490
+ body: serializedBody
489
491
  });
490
492
  } catch (err) {
491
493
  const msg = err instanceof Error ? err.message : String(err);
@@ -529,6 +531,9 @@ var ApiClient = class {
529
531
  async listKeywords(project) {
530
532
  return this.request("GET", `/projects/${encodeURIComponent(project)}/keywords`);
531
533
  }
534
+ async deleteKeywords(project, keywords) {
535
+ await this.request("DELETE", `/projects/${encodeURIComponent(project)}/keywords`, { keywords });
536
+ }
532
537
  async appendKeywords(project, keywords) {
533
538
  await this.request("POST", `/projects/${encodeURIComponent(project)}/keywords`, { keywords });
534
539
  }
@@ -589,6 +594,18 @@ var ApiClient = class {
589
594
  async testNotification(project, id) {
590
595
  return this.request("POST", `/projects/${encodeURIComponent(project)}/notifications/${encodeURIComponent(id)}/test`);
591
596
  }
597
+ async addLocation(project, body) {
598
+ return this.request("POST", `/projects/${encodeURIComponent(project)}/locations`, body);
599
+ }
600
+ async listLocations(project) {
601
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/locations`);
602
+ }
603
+ async removeLocation(project, label) {
604
+ await this.request("DELETE", `/projects/${encodeURIComponent(project)}/locations/${encodeURIComponent(label)}`);
605
+ }
606
+ async setDefaultLocation(project, label) {
607
+ return this.request("PUT", `/projects/${encodeURIComponent(project)}/locations/default`, { label });
608
+ }
592
609
  async getTelemetry() {
593
610
  return this.request("GET", "/telemetry");
594
611
  }
@@ -636,6 +653,16 @@ var ApiClient = class {
636
653
  async gscDeindexed(project) {
637
654
  return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/deindexed`);
638
655
  }
656
+ async gscCoverage(project) {
657
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage`);
658
+ }
659
+ async gscCoverageHistory(project, params) {
660
+ const qs = params?.limit != null ? `?limit=${params.limit}` : "";
661
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/google/gsc/coverage/history${qs}`);
662
+ }
663
+ async gscInspectSitemap(project, body) {
664
+ return this.request("POST", `/projects/${encodeURIComponent(project)}/google/gsc/inspect-sitemap`, body ?? {});
665
+ }
639
666
  };
640
667
 
641
668
  // src/commands/project.ts
@@ -734,6 +761,47 @@ async function deleteProject(name) {
734
761
  await client.deleteProject(name);
735
762
  console.log(`Project deleted: ${name}`);
736
763
  }
764
+ async function addLocation(project, opts) {
765
+ const client = getClient();
766
+ await client.addLocation(project, opts);
767
+ console.log(`Location added: ${opts.label} (${opts.city}, ${opts.region}, ${opts.country})`);
768
+ }
769
+ async function listLocations(project, format) {
770
+ const client = getClient();
771
+ const result = await client.listLocations(project);
772
+ if (format === "json") {
773
+ console.log(JSON.stringify(result, null, 2));
774
+ return;
775
+ }
776
+ if (result.locations.length === 0) {
777
+ console.log(`No locations configured for "${project}".`);
778
+ return;
779
+ }
780
+ console.log(`Locations for "${project}" (${result.locations.length}):
781
+ `);
782
+ console.log(" LABEL CITY REGION COUNTRY DEFAULT");
783
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500");
784
+ for (const loc of result.locations) {
785
+ const isDefault = loc.label === result.defaultLocation ? " *" : "";
786
+ console.log(
787
+ ` ${loc.label.padEnd(15)} ${loc.city.padEnd(19)} ${loc.region.padEnd(19)} ${loc.country.padEnd(7)}${isDefault}`
788
+ );
789
+ }
790
+ if (result.defaultLocation) {
791
+ console.log(`
792
+ Default: ${result.defaultLocation}`);
793
+ }
794
+ }
795
+ async function removeLocation(project, label) {
796
+ const client = getClient();
797
+ await client.removeLocation(project, label);
798
+ console.log(`Location removed: ${label}`);
799
+ }
800
+ async function setDefaultLocation(project, label) {
801
+ const client = getClient();
802
+ await client.setDefaultLocation(project, label);
803
+ console.log(`Default location set to: ${label}`);
804
+ }
737
805
 
738
806
  // src/commands/keyword.ts
739
807
  import fs3 from "fs";
@@ -746,6 +814,14 @@ async function addKeywords(project, keywords) {
746
814
  await client.appendKeywords(project, keywords);
747
815
  console.log(`Added ${keywords.length} key phrase(s) to "${project}".`);
748
816
  }
817
+ async function removeKeywords(project, keywords) {
818
+ const client = getClient2();
819
+ const existing = await client.listKeywords(project);
820
+ const existingSet = new Set(existing.map((k) => k.keyword));
821
+ const actuallyDeleted = keywords.filter((k) => existingSet.has(k)).length;
822
+ await client.deleteKeywords(project, keywords);
823
+ console.log(`Removed ${actuallyDeleted} key phrase(s) from "${project}".`);
824
+ }
749
825
  async function listKeywords(project, format) {
750
826
  const client = getClient2();
751
827
  const kws = await client.listKeywords(project);
@@ -838,7 +914,63 @@ async function triggerRun(project, opts) {
838
914
  if (opts?.provider) {
839
915
  body.providers = [opts.provider];
840
916
  }
841
- const run = await client.triggerRun(project, body);
917
+ if (opts?.location) {
918
+ body.location = opts.location;
919
+ }
920
+ if (opts?.allLocations) {
921
+ body.allLocations = true;
922
+ }
923
+ if (opts?.noLocation) {
924
+ body.noLocation = true;
925
+ }
926
+ const response = await client.triggerRun(project, body);
927
+ if (Array.isArray(response)) {
928
+ const locationRuns = response;
929
+ if (opts?.format === "json") {
930
+ if (opts?.wait) {
931
+ const settled = await Promise.all(
932
+ locationRuns.map(async (r) => {
933
+ if (!r.id || r.status === "conflict") return r;
934
+ const final = await pollRun(client, r.id);
935
+ return { ...r, ...final };
936
+ })
937
+ );
938
+ console.log(JSON.stringify(settled, null, 2));
939
+ } else {
940
+ console.log(JSON.stringify(locationRuns, null, 2));
941
+ }
942
+ return;
943
+ }
944
+ console.log(`Triggered ${locationRuns.length} location sweep(s) \u2014 ${locationRuns.length}\xD7 API calls:
945
+ `);
946
+ console.log(" LOCATION RUN ID STATUS");
947
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
948
+ for (const r of locationRuns) {
949
+ const loc = (r.location ?? "(unknown)").padEnd(15);
950
+ const id = (r.id ?? "(conflict)").padEnd(36);
951
+ console.log(` ${loc} ${id} ${r.status}`);
952
+ }
953
+ if (opts?.wait) {
954
+ const pending = locationRuns.filter((r) => r.id && r.status !== "conflict");
955
+ if (pending.length > 0) {
956
+ process.stderr.write(`Waiting for ${pending.length} run(s)`);
957
+ await Promise.all(
958
+ pending.map(async (r) => {
959
+ const final = await pollRun(client, r.id);
960
+ r.status = final.status;
961
+ })
962
+ );
963
+ process.stderr.write("\n");
964
+ console.log("\nFinal statuses:");
965
+ for (const r of locationRuns) {
966
+ const loc = (r.location ?? "(unknown)").padEnd(15);
967
+ console.log(` ${loc} ${r.status}`);
968
+ }
969
+ }
970
+ }
971
+ return;
972
+ }
973
+ const run = response;
842
974
  if (opts?.wait) {
843
975
  process.stderr.write(`Run ${run.id} started`);
844
976
  const result = await pollRun(client, run.id);
@@ -1581,6 +1713,109 @@ async function googleInspections(project, opts) {
1581
1713
  );
1582
1714
  }
1583
1715
  }
1716
+ async function googleCoverage(project, format) {
1717
+ const client = getClient11();
1718
+ const result = await client.gscCoverage(project);
1719
+ if (format === "json") {
1720
+ console.log(JSON.stringify(result, null, 2));
1721
+ return;
1722
+ }
1723
+ const { summary } = result;
1724
+ if (summary.total === 0) {
1725
+ console.log('No URL inspections found. Run "canonry google inspect-sitemap <project>" first.');
1726
+ return;
1727
+ }
1728
+ const pctColor = summary.percentage >= 80 ? "\x1B[32m" : summary.percentage >= 50 ? "\x1B[33m" : "\x1B[31m";
1729
+ const reset = "\x1B[0m";
1730
+ console.log(`
1731
+ Index Coverage for "${project}"
1732
+ `);
1733
+ console.log(` SUMMARY: ${pctColor}${summary.indexed} / ${summary.total} pages indexed (${summary.percentage}%)${reset}
1734
+ `);
1735
+ if (result.indexed.length > 0) {
1736
+ console.log(` INDEXED (${result.indexed.length}):`);
1737
+ for (const page of result.indexed) {
1738
+ const crawl = page.crawlTime ? ` (crawled: ${page.crawlTime.split("T")[0]})` : "";
1739
+ console.log(` ${page.url}${crawl}`);
1740
+ }
1741
+ console.log();
1742
+ }
1743
+ if (result.notIndexed.length > 0) {
1744
+ console.log(` NOT INDEXED (${result.notIndexed.length}):`);
1745
+ for (const page of result.notIndexed) {
1746
+ const reason = page.coverageState ? ` \u2014 ${page.coverageState}` : "";
1747
+ console.log(` ${page.url}${reason}`);
1748
+ }
1749
+ console.log();
1750
+ }
1751
+ if (result.deindexed.length > 0) {
1752
+ console.log(` DEINDEXED (${result.deindexed.length}):`);
1753
+ for (const page of result.deindexed) {
1754
+ console.log(` ${page.url} (${page.previousState} -> ${page.currentState})`);
1755
+ }
1756
+ console.log();
1757
+ }
1758
+ if (result.lastInspectedAt) {
1759
+ console.log(` Last inspected: ${result.lastInspectedAt}`);
1760
+ }
1761
+ }
1762
+ async function googleInspectSitemap(project, opts) {
1763
+ const client = getClient11();
1764
+ const run = await client.gscInspectSitemap(project, {
1765
+ sitemapUrl: opts.sitemapUrl
1766
+ });
1767
+ if (opts.format === "json") {
1768
+ console.log(JSON.stringify(run, null, 2));
1769
+ return;
1770
+ }
1771
+ console.log(`Sitemap inspection started (run ${run.id})`);
1772
+ if (opts.wait) {
1773
+ const timeout = 30 * 60 * 1e3;
1774
+ const start = Date.now();
1775
+ process.stderr.write("Waiting for sitemap inspection to complete");
1776
+ while (Date.now() - start < timeout) {
1777
+ await new Promise((r) => setTimeout(r, 3e3));
1778
+ const current = await client.getRun(run.id);
1779
+ process.stderr.write(".");
1780
+ if (current.status === "completed" || current.status === "partial" || current.status === "failed") {
1781
+ process.stderr.write("\n");
1782
+ if (current.status === "completed") {
1783
+ console.log("Sitemap inspection completed successfully.");
1784
+ } else if (current.status === "partial") {
1785
+ console.log("Sitemap inspection completed with some errors.");
1786
+ } else {
1787
+ console.error("Sitemap inspection failed.");
1788
+ }
1789
+ return;
1790
+ }
1791
+ }
1792
+ process.stderr.write("\n");
1793
+ console.error("Timed out waiting for sitemap inspection to complete.");
1794
+ process.exit(1);
1795
+ }
1796
+ }
1797
+ async function googleCoverageHistory(project, opts) {
1798
+ const client = getClient11();
1799
+ const rows = await client.gscCoverageHistory(project, { limit: opts.limit });
1800
+ if (opts.format === "json") {
1801
+ console.log(JSON.stringify(rows, null, 2));
1802
+ return;
1803
+ }
1804
+ if (rows.length === 0) {
1805
+ console.log("No coverage history found. Run a GSC sync or sitemap inspection first.");
1806
+ return;
1807
+ }
1808
+ console.log(`
1809
+ GSC Coverage History for "${project}" (${rows.length} snapshots):
1810
+ `);
1811
+ console.log(` ${"DATE".padEnd(12)}${"INDEXED".padEnd(10)}${"NOT INDEXED".padEnd(14)}TOP REASON`);
1812
+ console.log(` ${"\u2500".repeat(12)}${"\u2500".repeat(10)}${"\u2500".repeat(14)}${"\u2500".repeat(30)}`);
1813
+ for (const row of rows) {
1814
+ const topReason = Object.entries(row.reasonBreakdown).sort((a, b) => b[1] - a[1])[0];
1815
+ const reasonStr = topReason ? `${topReason[0]} (${topReason[1]})` : "-";
1816
+ console.log(` ${row.date.padEnd(12)}${String(row.indexed).padEnd(10)}${String(row.notIndexed).padEnd(14)}${reasonStr}`);
1817
+ }
1818
+ }
1584
1819
  async function googleDeindexed(project, format) {
1585
1820
  const client = getClient11();
1586
1821
  const rows = await client.gscDeindexed(project);
@@ -1618,7 +1853,12 @@ Usage:
1618
1853
  canonry project list List all projects
1619
1854
  canonry project show <name> Show project details
1620
1855
  canonry project delete <name> Delete a project
1856
+ canonry project add-location <name> Add a location (--label, --city, --region, --country)
1857
+ canonry project locations <name> List locations for a project
1858
+ canonry project remove-location <name> <label> Remove a location
1859
+ canonry project set-default-location <name> <label> Set default location
1621
1860
  canonry keyword add <project> <kw> Add key phrases to a project
1861
+ canonry keyword remove <project> <kw> Remove key phrases from a project
1622
1862
  canonry keyword list <project> List key phrases for a project
1623
1863
  canonry keyword import <project> <file> Import key phrases from file
1624
1864
  canonry keyword generate <project> Auto-generate key phrases (--provider, --count, --save)
@@ -1626,6 +1866,9 @@ Usage:
1626
1866
  canonry competitor list <project> List competitors
1627
1867
  canonry run <project> Trigger a run (all providers)
1628
1868
  canonry run <project> --provider <name> Trigger a run for a specific provider
1869
+ canonry run <project> --location <label> Run with a specific location
1870
+ canonry run <project> --all-locations Run for every configured location (N\xD7 API calls)
1871
+ canonry run <project> --no-location Explicitly skip location context
1629
1872
  canonry run <project> --wait Trigger and wait for completion
1630
1873
  canonry run --all Trigger runs for all projects
1631
1874
  canonry run show <id> Show run details and snapshots
@@ -1653,6 +1896,8 @@ Usage:
1653
1896
  canonry google sync <project> Sync GSC data (--days 30, --full, --wait)
1654
1897
  canonry google performance <project> Show GSC search performance data
1655
1898
  canonry google inspect <project> <url> Inspect a URL via GSC
1899
+ canonry google inspect-sitemap <project> Bulk inspect all URLs from sitemap (--sitemap-url, --wait)
1900
+ canonry google coverage <project> Show index coverage summary
1656
1901
  canonry google inspections <project> Show URL inspection history (--url <url>)
1657
1902
  canonry google deindexed <project> Show pages that lost indexing
1658
1903
  canonry settings Show active provider and quota settings
@@ -1684,6 +1929,9 @@ Options:
1684
1929
  --language <lang> Language code (default: en)
1685
1930
  --provider <name> Provider to use (gemini, openai, claude, local)
1686
1931
  --format <fmt> Output format: text (default) or json
1932
+ --location <label> Run with a specific configured location
1933
+ --all-locations Run for every configured location
1934
+ --no-location Explicitly skip location context
1687
1935
  --wait Wait for run to complete before returning
1688
1936
  --all Run all projects (with 'run' command)
1689
1937
  --include-results Include results in export
@@ -1877,9 +2125,68 @@ async function main() {
1877
2125
  await deleteProject(name);
1878
2126
  break;
1879
2127
  }
2128
+ case "add-location": {
2129
+ const name = args[2];
2130
+ if (!name) {
2131
+ console.error("Error: project name is required");
2132
+ process.exit(1);
2133
+ }
2134
+ const { values: locValues } = parseArgs({
2135
+ args: args.slice(3),
2136
+ options: {
2137
+ label: { type: "string" },
2138
+ city: { type: "string" },
2139
+ region: { type: "string" },
2140
+ country: { type: "string" },
2141
+ timezone: { type: "string" }
2142
+ },
2143
+ allowPositionals: false
2144
+ });
2145
+ if (!locValues.label || !locValues.city || !locValues.region || !locValues.country) {
2146
+ console.error("Error: --label, --city, --region, and --country are all required");
2147
+ process.exit(1);
2148
+ }
2149
+ await addLocation(name, {
2150
+ label: locValues.label,
2151
+ city: locValues.city,
2152
+ region: locValues.region,
2153
+ country: locValues.country,
2154
+ timezone: locValues.timezone
2155
+ });
2156
+ break;
2157
+ }
2158
+ case "locations": {
2159
+ const name = args[2];
2160
+ if (!name) {
2161
+ console.error("Error: project name is required");
2162
+ process.exit(1);
2163
+ }
2164
+ await listLocations(name, format);
2165
+ break;
2166
+ }
2167
+ case "remove-location": {
2168
+ const name = args[2];
2169
+ const label = args[3];
2170
+ if (!name || !label) {
2171
+ console.error("Error: project name and location label are required");
2172
+ process.exit(1);
2173
+ }
2174
+ await removeLocation(name, label);
2175
+ break;
2176
+ }
2177
+ case "set-default-location": {
2178
+ const name = args[2];
2179
+ const label = args[3];
2180
+ if (!name || !label) {
2181
+ console.error("Error: project name and location label are required");
2182
+ process.exit(1);
2183
+ }
2184
+ await setDefaultLocation(name, label);
2185
+ break;
2186
+ }
1880
2187
  default:
1881
2188
  console.error(`Unknown project subcommand: ${subcommand ?? "(none)"}`);
1882
- console.log("Available: create, update, list, show, delete");
2189
+ console.log("Available: create, update, list, show, delete, add-location, locations, remove-location, set-default-location");
1883
2190
  process.exit(1);
1884
2191
  }
1885
2192
  break;
@@ -1897,6 +2204,17 @@ async function main() {
1897
2204
  await addKeywords(project, kws);
1898
2205
  break;
1899
2206
  }
2207
+ case "remove":
2208
+ case "delete": {
2209
+ const project = args[2];
2210
+ const kws = args.slice(3).filter((a, i, arr) => !a.startsWith("--") && !(i > 0 && arr[i - 1].startsWith("--")));
2211
+ if (!project || kws.length === 0) {
2212
+ console.error("Error: project name and at least one key phrase required");
2213
+ process.exit(1);
2214
+ }
2215
+ await removeKeywords(project, kws);
2216
+ break;
2217
+ }
1900
2218
  case "list": {
1901
2219
  const project = args[2];
1902
2220
  if (!project) {
@@ -1944,7 +2262,7 @@ async function main() {
1944
2262
  }
1945
2263
  default:
1946
2264
  console.error(`Unknown keyword subcommand: ${subcommand ?? "(none)"}`);
1947
- console.log("Available: add, list, import, generate");
2265
+ console.log("Available: add, remove, list, import, generate");
1948
2266
  process.exit(1);
1949
2267
  }
1950
2268
  break;
@@ -1994,6 +2312,9 @@ async function main() {
1994
2312
  provider: { type: "string" },
1995
2313
  wait: { type: "boolean", default: false },
1996
2314
  all: { type: "boolean", default: false },
2315
+ location: { type: "string" },
2316
+ "all-locations": { type: "boolean", default: false },
2317
+ "no-location": { type: "boolean", default: false },
1997
2318
  format: { type: "string" }
1998
2319
  },
1999
2320
  allowPositionals: true
@@ -2018,6 +2339,9 @@ async function main() {
2018
2339
  await triggerRun(project, {
2019
2340
  provider: runParsed.values.provider,
2020
2341
  wait: runParsed.values.wait,
2342
+ location: runParsed.values.location,
2343
+ allLocations: runParsed.values["all-locations"],
2344
+ noLocation: runParsed.values["no-location"],
2021
2345
  format: runFormat
2022
2346
  });
2023
2347
  }
@@ -2420,6 +2744,58 @@ async function main() {
2420
2744
  });
2421
2745
  break;
2422
2746
  }
2747
+ case "inspect-sitemap": {
2748
+ const project = args[2];
2749
+ if (!project) {
2750
+ console.error("Error: project name is required");
2751
+ process.exit(1);
2752
+ }
2753
+ const { values: inspSitemapValues } = parseArgs({
2754
+ args: args.slice(3),
2755
+ options: {
2756
+ "sitemap-url": { type: "string" },
2757
+ wait: { type: "boolean", default: false },
2758
+ format: { type: "string" }
2759
+ },
2760
+ allowPositionals: false
2761
+ });
2762
+ await googleInspectSitemap(project, {
2763
+ sitemapUrl: inspSitemapValues["sitemap-url"],
2764
+ wait: inspSitemapValues.wait ?? false,
2765
+ format: inspSitemapValues.format === "json" ? "json" : format
2766
+ });
2767
+ break;
2768
+ }
2769
+ case "coverage": {
2770
+ const project = args[2];
2771
+ if (!project) {
2772
+ console.error("Error: project name is required");
2773
+ process.exit(1);
2774
+ }
2775
+ await googleCoverage(project, format);
2776
+ break;
2777
+ }
2778
+ case "coverage-history": {
2779
+ const project = args[2];
2780
+ if (!project) {
2781
+ console.error("Error: project name is required");
2782
+ process.exit(1);
2783
+ }
2784
+ const { values: histValues } = parseArgs({
2785
+ args: args.slice(3),
2786
+ options: {
2787
+ limit: { type: "string" },
2788
+ format: { type: "string" }
2789
+ },
2790
+ allowPositionals: false
2791
+ });
2792
+ const limitNum = histValues.limit ? parseInt(histValues.limit, 10) : void 0;
2793
+ await googleCoverageHistory(project, {
2794
+ limit: limitNum != null && !Number.isNaN(limitNum) ? limitNum : void 0,
2795
+ format: histValues.format === "json" ? "json" : format
2796
+ });
2797
+ break;
2798
+ }
2423
2799
  case "deindexed": {
2424
2800
  const project = args[2];
2425
2801
  if (!project) {
@@ -2431,7 +2807,7 @@ async function main() {
2431
2807
  }
2432
2808
  default:
2433
2809
  console.error(`Unknown google subcommand: ${subcommand ?? "(none)"}`);
2434
- console.log("Available: connect, disconnect, status, properties, set-property, sync, performance, inspect, inspections, deindexed");
2810
+ console.log("Available: connect, disconnect, status, properties, set-property, sync, performance, inspect, inspect-sitemap, coverage, coverage-history, inspections, deindexed");
2435
2811
  process.exit(1);
2436
2812
  }
2437
2813
  break;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-O4HLQBL7.js";
4
+ } from "./chunk-JKIWFSYI.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.12.0",
3
+ "version": "1.13.3",
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",
@@ -53,12 +53,12 @@
53
53
  "tsx": "^4.19.0",
54
54
  "@ainyc/canonry-api-routes": "0.0.0",
55
55
  "@ainyc/canonry-config": "0.0.0",
56
- "@ainyc/canonry-contracts": "0.0.0",
57
56
  "@ainyc/canonry-db": "0.0.0",
58
- "@ainyc/canonry-provider-gemini": "0.0.0",
59
57
  "@ainyc/canonry-provider-claude": "0.0.0",
60
58
  "@ainyc/canonry-provider-local": "0.0.0",
59
+ "@ainyc/canonry-provider-gemini": "0.0.0",
61
60
  "@ainyc/canonry-provider-openai": "0.0.0",
61
+ "@ainyc/canonry-contracts": "0.0.0",
62
62
  "@ainyc/canonry-integration-google": "0.0.0"
63
63
  },
64
64
  "scripts": {