@ainyc/canonry 1.25.3 → 1.26.2

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-G5TMCVS4.js";
22
+ } from "./chunk-NVCAUQ33.js";
23
23
 
24
24
  // src/cli.ts
25
25
  import { pathToFileURL } from "url";
@@ -148,14 +148,56 @@ Usage: ${spec.usage}`, {
148
148
  }
149
149
 
150
150
  // src/client.ts
151
+ function createApiClient() {
152
+ const config = loadConfig();
153
+ const basePathResolved = !!config.basePath || "CANONRY_BASE_PATH" in process.env;
154
+ return new ApiClient(config.apiUrl, config.apiKey, { skipProbe: basePathResolved });
155
+ }
151
156
  var ApiClient = class {
152
157
  baseUrl;
158
+ originUrl;
153
159
  apiKey;
154
- constructor(baseUrl, apiKey) {
155
- this.baseUrl = baseUrl.replace(/\/$/, "") + "/api/v1";
160
+ probePromise = null;
161
+ probeSkipped;
162
+ constructor(baseUrl, apiKey, opts) {
163
+ this.originUrl = baseUrl.replace(/\/$/, "");
164
+ this.baseUrl = this.originUrl + "/api/v1";
156
165
  this.apiKey = apiKey;
166
+ this.probeSkipped = opts?.skipProbe ?? false;
167
+ }
168
+ /**
169
+ * On first API call, probe /health to auto-discover basePath when the user
170
+ * hasn't configured one locally. This lets `canonry run` in a separate shell
171
+ * discover that the server is running at e.g. /canonry/ without requiring
172
+ * config.yaml edits or CANONRY_BASE_PATH in every shell.
173
+ */
174
+ probeBasePath() {
175
+ if (this.probeSkipped) return Promise.resolve();
176
+ if (!this.probePromise) {
177
+ this.probePromise = (async () => {
178
+ try {
179
+ const origin = new URL(this.originUrl).origin;
180
+ const res = await fetch(`${origin}/health`, {
181
+ signal: AbortSignal.timeout(2e3)
182
+ });
183
+ if (res.ok) {
184
+ const body = await res.json();
185
+ if (body.basePath && typeof body.basePath === "string") {
186
+ const normalized = "/" + body.basePath.replace(/^\/|\/$/g, "");
187
+ if (normalized !== "/") {
188
+ this.originUrl = origin + normalized;
189
+ this.baseUrl = this.originUrl + "/api/v1";
190
+ }
191
+ }
192
+ }
193
+ } catch {
194
+ }
195
+ })();
196
+ }
197
+ return this.probePromise;
157
198
  }
158
199
  async request(method, path4, body) {
200
+ await this.probeBasePath();
159
201
  const url = `${this.baseUrl}${path4}`;
160
202
  const serializedBody = body != null ? JSON.stringify(body) : void 0;
161
203
  const headers = {
@@ -440,8 +482,7 @@ var ApiClient = class {
440
482
 
441
483
  // src/commands/bing.ts
442
484
  function getClient() {
443
- const config = loadConfig();
444
- return new ApiClient(config.apiUrl, config.apiKey);
485
+ return createApiClient();
445
486
  }
446
487
  async function bingConnect(project, opts) {
447
488
  let apiKey = opts?.apiKey;
@@ -636,6 +677,46 @@ async function bingInspections(project, opts) {
636
677
  );
637
678
  }
638
679
  }
680
+ async function bingRefresh(project, format) {
681
+ const client = getClient();
682
+ const rows = await client.bingInspections(project);
683
+ const uniqueUrls = [...new Set(rows.map((r) => r.url))];
684
+ if (uniqueUrls.length === 0) {
685
+ if (format === "json") {
686
+ console.log(JSON.stringify({ refreshed: 0, message: "No previously inspected URLs to refresh." }, null, 2));
687
+ return;
688
+ }
689
+ console.log('No previously inspected URLs to refresh. Run "canonry bing inspect <project> <url>" first.');
690
+ return;
691
+ }
692
+ if (format !== "json") {
693
+ process.stderr.write(`Re-inspecting ${uniqueUrls.length} URL(s) via Bing`);
694
+ }
695
+ const CONCURRENCY = 10;
696
+ const errors = [];
697
+ for (let i = 0; i < uniqueUrls.length; i += CONCURRENCY) {
698
+ const batch = uniqueUrls.slice(i, i + CONCURRENCY);
699
+ const results = await Promise.allSettled(
700
+ batch.map((url) => client.bingInspectUrl(project, url))
701
+ );
702
+ for (const r of results) {
703
+ if (r.status === "rejected") {
704
+ errors.push(r.reason instanceof Error ? r.reason.message : String(r.reason));
705
+ }
706
+ }
707
+ if (format !== "json") {
708
+ process.stderr.write(".");
709
+ }
710
+ }
711
+ if (format !== "json") {
712
+ process.stderr.write("\n");
713
+ }
714
+ if (errors.length > 0) {
715
+ process.stderr.write(`${errors.length} URL(s) failed to re-inspect.
716
+ `);
717
+ }
718
+ await bingCoverage(project, format);
719
+ }
639
720
  async function bingRequestIndexing(project, opts) {
640
721
  const client = getClient();
641
722
  const body = {};
@@ -900,14 +981,22 @@ var BING_CLI_COMMANDS = [
900
981
  await bingPerformance(project, input.format);
901
982
  }
902
983
  },
984
+ {
985
+ path: ["bing", "refresh"],
986
+ usage: "canonry bing refresh <project> [--format json]",
987
+ run: async (input) => {
988
+ const project = requireProject(input, "bing.refresh", "canonry bing refresh <project> [--format json]");
989
+ await bingRefresh(project, input.format);
990
+ }
991
+ },
903
992
  {
904
993
  path: ["bing"],
905
- usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance> <project> [args]",
994
+ usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
906
995
  run: async (input) => {
907
996
  unknownSubcommand(input.positionals[0], {
908
997
  command: "bing",
909
- usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance> <project> [args]",
910
- available: ["connect", "disconnect", "status", "sites", "set-site", "coverage", "inspect", "inspections", "request-indexing", "performance"]
998
+ usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
999
+ available: ["connect", "disconnect", "status", "sites", "set-site", "coverage", "inspect", "inspections", "request-indexing", "performance", "refresh"]
911
1000
  });
912
1001
  }
913
1002
  }
@@ -915,8 +1004,7 @@ var BING_CLI_COMMANDS = [
915
1004
 
916
1005
  // src/commands/cdp.ts
917
1006
  function getClient2() {
918
- const config = loadConfig();
919
- return new ApiClient(config.apiUrl, config.apiKey);
1007
+ return createApiClient();
920
1008
  }
921
1009
  async function cdpConnect(opts) {
922
1010
  const config = loadConfig();
@@ -1107,8 +1195,7 @@ var CDP_CLI_COMMANDS = [
1107
1195
 
1108
1196
  // src/commands/ga.ts
1109
1197
  function getClient3() {
1110
- const config = loadConfig();
1111
- return new ApiClient(config.apiUrl, config.apiKey);
1198
+ return createApiClient();
1112
1199
  }
1113
1200
  async function gaConnect(project, opts) {
1114
1201
  if (!opts.propertyId) {
@@ -1350,8 +1437,7 @@ var GA_CLI_COMMANDS = [
1350
1437
 
1351
1438
  // src/commands/competitor.ts
1352
1439
  function getClient4() {
1353
- const config = loadConfig();
1354
- return new ApiClient(config.apiUrl, config.apiKey);
1440
+ return createApiClient();
1355
1441
  }
1356
1442
  async function addCompetitors(project, domains, format) {
1357
1443
  const client = getClient4();
@@ -1432,8 +1518,7 @@ var COMPETITOR_CLI_COMMANDS = [
1432
1518
 
1433
1519
  // src/commands/google.ts
1434
1520
  function getClient5() {
1435
- const config = loadConfig();
1436
- return new ApiClient(config.apiUrl, config.apiKey);
1521
+ return createApiClient();
1437
1522
  }
1438
1523
  async function waitForRunStatus(client, runId, config) {
1439
1524
  const start = Date.now();
@@ -1946,6 +2031,29 @@ async function googleRequestIndexing(project, opts) {
1946
2031
  console.log("All requested URLs are now indexed.");
1947
2032
  }
1948
2033
  }
2034
+ async function googleRefresh(project, format) {
2035
+ const client = getClient5();
2036
+ const run = await client.gscSync(project, {});
2037
+ if (format !== "json") {
2038
+ process.stderr.write("Refreshing GSC coverage data");
2039
+ }
2040
+ const current = await waitForRunStatus(client, run.id, {
2041
+ timeoutMs: 10 * 60 * 1e3,
2042
+ intervalMs: 2e3,
2043
+ progressLabel: "",
2044
+ successStatuses: ["completed", "partial"],
2045
+ failureStatuses: ["failed"],
2046
+ timeoutCode: "GOOGLE_REFRESH_TIMEOUT",
2047
+ failureCode: "GOOGLE_REFRESH_FAILED",
2048
+ timeoutMessage: "Timed out waiting for GSC refresh to complete.",
2049
+ failureMessage: "GSC refresh failed.",
2050
+ details: { project }
2051
+ });
2052
+ if (current.status === "partial" && format !== "json") {
2053
+ process.stderr.write("Refresh completed with some errors.\n");
2054
+ }
2055
+ await googleCoverage(project, format);
2056
+ }
1949
2057
  async function googleDeindexed(project, format) {
1950
2058
  const client = getClient5();
1951
2059
  const rows = await client.gscDeindexed(project);
@@ -2213,14 +2321,22 @@ var GOOGLE_CLI_COMMANDS = [
2213
2321
  });
2214
2322
  }
2215
2323
  },
2324
+ {
2325
+ path: ["google", "refresh"],
2326
+ usage: "canonry google refresh <project> [--format json]",
2327
+ run: async (input) => {
2328
+ const project = requireProject(input, "google.refresh", "canonry google refresh <project> [--format json]");
2329
+ await googleRefresh(project, input.format);
2330
+ }
2331
+ },
2216
2332
  {
2217
2333
  path: ["google"],
2218
- usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|discover-sitemaps|sync|performance|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing> <project> [args]",
2334
+ usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|discover-sitemaps|sync|performance|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
2219
2335
  run: async (input) => {
2220
2336
  unknownSubcommand(input.positionals[0], {
2221
2337
  command: "google",
2222
- usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|discover-sitemaps|sync|performance|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing> <project> [args]",
2223
- available: ["connect", "disconnect", "status", "properties", "set-property", "set-sitemap", "list-sitemaps", "discover-sitemaps", "sync", "performance", "inspect", "inspect-sitemap", "coverage", "coverage-history", "inspections", "deindexed", "request-indexing"]
2338
+ usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|discover-sitemaps|sync|performance|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
2339
+ available: ["connect", "disconnect", "status", "properties", "set-property", "set-sitemap", "list-sitemaps", "discover-sitemaps", "sync", "performance", "inspect", "inspect-sitemap", "coverage", "coverage-history", "inspections", "deindexed", "request-indexing", "refresh"]
2224
2340
  });
2225
2341
  }
2226
2342
  }
@@ -2229,8 +2345,7 @@ var GOOGLE_CLI_COMMANDS = [
2229
2345
  // src/commands/keyword.ts
2230
2346
  import fs from "fs";
2231
2347
  function getClient6() {
2232
- const config = loadConfig();
2233
- return new ApiClient(config.apiUrl, config.apiKey);
2348
+ return createApiClient();
2234
2349
  }
2235
2350
  async function addKeywords(project, keywords, format) {
2236
2351
  const client = getClient6();
@@ -2474,8 +2589,7 @@ var KEYWORD_CLI_COMMANDS = [
2474
2589
 
2475
2590
  // src/commands/notify.ts
2476
2591
  function getClient7() {
2477
- const config = loadConfig();
2478
- return new ApiClient(config.apiUrl, config.apiKey);
2592
+ return createApiClient();
2479
2593
  }
2480
2594
  async function addNotification(project, opts) {
2481
2595
  const client = getClient7();
@@ -2551,7 +2665,7 @@ function listEvents(format) {
2551
2665
  function printNotification(n) {
2552
2666
  console.log(` ID: ${n.id}`);
2553
2667
  console.log(` Channel: ${n.channel}`);
2554
- console.log(` URL: ${n.url}`);
2668
+ console.log(` URL: ${n.urlDisplay ?? n.url}`);
2555
2669
  console.log(` Events: ${n.events.join(", ")}`);
2556
2670
  console.log(` Enabled: ${n.enabled ? "yes" : "no"}`);
2557
2671
  }
@@ -2647,8 +2761,7 @@ async function applyConfigFile(filePath) {
2647
2761
  }
2648
2762
  const content = fs2.readFileSync(filePath, "utf-8");
2649
2763
  const docs = parseAllDocuments(content);
2650
- const clientConfig = loadConfig();
2651
- const client = new ApiClient(clientConfig.apiUrl, clientConfig.apiKey);
2764
+ const client = createApiClient();
2652
2765
  const errors = [];
2653
2766
  const applied = [];
2654
2767
  for (let i = 0; i < docs.length; i++) {
@@ -2710,8 +2823,7 @@ async function applyConfigs(filePaths, format) {
2710
2823
 
2711
2824
  // src/commands/analytics.ts
2712
2825
  function getClient8() {
2713
- const config = loadConfig();
2714
- return new ApiClient(config.apiUrl, config.apiKey);
2826
+ return createApiClient();
2715
2827
  }
2716
2828
  async function showAnalytics(project, options) {
2717
2829
  const client = getClient8();
@@ -2818,8 +2930,7 @@ Source Origin Breakdown`);
2818
2930
 
