@ainyc/canonry 4.76.2 → 4.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/assets/assets/{BacklinksPage-GkTp8zLM.js → BacklinksPage-CwXveumn.js} +1 -1
  2. package/assets/assets/{ChartPrimitives-0noLg9_T.js → ChartPrimitives-DntKGI5J.js} +1 -1
  3. package/assets/assets/{ProjectPage-Bg5sKYhO.js → ProjectPage-CVudiU8X.js} +4 -4
  4. package/assets/assets/{RunRow-BEhaQDol.js → RunRow-DMtYXaxG.js} +1 -1
  5. package/assets/assets/{RunsPage-CV2wKkaz.js → RunsPage-Cz-YlucO.js} +1 -1
  6. package/assets/assets/{SettingsPage-Ctij-T9F.js → SettingsPage-BCuG3C-0.js} +1 -1
  7. package/assets/assets/TrafficPage-DV8X47wa.js +1 -0
  8. package/assets/assets/TrafficSourceDetailPage-BmYhK9jm.js +1 -0
  9. package/assets/assets/arrow-left-CUmHyNnF.js +1 -0
  10. package/assets/assets/extract-error-message-DFjy9_zi.js +1 -0
  11. package/assets/assets/{index-DRhoqa2-.css → index-BgWgJE7S.css} +1 -1
  12. package/assets/assets/{index-B-_Dwwhd.js → index-D9smxU6R.js} +4 -4
  13. package/assets/assets/{trash-2-C65kNIO5.js → trash-2-B_UtEEm8.js} +1 -1
  14. package/assets/index.html +2 -2
  15. package/dist/{chunk-BIT3JPMW.js → chunk-3WXARKUE.js} +105 -15
  16. package/dist/{chunk-ETM5EPQH.js → chunk-KPN22EWK.js} +2 -2
  17. package/dist/{chunk-ZUQK4TLL.js → chunk-QKTIP6GC.js} +46 -17
  18. package/dist/{chunk-42TDEKKM.js → chunk-XI6YSTGE.js} +1 -1
  19. package/dist/cli.js +13 -4
  20. package/dist/index.js +4 -4
  21. package/dist/{intelligence-service-QIGX7IVQ.js → intelligence-service-CDVUUG7O.js} +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/package.json +10 -10
  24. package/assets/assets/TrafficPage-TnIPaMcL.js +0 -1
  25. package/assets/assets/TrafficSourceDetailPage-BBmIwaee.js +0 -1
  26. package/assets/assets/extract-error-message-BdLppivS.js +0 -1
  27. package/assets/assets/server-traffic-ScU00kCP.js +0 -1
@@ -1 +1 @@
1
- import{c}from"./index-B-_Dwwhd.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
1
+ import{c}from"./index-D9smxU6R.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
package/assets/index.html CHANGED
@@ -12,12 +12,12 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-B-_Dwwhd.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-D9smxU6R.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dq7p98wZ.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-B57xfQbP.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-ClRVR6aX.js">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-DK7fbRNb.js">
20
- <link rel="stylesheet" crossorigin href="./assets/index-DRhoqa2-.css">
20
+ <link rel="stylesheet" crossorigin href="./assets/index-BgWgJE7S.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
@@ -232,7 +232,7 @@ import {
232
232
  wordpressSchemaDeployResultDtoSchema,
233
233
  wordpressSchemaStatusResultDtoSchema,
234
234
  wordpressStatusDtoSchema
235
- } from "./chunk-ETM5EPQH.js";
235
+ } from "./chunk-KPN22EWK.js";
236
236
 
237
237
  // src/intelligence-service.ts
238
238
  import { eq as eq33, desc as desc17, asc as asc4, and as and24, ne as ne5, or as or5, inArray as inArray12, gte as gte6, lte as lte3 } from "drizzle-orm";
@@ -19153,6 +19153,68 @@ async function getValidToken(store, domain, connectionType, clientId, clientSecr
19153
19153
  propertyId: conn.propertyId ?? null
19154
19154
  };
19155
19155
  }
