@ainyc/canonry 4.2.2 → 4.7.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.
@@ -4,10 +4,11 @@ import {
4
4
  RunKinds,
5
5
  __export,
6
6
  brandLabelFromDomain,
7
- categorizeSource,
7
+ categorizeSourceWithCompetitors,
8
+ categoryLabel,
8
9
  determineAnswerMentioned,
9
10
  normalizeProjectDomain
10
- } from "./chunk-T2I6AO7D.js";
11
+ } from "./chunk-XAW66QUX.js";
11
12
 
12
13
  // src/intelligence-service.ts
13
14
  import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
@@ -2581,8 +2582,13 @@ function buildAiSourceOrigin(snapshots, projectDomains, competitorDomains, topDo
2581
2582
  for (const snap of snapshots) {
2582
2583
  for (const raw of snap.citedDomains) {
2583
2584
  if (citedDomainBelongsToProject(raw, projectDomains)) continue;
2584
- const { category, label, domain } = categorizeSource(raw);
2585
- const cat = categoryCounts.get(category) ?? { label, count: 0 };
2585
+ const { category, domain } = categorizeSourceWithCompetitors(
2586
+ raw,
2587
+ competitorDomains,
2588
+ citedDomainBelongsToProject
2589
+ );
2590
+ const bucketLabel = categoryLabel(category);
2591
+ const cat = categoryCounts.get(category) ?? { label: bucketLabel, count: 0 };
2586
2592
  cat.count++;
2587
2593
  categoryCounts.set(category, cat);
2588
2594
  domainCounts.set(domain, (domainCounts.get(domain) ?? 0) + 1);
@@ -12,7 +12,7 @@ import {
12
12
  queryGenerateRequestSchema,
13
13
  runTriggerRequestSchema,
14
14
  scheduleUpsertRequestSchema
15
- } from "./chunk-T2I6AO7D.js";
15
+ } from "./chunk-XAW66QUX.js";
16
16
 
17
17
  // src/config.ts
18
18
  import fs from "fs";
@@ -1176,7 +1176,7 @@ var canonryMcpTools = [
1176
1176
  defineTool({
1177
1177
  name: "canonry_report",
1178
1178
  title: "Get aggregated AEO report",
1179
- description: "Returns the full client-facing AEO report bundle for a project \u2014 executive summary, per-query \xD7 per-provider citation matrix, competitor landscape, AI citation sources, GSC/GA4 performance, social and AI referrals, indexing health, citations trend, prioritized insights, and recommended next steps. Same payload `canonry report <project>` consumes to render the self-contained HTML.",
1179
+ description: "Returns the full canonical AEO report bundle for a project \u2014 executive summary, client summary, agency diagnostics, action plan, per-query \xD7 per-provider citation matrix, competitor landscape, AI citation sources, GSC/GA4 performance, social and AI referrals, indexing health, citations trend, prioritized insights, and recommended next steps. Same payload `canonry report <project>` consumes to render audience-specific HTML.",
1180
1180
  access: "read",
1181
1181
  tier: "monitoring",
1182
1182
  inputSchema: projectInputSchema,
@@ -42,6 +42,39 @@ var locationContextSchema = z.object({
42
42
  country: z.string().length(2),
43
43
  timezone: z.string().optional()
44
44
  });
45
+ var PROVIDER_LOCATION_HANDLING = {
46
+ gemini: {
47
+ treatment: "prompt",
48
+ description: "Location appended to the query text the Gemini model receives."
49
+ },
50
+ perplexity: {
51
+ treatment: "prompt",
52
+ description: "Location appended to the query text the Perplexity model receives."
53
+ },
54
+ local: {
55
+ treatment: "prompt",
56
+ description: "Location appended to the system message sent to the local model."
57
+ },
58
+ openai: {
59
+ treatment: "request-param",
60
+ description: "Location sent as a structured `user_location` field on OpenAI\u2019s web_search_preview tool."
61
+ },
62
+ claude: {
63
+ treatment: "request-param",
64
+ description: "Location sent as a structured `user_location` field on Anthropic\u2019s web_search_20250305 tool."
65
+ },
66
+ "cdp:chatgpt": {
67
+ treatment: "browser-geo",
68
+ description: "CDP relies on the browser session\u2019s own geolocation; canonry\u2019s configured location is not forwarded."
69
+ }
70
+ };
71
+ var UNKNOWN_PROVIDER_HANDLING = {
72
+ treatment: "ignored",
73
+ description: "No documented location handling for this provider \u2014 assume the configured location was not applied."
74
+ };
75
+ function getProviderLocationHandling(provider) {
76
+ return PROVIDER_LOCATION_HANDLING[provider] ?? UNKNOWN_PROVIDER_HANDLING;
77
+ }
45
78
 
46
79
  // ../contracts/src/run.ts
47
80
  var runStatusSchema = z2.enum(["queued", "running", "completed", "partial", "failed", "cancelled"]);
@@ -1030,6 +1063,29 @@ function windowCutoff(window) {
1030
1063
 
1031
1064
  // ../contracts/src/source-categories.ts
1032
1065
  var SOURCE_CATEGORY_RULES = [
1066
+ // Directories, marketplaces & review platforms — generic across industries.
1067
+ // Industry-specific directories (NRCA, GAF, etc.) intentionally omitted;
1068
+ // they would slip past for the next vertical and create maintenance churn.
1069
+ { pattern: "yelp.com", category: "directory", label: "Yelp" },
1070
+ { pattern: "angi.com", category: "directory", label: "Angi" },
1071
+ { pattern: "angieslist.com", category: "directory", label: "Angi" },
1072
+ { pattern: "homeadvisor.com", category: "directory", label: "HomeAdvisor" },
1073
+ { pattern: "bbb.org", category: "directory", label: "Better Business Bureau" },
1074
+ { pattern: "trustpilot.com", category: "directory", label: "Trustpilot" },
1075
+ { pattern: "houzz.com", category: "directory", label: "Houzz" },
1076
+ { pattern: "thumbtack.com", category: "directory", label: "Thumbtack" },
1077
+ { pattern: "nextdoor.com", category: "directory", label: "Nextdoor" },
1078
+ { pattern: "yellowpages.com", category: "directory", label: "Yellow Pages" },
1079
+ { pattern: "manta.com", category: "directory", label: "Manta" },
1080
+ { pattern: "foursquare.com", category: "directory", label: "Foursquare" },
1081
+ { pattern: "g2.com", category: "directory", label: "G2" },
1082
+ { pattern: "capterra.com", category: "directory", label: "Capterra" },
1083
+ { pattern: "getapp.com", category: "directory", label: "GetApp" },
1084
+ { pattern: "softwareadvice.com", category: "directory", label: "Software Advice" },
1085
+ { pattern: "trustradius.com", category: "directory", label: "TrustRadius" },
1086
+ { pattern: "producthunt.com", category: "directory", label: "Product Hunt" },
1087
+ { pattern: "glassdoor.com", category: "directory", label: "Glassdoor" },
1088
+ { pattern: "indeed.com", category: "directory", label: "Indeed" },
1033
1089
  // Forums
1034
1090
  { pattern: "reddit.com", category: "forum", label: "Reddit" },
1035
1091
  { pattern: "quora.com", category: "forum", label: "Quora" },
@@ -1090,6 +1146,8 @@ var SOURCE_CATEGORY_RULES = [
1090
1146
  { pattern: ".edu", category: "academic", label: "Academic (.edu)" }
1091
1147
  ];
1092
1148
  var CATEGORY_LABELS = {
1149
+ competitor: "Tracked competitors",
1150
+ directory: "Directories & review sites",
1093
1151
  social: "Social Media",
1094
1152
  forum: "Forums & Q&A",
1095
1153
  news: "News & Media",
@@ -1098,7 +1156,7 @@ var CATEGORY_LABELS = {
1098
1156
  ecommerce: "E-commerce",
1099
1157
  video: "Video",
1100
1158
  academic: "Academic",
1101
- other: "Other"
1159
+ other: "Independent sites"
1102
1160
  };
1103
1161
  function categorizeSource(uri) {
1104
1162
  let domain;
@@ -1116,6 +1174,13 @@ function categorizeSource(uri) {
1116
1174
  }
1117
1175
  return { category: "other", label: CATEGORY_LABELS.other, domain };
1118
1176
  }
1177
+ function categorizeSourceWithCompetitors(uri, competitorDomains, isCompetitorMatch) {
1178
+ const base = categorizeSource(uri);
1179
+ if (isCompetitorMatch(base.domain, competitorDomains)) {
1180
+ return { category: "competitor", label: CATEGORY_LABELS.competitor, domain: base.domain };
1181
+ }
1182
+ return base;
1183
+ }
1119
1184
  function categoryLabel(category) {
1120
1185
  return CATEGORY_LABELS[category];
1121
1186
  }
@@ -1897,6 +1962,14 @@ function citationStateToCited(state) {
1897
1962
  return state === "cited";
1898
1963
  }
1899
1964
 
1965
+ // ../contracts/src/report.ts
1966
+ function reportActionTone(action) {
1967
+ if (action.horizon === "immediate") return "negative";
1968
+ if (action.confidence === "high") return "caution";
1969
+ if (action.confidence === "low") return "neutral";
1970
+ return "caution";
1971
+ }
1972
+
1900
1973
  // ../contracts/src/skills.ts
1901
1974
  import { z as z19 } from "zod";
1902
1975
  var codingAgentSchema = z19.enum(["claude", "codex"]);
@@ -1911,6 +1984,7 @@ export {
1911
1984
  isBrowserProvider,
1912
1985
  resolveProviderInput,
1913
1986
  locationContextSchema,
1987
+ getProviderLocationHandling,
1914
1988
  notificationEventSchema,
1915
1989
  notificationCreateRequestSchema,
1916
1990
  findDuplicateLocationLabels,
@@ -1960,6 +2034,7 @@ export {
1960
2034
  parseWindow,
1961
2035
  windowCutoff,
1962
2036
  categorizeSource,
2037
+ categorizeSourceWithCompetitors,
1963
2038
  categoryLabel,
1964
2039
  extractAnswerMentions,
1965
2040
  determineAnswerMentioned,
@@ -1980,6 +2055,7 @@ export {
1980
2055
  normalizeUrlPath,
1981
2056
  emptyCitationVisibility,
1982
2057
  citationStateToCited,
2058
+ reportActionTone,
1983
2059
  CodingAgents,
1984
2060
  skillsClientSchema,
1985
2061
  SkillsClients
package/dist/cli.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  setGoogleAuthConfig,
19
19
  showFirstRunNotice,
20
20
  trackEvent
21
- } from "./chunk-HJZY4EOE.js";
21
+ } from "./chunk-DVTPGC6O.js";
22
22
  import {
23
23
  CliError,
24
24
  EXIT_SYSTEM_ERROR,
@@ -33,7 +33,7 @@ import {
33
33
  saveConfig,
34
34
  saveConfigPatch,
35
35
  usageError
36
- } from "./chunk-SR7TGHHG.js";
36
+ } from "./chunk-VDEMEI64.js";
37
37
  import {
38
38
  apiKeys,
39
39
  competitors,
@@ -45,7 +45,7 @@ import {
45
45
  projects,
46
46
  querySnapshots,
47
47
  runs
48
- } from "./chunk-7YSI4GFA.js";
48
+ } from "./chunk-OOADR2Q5.js";
49
49
  import {
50
50
  CcReleaseSyncStatuses,
51
51
  CheckScopes,
@@ -64,7 +64,7 @@ import {
64
64
  providerQuotaPolicySchema,
65
65
  resolveProviderInput,
66
66
  skillsClientSchema
67
- } from "./chunk-T2I6AO7D.js";
67
+ } from "./chunk-XAW66QUX.js";
68
68
 
69
69
  // src/cli.ts
70
70
  import { pathToFileURL } from "url";
@@ -580,7 +580,7 @@ function readStoredGroundingSources(rawResponse) {
580
580
  return result;
581
581
  }
582
582
  async function backfillInsightsCommand(project, opts) {
583
- const { IntelligenceService } = await import("./intelligence-service-CQGAXKKN.js");
583
+ const { IntelligenceService } = await import("./intelligence-service-ABHO5HHA.js");
584
584
  const config = loadConfig();
585
585
  const db = createClient(config.database);
586
586
  migrate(db);
@@ -5457,19 +5457,20 @@ var PROJECT_CLI_COMMANDS = [
5457
5457
  // src/commands/report.ts
5458
5458
  import fs5 from "fs";
5459
5459
  import path3 from "path";
5460
- function defaultOutputPath(project) {
5460
+ function defaultOutputPath(project, audience) {
5461
5461
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5462
- return path3.resolve(process.cwd(), `canonry-report-${project}-${date}.html`);
5462
+ return path3.resolve(process.cwd(), `canonry-report-${project}-${audience}-${date}.html`);
5463
5463
  }
5464
5464
  async function runReportCommand(project, opts = {}) {
5465
5465
  const client = createApiClient();
5466
5466
  const report = await client.getReport(project);
5467
+ const audience = opts.audience ?? "agency";
5467
5468
  if (opts.format === "json") {
5468
5469
  console.log(JSON.stringify(report, null, 2));
5469
5470
  return;
5470
5471
  }
5471
- const html = renderReportHtml(report);
5472
- const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project);
5472
+ const html = renderReportHtml(report, { audience });
5473
+ const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project, audience);
5473
5474
  const dir = path3.dirname(targetPath);
5474
5475
  if (!fs5.existsSync(dir)) {
5475
5476
  fs5.mkdirSync(dir, { recursive: true });
@@ -5479,18 +5480,27 @@ async function runReportCommand(project, opts = {}) {
5479
5480
  }
5480
5481
 
5481
5482
  // src/cli-commands/report.ts
5482
- var USAGE2 = "canonry report <project> [--output <path>] [--format json]";
5483
+ var USAGE2 = "canonry report <project> [--audience agency|client] [--output <path>] [--format json]";
5484
+ function parseAudience(value) {
5485
+ if (value === void 0) return void 0;
5486
+ if (value === "agency" || value === "client") return value;
5487
+ throw usageError(`Error: --audience must be "agency" or "client"
5488
+
5489
+ Usage: ${USAGE2}`);
5490
+ }
5483
5491
  var REPORT_CLI_COMMANDS = [
5484
5492
  {
5485
5493
  path: ["report"],
5486
5494
  usage: USAGE2,
5487
5495
  options: {
5496
+ audience: { type: "string" },
5488
5497
  output: { type: "string", short: "o" }
5489
5498
  },
5490
5499
  run: async (input) => {
5491
5500
  const project = requireProject(input, "report", USAGE2);
5492
5501
  await runReportCommand(project, {
5493
5502
  format: input.format,
5503
+ audience: parseAudience(getString(input.values, "audience")),
5494
5504
  output: getString(input.values, "output")
5495
5505
  });
5496
5506
  }
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-HJZY4EOE.js";
3
+ } from "./chunk-DVTPGC6O.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-SR7TGHHG.js";
7
- import "./chunk-7YSI4GFA.js";
8
- import "./chunk-T2I6AO7D.js";
6
+ } from "./chunk-VDEMEI64.js";
7
+ import "./chunk-OOADR2Q5.js";
8
+ import "./chunk-XAW66QUX.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-7YSI4GFA.js";
4
- import "./chunk-T2I6AO7D.js";
3
+ } from "./chunk-OOADR2Q5.js";
4
+ import "./chunk-XAW66QUX.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  CliError,
3
3
  canonryMcpTools,
4
4
  createApiClient
5
- } from "./chunk-SR7TGHHG.js";
6
- import "./chunk-T2I6AO7D.js";
5
+ } from "./chunk-VDEMEI64.js";
6
+ import "./chunk-XAW66QUX.js";
7
7
 
8
8
  // src/mcp/cli.ts
9
9
  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.2.2",
3
+ "version": "4.7.2",
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",
@@ -59,20 +59,20 @@
59
59
  "@types/node-cron": "^3.0.11",
60
60
  "tsup": "^8.5.1",
61
61
  "tsx": "^4.19.0",
62
- "@ainyc/canonry-api-routes": "0.0.0",
63
62
  "@ainyc/canonry-contracts": "0.0.0",
64
- "@ainyc/canonry-db": "0.0.0",
63
+ "@ainyc/canonry-api-routes": "0.0.0",
65
64
  "@ainyc/canonry-intelligence": "0.0.0",
66
- "@ainyc/canonry-config": "0.0.0",
67
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
68
65
  "@ainyc/canonry-integration-bing": "0.0.0",
66
+ "@ainyc/canonry-db": "0.0.0",
67
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
68
+ "@ainyc/canonry-integration-google": "0.0.0",
69
+ "@ainyc/canonry-config": "0.0.0",
69
70
  "@ainyc/canonry-integration-wordpress": "0.0.0",
71
+ "@ainyc/canonry-provider-cdp": "0.0.0",
70
72
  "@ainyc/canonry-provider-claude": "0.0.0",
73
+ "@ainyc/canonry-provider-local": "0.0.0",
71
74
  "@ainyc/canonry-provider-gemini": "0.0.0",
72
- "@ainyc/canonry-provider-cdp": "0.0.0",
73
- "@ainyc/canonry-integration-google": "0.0.0",
74
75
  "@ainyc/canonry-provider-openai": "0.0.0",
75
- "@ainyc/canonry-provider-local": "0.0.0",
76
76
  "@ainyc/canonry-provider-perplexity": "0.0.0"
77
77
  },
78
78
  "scripts": {