2819
2931
  // src/commands/evidence.ts
2820
2932
  function getClient9() {
2821
- const config = loadConfig();
2822
- return new ApiClient(config.apiUrl, config.apiKey);
2933
+ return createApiClient();
2823
2934
  }
2824
2935
  async function showEvidence(project, format) {
2825
2936
  const client = getClient9();
@@ -2854,8 +2965,7 @@ async function showEvidence(project, format) {
2854
2965
  // src/commands/export-cmd.ts
2855
2966
  import { stringify } from "yaml";
2856
2967
  async function exportProject(project, opts) {
2857
- const config = loadConfig();
2858
- const client = new ApiClient(config.apiUrl, config.apiKey);
2968
+ const client = createApiClient();
2859
2969
  const data = await client.getExport(project);
2860
2970
  if (opts.includeResults) {
2861
2971
  try {
@@ -2876,8 +2986,7 @@ async function exportProject(project, opts) {
2876
2986
 
2877
2987
  // src/commands/history.ts
2878
2988
  function getClient10() {
2879
- const config = loadConfig();
2880
- return new ApiClient(config.apiUrl, config.apiKey);
2989
+ return createApiClient();
2881
2990
  }
2882
2991
  async function showHistory(project, format) {
2883
2992
  const client = getClient10();
@@ -2916,8 +3025,7 @@ async function showHistory(project, format) {
2916
3025
 
2917
3026
  // src/commands/status.ts
2918
3027
  function getClient11() {
2919
- const config = loadConfig();
2920
- return new ApiClient(config.apiUrl, config.apiKey);
3028
+ return createApiClient();
2921
3029
  }
2922
3030
  async function showStatus(project, format) {
2923
3031
  const client = getClient11();
@@ -3029,8 +3137,7 @@ var OPERATOR_CLI_COMMANDS = [
3029
3137
 
3030
3138
  // src/commands/project.ts
3031
3139
  function getClient12() {
3032
- const config = loadConfig();
3033
- return new ApiClient(config.apiUrl, config.apiKey);
3140
+ return createApiClient();
3034
3141
  }
3035
3142
  async function createProject(name, opts) {
3036
3143
  const client = getClient12();
@@ -3366,8 +3473,7 @@ var PROJECT_CLI_COMMANDS = [
3366
3473
 
3367
3474
  // src/commands/run.ts
3368
3475
  function getClient13() {
3369
- const config = loadConfig();
3370
- return new ApiClient(config.apiUrl, config.apiKey);
3476
+ return createApiClient();
3371
3477
  }
3372
3478
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
3373
3479
  async function triggerRun(project, opts) {
@@ -3696,8 +3802,7 @@ var RUN_CLI_COMMANDS = [
3696
3802
 
3697
3803
  // src/commands/schedule.ts
3698
3804
  function getClient14() {
3699
- const config = loadConfig();
3700
- return new ApiClient(config.apiUrl, config.apiKey);
3805
+ return createApiClient();
3701
3806
  }
3702
3807
  async function setSchedule(project, opts) {
3703
3808
  const client = getClient14();
@@ -3860,8 +3965,7 @@ var SCHEDULE_CLI_COMMANDS = [
3860
3965
 
3861
3966
  // src/commands/settings.ts
3862
3967
  function getClient15() {
3863
- const config = loadConfig();
3864
- return new ApiClient(config.apiUrl, config.apiKey);
3968
+ return createApiClient();
3865
3969
  }
3866
3970
  async function setProvider(name, opts) {
3867
3971
  const client = getClient15();
@@ -5021,6 +5125,7 @@ Usage:
5021
5125
  canonry google request-indexing <project> <url> Request Google indexing for a URL
5022
5126
  canonry google request-indexing <project> --all-unindexed Request indexing for all unindexed URLs
5023
5127
  canonry google coverage <project> Show index coverage summary
5128
+ canonry google refresh <project> Force-fetch fresh GSC coverage data and display updated summary
5024
5129
  canonry google inspections <project> Show URL inspection history (--url <url>)
5025
5130
  canonry google deindexed <project> Show pages that lost indexing
5026
5131
  canonry bing connect <project> Connect Bing Webmaster Tools (prompted for API key)
@@ -5029,6 +5134,7 @@ Usage:
5029
5134
  canonry bing sites <project> List registered Bing sites
5030
5135
  canonry bing set-site <project> <url> Set active Bing site
5031
5136
  canonry bing coverage <project> Show Bing index coverage summary
5137
+ canonry bing refresh <project> Force-fetch fresh Bing coverage data and display updated summary
5032
5138
  canonry bing inspect <project> <url> Inspect a URL via Bing
5033
5139
  canonry bing inspections <project> Show Bing URL inspection history (--url <url>)
5034
5140
  canonry bing request-indexing <project> <url> Submit URL to Bing for indexing
package/dist/index.d.ts CHANGED
@@ -69,6 +69,7 @@ interface CanonryConfig {
69
69
  google?: GoogleConfigEntry;
70
70
  bing?: BingConfigEntry;
71
71
  ga4?: Ga4ConfigEntry;
72
+ dashboardPasswordHash?: string;
72
73
  telemetry?: boolean;
73
74
  anonymousId?: string;
74
75
  }
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createServer,
3
3
  loadConfig
4
- } from "./chunk-G5TMCVS4.js";
4
+ } from "./chunk-NVCAUQ33.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.25.3",
3
+ "version": "1.26.2",
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,17 +53,17 @@
53
53
  "tsup": "^8.5.1",
54
54
  "tsx": "^4.19.0",
55
55
  "@ainyc/canonry-contracts": "0.0.0",
56
- "@ainyc/canonry-config": "0.0.0",
57
56
  "@ainyc/canonry-api-routes": "0.0.0",
58
- "@ainyc/canonry-provider-local": "0.0.0",
59
- "@ainyc/canonry-provider-claude": "0.0.0",
60
57
  "@ainyc/canonry-db": "0.0.0",
58
+ "@ainyc/canonry-config": "0.0.0",
59
+ "@ainyc/canonry-provider-claude": "0.0.0",
60
+ "@ainyc/canonry-provider-gemini": "0.0.0",
61
61
  "@ainyc/canonry-provider-cdp": "0.0.0",
62
+ "@ainyc/canonry-provider-local": "0.0.0",
62
63
  "@ainyc/canonry-integration-bing": "0.0.0",
63
- "@ainyc/canonry-provider-gemini": "0.0.0",
64
+ "@ainyc/canonry-integration-google": "0.0.0",
64
65
  "@ainyc/canonry-provider-openai": "0.0.0",
65
- "@ainyc/canonry-provider-perplexity": "0.0.0",
66
- "@ainyc/canonry-integration-google": "0.0.0"
66
+ "@ainyc/canonry-provider-perplexity": "0.0.0"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsup && tsx build-web.ts",