19156
+ function parseGscApiDisabled(message) {
19157
+ if (!/accessNotConfigured|SERVICE_DISABLED|has not been used in project|is disabled/i.test(message)) {
19158
+ return null;
19159
+ }
19160
+ const projectNumber = message.match(/[?&]project=(\d+)/)?.[1] ?? message.match(/project\s+(\d+)/i)?.[1] ?? null;
19161
+ const base = "https://console.developers.google.com/apis/api";
19162
+ const qs = projectNumber ? `?project=${projectNumber}` : "";
19163
+ return {
19164
+ projectNumber,
19165
+ enableUrl: `${base}/searchconsole.googleapis.com/overview${qs}`,
19166
+ indexingApiUrl: `${base}/indexing.googleapis.com/overview${qs}`
19167
+ };
19168
+ }
19169
+ function googleAuthErrorStatus(err) {
19170
+ if (err.statusCode != null) return err.statusCode;
19171
+ const match = err.message.match(/failed \((\d{3})\)/);
19172
+ return match ? Number(match[1]) : null;
19173
+ }
19174
+ function gscErrorToAppError(err, context) {
19175
+ if (err instanceof AppError) return err;
19176
+ if (err instanceof GoogleApiError) {
19177
+ if (err.status === 429) {
19178
+ return quotaExceeded("Google Search Console API (rate limited; retries exhausted)");
19179
+ }
19180
+ if (err.status === 403) {
19181
+ const disabled = parseGscApiDisabled(err.message);
19182
+ if (disabled) {
19183
+ const inProject = disabled.projectNumber ? ` (project ${disabled.projectNumber})` : "";
19184
+ return forbidden(
19185
+ `${context}: the Google Search Console API is not enabled for your Google Cloud project${inProject}. Enable the Search Console API and the Indexing API, wait ~2\u20135 minutes, then retry: ${disabled.enableUrl}`,
19186
+ { reason: "gsc-api-disabled", upstreamStatus: 403, ...disabled }
19187
+ );
19188
+ }
19189
+ return forbidden(
19190
+ `${context}: the connected Google account does not have access to a verified Search Console property for this domain. Connect the account that owns the property.`,
19191
+ { reason: "gsc-no-property-access", upstreamStatus: 403 }
19192
+ );
19193
+ }
19194
+ if (err.status === 401) {
19195
+ return forbidden(
19196
+ `${context}: the Google connection has expired or was revoked. Reconnect Google Search Console.`,
19197
+ { reason: "gsc-reconnect", upstreamStatus: 401 }
19198
+ );
19199
+ }
19200
+ return providerError(`${context}: ${err.message}`, { upstreamStatus: err.status });
19201
+ }
19202
+ if (err instanceof GoogleAuthError) {
19203
+ const status = googleAuthErrorStatus(err);
19204
+ if (status === 429) return quotaExceeded("Google OAuth token refresh (rate limited)");
19205
+ if (status != null && status >= 400 && status < 500) {
19206
+ return forbidden(
19207
+ `${context}: the stored Google credentials are no longer valid (token refresh failed). Reconnect Google Search Console.`,
19208
+ { reason: "gsc-reconnect", upstreamStatus: status }
19209
+ );
19210
+ }
19211
+ return providerError(
19212
+ `${context}: ${err.message}. Reconnect Google Search Console if this persists.`,
19213
+ status != null ? { upstreamStatus: status } : void 0
19214
+ );
19215
+ }
19216
+ return providerError(`${context}: ${err instanceof Error ? err.message : String(err)}`);
19217
+ }
19156
19218
  async function googleRoutes(app, opts) {
19157
19219
  if (opts.googleStateSecret === void 0) {
19158
19220
  app.log.warn(
@@ -19375,9 +19437,13 @@ async function googleRoutes(app, opts) {
19375
19437
  }
19376
19438
  const store = requireConnectionStore();
19377
19439
  const project = resolveProject(app.db, request.params.name);
19378
- const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19379
- const sites = await listSites(accessToken);
19380
- return { sites };
19440
+ try {
19441
+ const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19442
+ const sites = await listSites(accessToken);
19443
+ return { sites };
19444
+ } catch (err) {
19445
+ throw gscErrorToAppError(err, "Failed to list Search Console properties");
19446
+ }
19381
19447
  });
19382
19448
  app.post("/projects/:name/google/gsc/sync", async (request) => {
19383
19449
  const store = requireConnectionStore();
@@ -19470,11 +19536,16 @@ async function googleRoutes(app, opts) {
19470
19536
  if (!url) {
19471
19537
  throw validationError("url is required");
19472
19538
  }
19473
- const { accessToken, propertyId } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19474
- if (!propertyId) {
19475
- throw validationError("No GSC property configured for this connection");
19539
+ let result;
19540
+ try {
19541
+ const { accessToken, propertyId } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19542
+ if (!propertyId) {
19543
+ throw validationError("No GSC property configured for this connection");
19544
+ }
19545
+ result = await inspectUrl(accessToken, url, propertyId);
19546
+ } catch (err) {
19547
+ throw gscErrorToAppError(err, "Failed to inspect URL in Search Console");
19476
19548
  }
19477
- const result = await inspectUrl(accessToken, url, propertyId);
19478
19549
  const ir = result.inspectionResult;
19479
19550
  const idx = ir.indexStatusResult;
19480
19551
  const mob = ir.mobileUsabilityResult;
@@ -19682,12 +19753,16 @@ async function googleRoutes(app, opts) {
19682
19753
  }
19683
19754
  const store = requireConnectionStore();
19684
19755
  const project = resolveProject(app.db, request.params.name);
19685
- const { accessToken, propertyId } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19686
- if (!propertyId) {
19687
- throw validationError('No GSC property configured for this connection. Set one with "canonry google set-property".');
19756
+ try {
19757
+ const { accessToken, propertyId } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19758
+ if (!propertyId) {
19759
+ throw validationError('No GSC property configured for this connection. Set one with "canonry google set-property".');
19760
+ }
19761
+ const sitemaps = await listSitemaps(accessToken, propertyId);
19762
+ return { sitemaps };
19763
+ } catch (err) {
19764
+ throw gscErrorToAppError(err, "Failed to list Search Console sitemaps");
19688
19765
  }
19689
- const sitemaps = await listSitemaps(accessToken, propertyId);
19690
- return { sitemaps };
19691
19766
  });
19692
19767
  app.post("/projects/:name/google/gsc/discover-sitemaps", async (request) => {
19693
19768
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getAuthConfig();
@@ -19703,8 +19778,13 @@ async function googleRoutes(app, opts) {
19703
19778
  if (!conn.propertyId) {
19704
19779
  throw validationError("No GSC property configured for this connection");
19705
19780
  }
19706
- const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19707
- const sitemaps = await listSitemaps(accessToken, conn.propertyId);
19781
+ let sitemaps;
19782
+ try {
19783
+ const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19784
+ sitemaps = await listSitemaps(accessToken, conn.propertyId);
19785
+ } catch (err) {
19786
+ throw gscErrorToAppError(err, "Failed to discover Search Console sitemaps");
19787
+ }
19708
19788
  if (sitemaps.length === 0) {
19709
19789
  throw validationError("No sitemaps found for this GSC property. Submit a sitemap in Google Search Console first.");
19710
19790
  }
@@ -24303,6 +24383,8 @@ var CloudRunAuthError = class extends Error {
24303
24383
  this.body = body;
24304
24384
  this.name = "CloudRunAuthError";
24305
24385
  }
24386
+ httpStatus;
24387
+ body;
24306
24388
  };
24307
24389
  function createServiceAccountJwt2(clientEmail, privateKey, scope) {
24308
24390
  if (!clientEmail) throw new CloudRunAuthError("clientEmail is required");
@@ -24479,6 +24561,8 @@ var CloudRunLoggingApiError = class extends Error {
24479
24561
  this.body = body;
24480
24562
  this.name = "CloudRunLoggingApiError";
24481
24563
  }
24564
+ status;
24565
+ body;
24482
24566
  };
24483
24567
  function validateAccessToken3(accessToken) {
24484
24568
  if (!accessToken.trim()) {
@@ -27533,6 +27617,8 @@ var WordpressTrafficApiError = class extends Error {
27533
27617
  this.body = body;
27534
27618
  this.name = "WordpressTrafficApiError";
27535
27619
  }
27620
+ status;
27621
+ body;
27536
27622
  };
27537
27623
  function trimRequired(name, value) {
27538
27624
  const trimmed = value.trim();
@@ -27734,6 +27820,9 @@ var VercelLogsApiError = class extends Error {
27734
27820
  this.retryAfterSeconds = retryAfterSeconds;
27735
27821
  this.name = "VercelLogsApiError";
27736
27822
  }
27823
+ status;
27824
+ body;
27825
+ retryAfterSeconds;
27737
27826
  };
27738
27827
  function parseRetryAfter2(headerValue) {
27739
27828
  if (!headerValue) return void 0;
@@ -32571,6 +32660,7 @@ var IntelligenceService = class {
32571
32660
  constructor(db) {
32572
32661
  this.db = db;
32573
32662
  }
32663
+ db;
32574
32664
  /**
32575
32665
  * Analyze a completed run and persist insights + health snapshot.
32576
32666
  * Idempotent: deletes prior results for the same runId before inserting.
@@ -41,8 +41,8 @@ function authRequired(message = "Authentication required") {
41
41
  function authInvalid() {
42
42
  return new AppError("AUTH_INVALID", "Invalid API key", 401);
43
43
  }
44
- function forbidden(message = "Forbidden") {
45
- return new AppError("FORBIDDEN", message, 403);
44
+ function forbidden(message = "Forbidden", details) {
45
+ return new AppError("FORBIDDEN", message, 403, details);
46
46
  }
47
47
  function quotaExceeded(metric) {
48
48
  return new AppError("QUOTA_EXCEEDED", `Quota exceeded for ${metric}`, 429);
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-42TDEKKM.js";
12
+ } from "./chunk-XI6YSTGE.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -97,7 +97,7 @@ import {
97
97
  siteAuditPages,
98
98
  siteAuditSnapshots,
99
99
  usageCounters
100
- } from "./chunk-BIT3JPMW.js";
100
+ } from "./chunk-3WXARKUE.js";
101
101
  import {
102
102
  AGENT_MEMORY_VALUE_MAX_BYTES,
103
103
  AGENT_PROVIDER_IDS,
@@ -149,7 +149,7 @@ import {
149
149
  validationError,
150
150
  winnabilityClassLabel,
151
151
  withRetry
152
- } from "./chunk-ETM5EPQH.js";
152
+ } from "./chunk-KPN22EWK.js";
153
153
 
154
154
  // src/telemetry.ts
155
155
  import crypto from "crypto";
@@ -476,15 +476,17 @@ function resolveModel(config) {
476
476
  return config.model || DEFAULT_MODEL;
477
477
  }
478
478
  function createClient2(config) {
479
+ const httpOptions = config.baseUrl ? { httpOptions: { baseUrl: config.baseUrl } } : {};
479
480
  if (isVertexConfig(config)) {
480
481
  return new GoogleGenAI({
481
482
  vertexai: true,
482
483
  project: config.vertexProject,
483
484
  location: config.vertexRegion || "us-central1",
484
- ...config.vertexCredentials ? { googleAuthOptions: { keyFilename: config.vertexCredentials } } : {}
485
+ ...config.vertexCredentials ? { googleAuthOptions: { keyFilename: config.vertexCredentials } } : {},
486
+ ...httpOptions
485
487
  });
486
488
  }
487
- return new GoogleGenAI({ apiKey: config.apiKey });
489
+ return new GoogleGenAI({ apiKey: config.apiKey, ...httpOptions });
488
490
  }
489
491
  function validateConfig(config) {
490
492
  if ("vertexProject" in config && config.vertexProject !== void 0 && config.vertexProject.trim().length === 0) {
@@ -749,7 +751,7 @@ async function embedQueries(queries2, options) {
749
751
  if (!options.apiKey && !options.client) {
750
752
  throw new Error("embedQueries: missing apiKey");
751
753
  }
752
- const client = options.client ?? createGeminiEmbedClient(options.apiKey);
754
+ const client = options.client ?? createGeminiEmbedClient(options.apiKey, options.baseUrl);
753
755
  return client.embedBatch(queries2, {
754
756
  model: options.model ?? DEFAULT_EMBED_MODEL,
755
757
  taskType: CLUSTERING_TASK_TYPE,
@@ -770,8 +772,11 @@ function extractEmbeddingVectors(response, expectedLength) {
770
772
  return e.values;
771
773
  });
772
774
  }
773
- function createGeminiEmbedClient(apiKey) {
774
- const genai = new GoogleGenAI2({ apiKey });
775
+ function createEmbedGenAI(apiKey, baseUrl) {
776
+ return new GoogleGenAI2({ apiKey, ...baseUrl ? { httpOptions: { baseUrl } } : {} });
777
+ }
778
+ function createGeminiEmbedClient(apiKey, baseUrl) {
779
+ const genai = createEmbedGenAI(apiKey, baseUrl);
775
780
  return {
776
781
  async embedBatch(queries2, opts) {
777
782
  const response = await genai.models.embedContent({
@@ -792,6 +797,7 @@ function toGeminiConfig(config) {
792
797
  return {
793
798
  apiKey: config.apiKey ?? "",
794
799
  model: config.model,
800
+ baseUrl: config.baseUrl,
795
801
  quotaPolicy: config.quotaPolicy,
796
802
  vertexProject: config.vertexProject,
797
803
  vertexRegion: config.vertexRegion,
@@ -895,6 +901,12 @@ async function withRetry3(fn, options = {}) {
895
901
 
896
902
  // ../provider-openai/src/normalize.ts
897
903
  var DEFAULT_MODEL2 = "gpt-5.4";
904
+ function createClient3(config) {
905
+ return new OpenAI({
906
+ apiKey: config.apiKey,
907
+ ...config.baseUrl ? { baseURL: config.baseUrl } : {}
908
+ });
909
+ }
898
910
  function validateConfig2(config) {
899
911
  if (!config.apiKey || config.apiKey.length === 0) {
900
912
  return { ok: false, provider: "openai", message: "missing api key" };
@@ -910,7 +922,7 @@ async function healthcheck2(config) {
910
922
  const validation = validateConfig2(config);
911
923
  if (!validation.ok) return validation;
912
924
  try {
913
- const client = new OpenAI({ apiKey: config.apiKey });
925
+ const client = createClient3(config);
914
926
  const response = await withRetry3(
915
927
  () => client.responses.create({
916
928
  model: config.model ?? DEFAULT_MODEL2,
@@ -935,7 +947,7 @@ async function healthcheck2(config) {
935
947
  }
936
948
  async function executeTrackedQuery2(input) {
937
949
  const model = input.config.model ?? DEFAULT_MODEL2;
938
- const client = new OpenAI({ apiKey: input.config.apiKey });
950
+ const client = createClient3(input.config);
939
951
  const webSearchTool = { type: "web_search" };
940
952
  if (input.location) {
941
953
  webSearchTool.user_location = {
@@ -1108,7 +1120,7 @@ function extractDomainFromUri2(uri) {
1108
1120
  }
1109
1121
  async function generateText2(prompt, config) {
1110
1122
  const model = config.model ?? DEFAULT_MODEL2;
1111
- const client = new OpenAI({ apiKey: config.apiKey });
1123
+ const client = createClient3(config);
1112
1124
  const response = await withRetry3(
1113
1125
  () => client.responses.create({
1114
1126
  model,
@@ -1130,6 +1142,7 @@ function toOpenAIConfig(config) {
1130
1142
  return {
1131
1143
  apiKey: config.apiKey ?? "",
1132
1144
  model: config.model,
1145
+ baseUrl: config.baseUrl,
1133
1146
  quotaPolicy: config.quotaPolicy
1134
1147
  };
1135
1148
  }
@@ -1823,6 +1836,7 @@ var CDPProviderError = class extends Error {
1823
1836
  this.code = code;
1824
1837
  this.name = "CDPProviderError";
1825
1838
  }
1839
+ code;
1826
1840
  };
1827
1841
 
1828
1842
  // ../provider-cdp/src/connection.ts
@@ -3031,6 +3045,8 @@ var ProviderExecutionGate = class {
3031
3045
  this.maxConcurrency = maxConcurrency;
3032
3046
  this.maxPerMinute = maxPerMinute;
3033
3047
  }
3048
+ maxConcurrency;
3049
+ maxPerMinute;
3034
3050
  window = [];
3035
3051
  waiters = [];
3036
3052
  rateLimitChain = Promise.resolve();
@@ -4933,7 +4949,7 @@ function buildDefaultDeps(registry) {
4933
4949
  },
4934
4950
  async embed(queries2) {
4935
4951
  if (cfg.apiKey) {
4936
- return embedQueries(queries2, { apiKey: cfg.apiKey });
4952
+ return embedQueries(queries2, { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
4937
4953
  }
4938
4954
  throw new Error("Discovery currently requires a Gemini API key. Vertex-mode embeddings are not yet implemented.");
4939
4955
  },
@@ -5761,7 +5777,7 @@ function readStoredGroundingSources(rawResponse) {
5761
5777
  return result;
5762
5778
  }
5763
5779
  async function backfillInsightsCommand(project, opts) {
5764
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-QIGX7IVQ.js");
5780
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-CDVUUG7O.js");
5765
5781
  const config = loadConfig();
5766
5782
  const db = createClient(config.database);
5767
5783
  migrate(db);
@@ -7179,6 +7195,11 @@ var RunCoordinator = class {
7179
7195
  this.onInsightsGenerated = onInsightsGenerated;
7180
7196
  this.onAeroEvent = onAeroEvent;
7181
7197
  }
7198
+ db;
7199
+ notifier;
7200
+ intelligenceService;
7201
+ onInsightsGenerated;
7202
+ onAeroEvent;
7182
7203
  async onRunCompleted(runId, projectId) {
7183
7204
  const runRow = this.db.select().from(runs).where(eq14(runs.id, runId)).get();
7184
7205
  const kind = runRow?.kind ?? RunKinds["answer-visibility"];
@@ -8843,6 +8864,8 @@ var ProviderExecutionGate2 = class {
8843
8864
  this.maxConcurrency = maxConcurrency;
8844
8865
  this.maxPerMinute = maxPerMinute;
8845
8866
  }
8867
+ maxConcurrency;
8868
+ maxPerMinute;
8846
8869
  window = [];
8847
8870
  waiters = [];
8848
8871
  rateLimitChain = Promise.resolve();
@@ -8904,6 +8927,7 @@ var SnapshotService = class {
8904
8927
  constructor(registry) {
8905
8928
  this.registry = registry;
8906
8929
  }
8930
+ registry;
8907
8931
  async createReport(input) {
8908
8932
  const companyName = input.companyName.trim();
8909
8933
  const domain = normalizeDomain(input.domain);
@@ -9452,11 +9476,16 @@ var BROWSER_ADAPTERS = [
9452
9476
  var adapterMap = Object.fromEntries(
9453
9477
  API_ADAPTERS.map((a) => [a.name, a])
9454
9478
  );
9455
- function summarizeProviderConfig(provider, config) {
9479
+ function summarizeProviderConfig(config) {
9456
9480
  return {
9457
9481
  configured: Boolean(config?.apiKey || config?.baseUrl),
9458
9482
  model: config?.model ?? null,
9459
- baseUrl: provider === "local" ? config?.baseUrl ?? null : null,
9483
+ // baseUrl is surfaced for ALL providers, not just local — gemini/openai now
9484
+ // honor a custom endpoint, so repointing one must show in the settings
9485
+ // summary AND produce an audit diff. Omitting it for API providers would let
9486
+ // an endpoint redirect (a credential-exfiltration vector on a box where the
9487
+ // provider key is the carrier) happen with no audit trail.
9488
+ baseUrl: config?.baseUrl ?? null,
9460
9489
  quota: { ...config?.quota ?? DEFAULT_QUOTA }
9461
9490
  };
9462
9491
  }
@@ -10330,7 +10359,7 @@ async function createServer(opts) {
10330
10359
  if (!adapterMap[name]) return null;
10331
10360
  if (!opts.config.providers) opts.config.providers = {};
10332
10361
  const existing = opts.config.providers[name];
10333
- const beforeConfig = summarizeProviderConfig(name, existing);
10362
+ const beforeConfig = summarizeProviderConfig(existing);
10334
10363
  const mergedQuota = incomingQuota ? { ...existing?.quota ?? DEFAULT_QUOTA, ...incomingQuota } : existing?.quota;
10335
10364
  opts.config.providers[name] = {
10336
10365
  apiKey: apiKey || existing?.apiKey,
@@ -10369,7 +10398,7 @@ async function createServer(opts) {
10369
10398
  entry.vertexConfigured = !!opts.config.providers?.[name]?.vertexProject;
10370
10399
  }
10371
10400
  }
10372
- const afterConfig = summarizeProviderConfig(name, opts.config.providers[name]);
10401
+ const afterConfig = summarizeProviderConfig(opts.config.providers[name]);
10373
10402
  if (JSON.stringify(beforeConfig) !== JSON.stringify(afterConfig)) {
10374
10403
  const diff = JSON.stringify({
10375
10404
  before: existing ? beforeConfig : null,
@@ -22,7 +22,7 @@ import {
22
22
  trafficConnectVercelRequestSchema,
23
23
  trafficConnectWordpressRequestSchema,
24
24
  trafficEventKindSchema
25
- } from "./chunk-ETM5EPQH.js";
25
+ } from "./chunk-KPN22EWK.js";
26
26
 
27
27
  // src/config.ts
28
28
  import fs from "fs";
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  setTelemetrySource,
28
28
  showFirstRunNotice,
29
29
  trackEvent
30
- } from "./chunk-ZUQK4TLL.js";
30
+ } from "./chunk-QKTIP6GC.js";
31
31
  import {
32
32
  CliError,
33
33
  EXIT_SYSTEM_ERROR,
@@ -44,7 +44,7 @@ import {
44
44
  saveConfig,
45
45
  saveConfigPatch,
46
46
  usageError
47
- } from "./chunk-42TDEKKM.js";
47
+ } from "./chunk-XI6YSTGE.js";
48
48
  import {
49
49
  apiKeys,
50
50
  createClient,
@@ -52,7 +52,7 @@ import {
52
52
  projects,
53
53
  queries,
54
54
  renderReportHtml
55
- } from "./chunk-BIT3JPMW.js";
55
+ } from "./chunk-3WXARKUE.js";
56
56
  import {
57
57
  CcReleaseSyncStatuses,
58
58
  CheckScopes,
@@ -72,7 +72,7 @@ import {
72
72
  providerQuotaPolicySchema,
73
73
  resolveProviderInput,
74
74
  winnabilityClassSchema
75
- } from "./chunk-ETM5EPQH.js";
75
+ } from "./chunk-KPN22EWK.js";
76
76
 
77
77
  // src/cli.ts
78
78
  import { pathToFileURL } from "url";
@@ -8305,6 +8305,9 @@ var PdfWriter = class {
8305
8305
  this.bold = bold;
8306
8306
  this.addPage();
8307
8307
  }
8308
+ doc;
8309
+ regular;
8310
+ bold;
8308
8311
  usableWidth = PAGE_WIDTH - MARGIN * 2;
8309
8312
  page;
8310
8313
  y = 0;
@@ -9633,6 +9636,7 @@ var envSchema = z.object({
9633
9636
  // Gemini
9634
9637
  GEMINI_API_KEY: z.string().optional(),
9635
9638
  GEMINI_MODEL: z.string().optional(),
9639
+ GEMINI_BASE_URL: z.string().optional(),
9636
9640
  GEMINI_MAX_CONCURRENCY: z.coerce.number().int().positive().default(2),
9637
9641
  GEMINI_MAX_REQUESTS_PER_MINUTE: z.coerce.number().int().positive().default(10),
9638
9642
  GEMINI_MAX_REQUESTS_PER_DAY: z.coerce.number().int().positive().default(1e3),
@@ -9643,6 +9647,7 @@ var envSchema = z.object({
9643
9647
  // OpenAI
9644
9648
  OPENAI_API_KEY: z.string().optional(),
9645
9649
  OPENAI_MODEL: z.string().optional(),
9650
+ OPENAI_BASE_URL: z.string().optional(),
9646
9651
  OPENAI_MAX_CONCURRENCY: z.coerce.number().int().positive().default(2),
9647
9652
  OPENAI_MAX_REQUESTS_PER_MINUTE: z.coerce.number().int().positive().default(10),
9648
9653
  OPENAI_MAX_REQUESTS_PER_DAY: z.coerce.number().int().positive().default(1e3),
@@ -9669,11 +9674,13 @@ var bootstrapEnvSchema = z.object({
9669
9674
  CANONRY_DATABASE_PATH: z.string().optional(),
9670
9675
  GEMINI_API_KEY: z.string().optional(),
9671
9676
  GEMINI_MODEL: z.string().optional(),
9677
+ GEMINI_BASE_URL: z.string().optional(),
9672
9678
  GEMINI_VERTEX_PROJECT: z.string().optional(),
9673
9679
  GEMINI_VERTEX_REGION: z.string().optional(),
9674
9680
  GEMINI_VERTEX_CREDENTIALS: z.string().optional(),
9675
9681
  OPENAI_API_KEY: z.string().optional(),
9676
9682
  OPENAI_MODEL: z.string().optional(),
9683
+ OPENAI_BASE_URL: z.string().optional(),
9677
9684
  ANTHROPIC_API_KEY: z.string().optional(),
9678
9685
  ANTHROPIC_MODEL: z.string().optional(),
9679
9686
  PERPLEXITY_API_KEY: z.string().optional(),
@@ -9692,6 +9699,7 @@ function getBootstrapEnv(source, overrides) {
9692
9699
  providers.gemini = {
9693
9700
  apiKey: parsed.GEMINI_API_KEY ?? "",
9694
9701
  model: parsed.GEMINI_MODEL || "gemini-2.5-flash",
9702
+ baseUrl: parsed.GEMINI_BASE_URL,
9695
9703
  quota: providerQuotaPolicySchema.parse({
9696
9704
  maxConcurrency: 2,
9697
9705
  maxRequestsPerMinute: 10,
@@ -9706,6 +9714,7 @@ function getBootstrapEnv(source, overrides) {
9706
9714
  providers.openai = {
9707
9715
  apiKey: parsed.OPENAI_API_KEY,
9708
9716
  model: parsed.OPENAI_MODEL || "gpt-5.4",
9717
+ baseUrl: parsed.OPENAI_BASE_URL,
9709
9718
  quota: providerQuotaPolicySchema.parse({
9710
9719
  maxConcurrency: 2,
9711
9720
  maxRequestsPerMinute: 10,
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-ZUQK4TLL.js";
3
+ } from "./chunk-QKTIP6GC.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-42TDEKKM.js";
7
- import "./chunk-BIT3JPMW.js";
8
- import "./chunk-ETM5EPQH.js";
6
+ } from "./chunk-XI6YSTGE.js";
7
+ import "./chunk-3WXARKUE.js";
8
+ import "./chunk-KPN22EWK.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-BIT3JPMW.js";
4
- import "./chunk-ETM5EPQH.js";
3
+ } from "./chunk-3WXARKUE.js";
4
+ import "./chunk-KPN22EWK.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-42TDEKKM.js";
7
- import "./chunk-ETM5EPQH.js";
6
+ } from "./chunk-XI6YSTGE.js";
7
+ import "./chunk-KPN22EWK.js";
8
8
 
9
9
  // src/mcp/cli.ts
10
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainyc/canonry",
3
- "version": "4.76.2",
3
+ "version": "4.78.0",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@ainyc/aeo-audit": "3.0.0",
40
- "@anthropic-ai/sdk": "^0.78.0",
41
- "@fastify/static": "^8.1.0",
40
+ "@anthropic-ai/sdk": "^0.91.1",
41
+ "@fastify/static": "^9.1.1",
42
42
  "@google/genai": "^1.46.0",
43
43
  "@mariozechner/pi-agent-core": "0.67.6",
44
44
  "@mariozechner/pi-ai": "0.67.6",
@@ -63,25 +63,25 @@
63
63
  "tsup": "^8.5.1",
64
64
  "tsx": "^4.19.0",
65
65
  "@ainyc/canonry-api-client": "0.0.0",
66
+ "@ainyc/canonry-config": "0.0.0",
66
67
  "@ainyc/canonry-api-routes": "0.0.0",
67
68
  "@ainyc/canonry-contracts": "0.0.0",
68
69
  "@ainyc/canonry-db": "0.0.0",
69
70
  "@ainyc/canonry-integration-bing": "0.0.0",
70
- "@ainyc/canonry-config": "0.0.0",
71
71
  "@ainyc/canonry-integration-cloud-run": "0.0.0",
72
72
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
73
73
  "@ainyc/canonry-integration-google": "0.0.0",
74
- "@ainyc/canonry-integration-google-places": "0.0.0",
75
- "@ainyc/canonry-integration-traffic": "0.0.0",
76
74
  "@ainyc/canonry-integration-google-business-profile": "0.0.0",
77
- "@ainyc/canonry-provider-cdp": "0.0.0",
78
- "@ainyc/canonry-intelligence": "0.0.0",
75
+ "@ainyc/canonry-integration-traffic": "0.0.0",
76
+ "@ainyc/canonry-integration-google-places": "0.0.0",
79
77
  "@ainyc/canonry-integration-wordpress": "0.0.0",
80
- "@ainyc/canonry-provider-gemini": "0.0.0",
78
+ "@ainyc/canonry-intelligence": "0.0.0",
81
79
  "@ainyc/canonry-provider-claude": "0.0.0",
80
+ "@ainyc/canonry-provider-cdp": "0.0.0",
82
81
  "@ainyc/canonry-provider-openai": "0.0.0",
83
82
  "@ainyc/canonry-provider-perplexity": "0.0.0",
84
- "@ainyc/canonry-provider-local": "0.0.0"
83
+ "@ainyc/canonry-provider-local": "0.0.0",
84
+ "@ainyc/canonry-provider-gemini": "0.0.0"
85
85
  },
86
86
  "scripts": {
87
87
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",