@ainyc/canonry 4.2.2 → 4.8.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.
@@ -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-IJEP6LB4.js";
11
12
 
12
13
  // src/intelligence-service.ts
13
14
  import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
@@ -2202,7 +2203,10 @@ function buildContentTargetRows(input) {
2202
2203
  existingAction: input.inProgressActions.get(targetRef) ?? null
2203
2204
  });
2204
2205
  }
2205
- return rows.sort((a, b) => b.score - a.score);
2206
+ return dedupeByIntent(
2207
+ rows.sort((a, b) => b.score - a.score),
2208
+ input.queryIntentModifiers ?? []
2209
+ );
2206
2210
  }
2207
2211
  function buildContentSourceRows(input) {
2208
2212
  return input.candidateQueries.map((cq) => ({
@@ -2273,6 +2277,49 @@ function computeAiReferralFactor(totalAiReferralSessions, competitorCount) {
2273
2277
  const competitorBoost = competitorCount > 0 ? 0.1 : 0;
2274
2278
  return Math.min(baseline + competitorBoost, 1);
2275
2279
  }
2280
+ var QUERY_INTENT_STOPWORDS = /* @__PURE__ */ new Set([
2281
+ "a",
2282
+ "an",
2283
+ "and",
2284
+ "at",
2285
+ "by",
2286
+ "for",
2287
+ "from",
2288
+ "in",
2289
+ "near",
2290
+ "of",
2291
+ "on",
2292
+ "or",
2293
+ "the",
2294
+ "to"
2295
+ ]);
2296
+ function dedupeByIntent(rows, modifiers) {
2297
+ if (rows.length <= 1 || modifiers.length === 0) return rows;
2298
+ const seen = /* @__PURE__ */ new Set();
2299
+ const result = [];
2300
+ const modifierTokens = new Set(
2301
+ modifiers.flatMap(tokenizeQuery).map(normalizeToken).filter(Boolean)
2302
+ );
2303
+ for (const row of rows) {
2304
+ const key = intentKey(row.query, modifierTokens);
2305
+ if (!key || seen.has(key)) continue;
2306
+ seen.add(key);
2307
+ result.push(row);
2308
+ }
2309
+ return result;
2310
+ }
2311
+ function intentKey(query, modifierTokens) {
2312
+ const tokens = tokenizeQuery(query).map(normalizeToken).filter(Boolean).filter((token) => !QUERY_INTENT_STOPWORDS.has(token)).filter((token) => !modifierTokens.has(token));
2313
+ return [...new Set(tokens)].sort().join(" ");
2314
+ }
2315
+ function tokenizeQuery(value) {
2316
+ return value.toLowerCase().match(/[a-z0-9]+/g) ?? [];
2317
+ }
2318
+ function normalizeToken(token) {
2319
+ if (token.length > 4 && token.endsWith("ies")) return `${token.slice(0, -3)}y`;
2320
+ if (token.length > 4 && token.endsWith("s") && !token.endsWith("ss")) return token.slice(0, -1);
2321
+ return token;
2322
+ }
2276
2323
  function pickTopCompetitor(competitors2) {
2277
2324
  if (competitors2.length === 0) return null;
2278
2325
  const top = [...competitors2].sort((a, b) => b.citationCount - a.citationCount)[0];
@@ -2581,8 +2628,13 @@ function buildAiSourceOrigin(snapshots, projectDomains, competitorDomains, topDo
2581
2628
  for (const snap of snapshots) {
2582
2629
  for (const raw of snap.citedDomains) {
2583
2630
  if (citedDomainBelongsToProject(raw, projectDomains)) continue;
2584
- const { category, label, domain } = categorizeSource(raw);
2585
- const cat = categoryCounts.get(category) ?? { label, count: 0 };
2631
+ const { category, domain } = categorizeSourceWithCompetitors(
2632
+ raw,
2633
+ competitorDomains,
2634
+ citedDomainBelongsToProject
2635
+ );
2636
+ const bucketLabel = categoryLabel(category);
2637
+ const cat = categoryCounts.get(category) ?? { label: bucketLabel, count: 0 };
2586
2638
  cat.count++;
2587
2639
  categoryCounts.set(category, cat);
2588
2640
  domainCounts.set(domain, (domainCounts.get(domain) ?? 0) + 1);
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-O4VXWABZ.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-5KIFQH52.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-QEPFB7UW.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-IJEP6LB4.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-X7CBN7S4.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-O4VXWABZ.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-5KIFQH52.js";
7
+ import "./chunk-QEPFB7UW.js";
8
+ import "./chunk-IJEP6LB4.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-QEPFB7UW.js";
4
+ import "./chunk-IJEP6LB4.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-5KIFQH52.js";
6
+ import "./chunk-IJEP6LB4.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.8.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",
@@ -60,20 +60,20 @@
60
60
  "tsup": "^8.5.1",
61
61
  "tsx": "^4.19.0",
62
62
  "@ainyc/canonry-api-routes": "0.0.0",
63
+ "@ainyc/canonry-config": "0.0.0",
63
64
  "@ainyc/canonry-contracts": "0.0.0",
64
- "@ainyc/canonry-db": "0.0.0",
65
65
  "@ainyc/canonry-intelligence": "0.0.0",
66
- "@ainyc/canonry-config": "0.0.0",
67
- "@ainyc/canonry-integration-commoncrawl": "0.0.0",
66
+ "@ainyc/canonry-db": "0.0.0",
68
67
  "@ainyc/canonry-integration-bing": "0.0.0",
69
- "@ainyc/canonry-integration-wordpress": "0.0.0",
68
+ "@ainyc/canonry-integration-google": "0.0.0",
69
+ "@ainyc/canonry-integration-commoncrawl": "0.0.0",
70
+ "@ainyc/canonry-provider-cdp": "0.0.0",
70
71
  "@ainyc/canonry-provider-claude": "0.0.0",
72
+ "@ainyc/canonry-integration-wordpress": "0.0.0",
71
73
  "@ainyc/canonry-provider-gemini": "0.0.0",
72
- "@ainyc/canonry-provider-cdp": "0.0.0",
73
- "@ainyc/canonry-integration-google": "0.0.0",
74
- "@ainyc/canonry-provider-openai": "0.0.0",
75
74
  "@ainyc/canonry-provider-local": "0.0.0",
76
- "@ainyc/canonry-provider-perplexity": "0.0.0"
75
+ "@ainyc/canonry-provider-perplexity": "0.0.0",
76
+ "@ainyc/canonry-provider-openai": "0.0.0"
77
77
  },
78
78
  "scripts": {
79
79
  "build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",