@ainyc/canonry 4.106.2 → 4.107.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.
- package/assets/assets/{BacklinksPage-BheRgyA3.js → BacklinksPage-Cuvg9TTn.js} +1 -1
- package/assets/assets/{ChartPrimitives-BnYX5sRb.js → ChartPrimitives-CoNjLN3a.js} +1 -1
- package/assets/assets/{ProjectPage-Bd3YW6WQ.js → ProjectPage-DhEoUnmx.js} +1 -1
- package/assets/assets/{RunRow-CZ1bXteZ.js → RunRow-Du9r-Mik.js} +1 -1
- package/assets/assets/{RunsPage-BBEuL03-.js → RunsPage-LIvuym9f.js} +1 -1
- package/assets/assets/{SettingsPage-CJ6kyLmY.js → SettingsPage-BrFy3IsH.js} +1 -1
- package/assets/assets/{TrafficPage-DPrsjoFM.js → TrafficPage-C16J18tV.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-EdC_c26M.js → TrafficSourceDetailPage-BtOwcgWX.js} +1 -1
- package/assets/assets/{arrow-left-B4_PxNUG.js → arrow-left-DL0hZP8l.js} +1 -1
- package/assets/assets/{extract-error-message-DoIzPHJI.js → extract-error-message-CgHHv6eH.js} +1 -1
- package/assets/assets/{index-Dr2rxkFR.js → index-54KuHvPl.js} +3 -3
- package/assets/assets/{trash-2-C9Ebw5p4.js → trash-2-CpYdnCg2.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-VGMTLUUM.js → chunk-KRP555VO.js} +2 -1
- package/dist/{chunk-VOR5ZTUU.js → chunk-MV3ZJOI7.js} +61 -0
- package/dist/{chunk-2AWATO4I.js → chunk-NDXV6EYD.js} +14 -4
- package/dist/{chunk-F3MKVMS5.js → chunk-QLWRJEII.js} +46 -3
- package/dist/cli.js +13 -8
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-JGZU27U3.js → intelligence-service-2DQLZUPW.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-54KuHvPl.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,7 +12,7 @@
|
|
|
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-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-54KuHvPl.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-C9EZkgbP.js">
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
trafficConnectVercelRequestSchema,
|
|
26
26
|
trafficConnectWordpressRequestSchema,
|
|
27
27
|
trafficEventKindSchema
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-MV3ZJOI7.js";
|
|
29
29
|
|
|
30
30
|
// src/config.ts
|
|
31
31
|
import fs from "fs";
|
|
@@ -5565,6 +5565,7 @@ var discoveryRunInputSchema = z2.object({
|
|
|
5565
5565
|
// Stronger descriptions for the LLM. The base Zod schema enforces the
|
|
5566
5566
|
// upper bound; this just clarifies the meaning of each knob.
|
|
5567
5567
|
icpDescription: z2.string().min(1).optional().describe("Free-text ICP description. If omitted, the project must already have spec.icpDescription stored."),
|
|
5568
|
+
buyerDescription: z2.string().min(1).optional().describe("Who evaluates or buys the offering, separate from the ICP. When present, every generated query is anchored on this buyer."),
|
|
5568
5569
|
dedupThreshold: z2.number().min(0).max(1).optional().describe("Cosine similarity threshold for clustering seed candidates. Defaults to 0.85. Lower values dedupe more aggressively."),
|
|
5569
5570
|
maxProbes: z2.number().int().positive().max(DISCOVERY_MAX_PROBES_CAP).optional().describe(`Max canonical queries to probe in this session. Default 100, hard cap ${DISCOVERY_MAX_PROBES_CAP}.`),
|
|
5570
5571
|
probeConcurrency: z2.number().int().min(1).max(DISCOVERY_PROBE_CONCURRENCY_CAP).optional().describe(`How many probes may run in parallel. Default 1 (strictly serial), hard cap ${DISCOVERY_PROBE_CONCURRENCY_CAP}. Probe rows are persisted in canonical order regardless of concurrency, so this only shortens wall-clock time.`)
|
|
@@ -2186,6 +2186,16 @@ var discoverySessionDtoSchema = z17.object({
|
|
|
2186
2186
|
*/
|
|
2187
2187
|
seedFromAnswerCount: z17.number().int().nullable().optional(),
|
|
2188
2188
|
seedFromGroundingCount: z17.number().int().nullable().optional(),
|
|
2189
|
+
/**
|
|
2190
|
+
* Diagnostics: how many raw seed candidates were dropped by the branded
|
|
2191
|
+
* self-query filter before seedCountRaw was recorded (the customer's own
|
|
2192
|
+
* brand or domain in the query text). Null on legacy sessions. Purely
|
|
2193
|
+
* additive: no gate or warning reads it.
|
|
2194
|
+
*/
|
|
2195
|
+
seedBrandFilteredCount: z17.number().int().nullable().optional(),
|
|
2196
|
+
/** Buyer definition the session was seeded with (part of session identity
|
|
2197
|
+
* for in-flight consolidation). Null on legacy / no-buyer sessions. */
|
|
2198
|
+
buyerDescription: z17.string().nullable().optional(),
|
|
2189
2199
|
dedupThreshold: z17.number().nullable().optional(),
|
|
2190
2200
|
probeCount: z17.number().int().nullable().optional(),
|
|
2191
2201
|
citedCount: z17.number().int().nullable().default(null),
|
|
@@ -2219,8 +2229,58 @@ function seedCollapseWarning(input) {
|
|
|
2219
2229
|
const noun = canonicalCount === 1 ? "query" : "queries";
|
|
2220
2230
|
return `Seed dedup collapsed ${seedCountRaw} raw candidates into ${canonicalCount} canonical ${noun} at threshold ${dedupThreshold}. Distinct intents were likely merged into one cluster; re-run with a higher --dedup-threshold.`;
|
|
2221
2231
|
}
|
|
2232
|
+
function filterBrandedSeedCandidates(input) {
|
|
2233
|
+
const tokens = brandMatchTokens(input.brandNames, input.canonicalDomains);
|
|
2234
|
+
if (tokens.length === 0) return { kept: [...input.candidates], droppedBranded: [] };
|
|
2235
|
+
const kept = [];
|
|
2236
|
+
const droppedBranded = [];
|
|
2237
|
+
for (const candidate of input.candidates) {
|
|
2238
|
+
const normalized = normalizeForBrandMatch(candidate);
|
|
2239
|
+
if (tokens.some((token) => containsWholeToken(normalized, token))) droppedBranded.push(candidate);
|
|
2240
|
+
else kept.push(candidate);
|
|
2241
|
+
}
|
|
2242
|
+
return { kept, droppedBranded };
|
|
2243
|
+
}
|
|
2244
|
+
function normalizeForBrandMatch(text) {
|
|
2245
|
+
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
2246
|
+
}
|
|
2247
|
+
function brandMatchTokens(brandNames, canonicalDomains) {
|
|
2248
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
2249
|
+
for (const name of brandNames) {
|
|
2250
|
+
const normalized = normalizeForBrandMatch(name);
|
|
2251
|
+
if (normalized.length < 3) continue;
|
|
2252
|
+
tokens.add(normalized);
|
|
2253
|
+
const squashed = normalized.replace(/[^a-z0-9]/g, "");
|
|
2254
|
+
if (squashed.length >= 4 && squashed !== normalized) tokens.add(squashed);
|
|
2255
|
+
}
|
|
2256
|
+
for (const domain of canonicalDomains) {
|
|
2257
|
+
const host = hostOf(domain) ?? normalizeForBrandMatch(domain).replace(/^www\./, "");
|
|
2258
|
+
if (!host) continue;
|
|
2259
|
+
tokens.add(host);
|
|
2260
|
+
tokens.add(`www.${host}`);
|
|
2261
|
+
}
|
|
2262
|
+
return [...tokens];
|
|
2263
|
+
}
|
|
2264
|
+
function containsWholeToken(normalized, token) {
|
|
2265
|
+
let index = normalized.indexOf(token);
|
|
2266
|
+
while (index !== -1) {
|
|
2267
|
+
const before = index === 0 ? " " : normalized[index - 1];
|
|
2268
|
+
const afterIndex = index + token.length;
|
|
2269
|
+
const after = afterIndex >= normalized.length ? " " : normalized[afterIndex];
|
|
2270
|
+
if (!/[a-z0-9]/.test(before) && !/[a-z0-9]/.test(after)) return true;
|
|
2271
|
+
index = normalized.indexOf(token, index + 1);
|
|
2272
|
+
}
|
|
2273
|
+
return false;
|
|
2274
|
+
}
|
|
2222
2275
|
var discoveryRunRequestSchema = z17.object({
|
|
2223
2276
|
icpDescription: z17.string().min(1).optional(),
|
|
2277
|
+
/**
|
|
2278
|
+
* Who evaluates or buys the offering, separate from what is sold
|
|
2279
|
+
* (icpDescription). When present, the seed prompt anchors every generated
|
|
2280
|
+
* query on this buyer, so discovery selects buyer-fit demand instead of
|
|
2281
|
+
* generic provider comparisons.
|
|
2282
|
+
*/
|
|
2283
|
+
buyerDescription: z17.string().min(1).optional(),
|
|
2224
2284
|
dedupThreshold: z17.number().min(0).max(1).optional(),
|
|
2225
2285
|
maxProbes: z17.number().int().positive().max(DISCOVERY_MAX_PROBES_CAP).optional(),
|
|
2226
2286
|
/**
|
|
@@ -5660,6 +5720,7 @@ export {
|
|
|
5660
5720
|
DISCOVERY_DEFAULT_PROBE_CONCURRENCY,
|
|
5661
5721
|
DISCOVERY_DEFAULT_DEDUP_THRESHOLD,
|
|
5662
5722
|
seedCollapseWarning,
|
|
5723
|
+
filterBrandedSeedCandidates,
|
|
5663
5724
|
discoveryRunRequestSchema,
|
|
5664
5725
|
discoveryPromoteRequestSchema,
|
|
5665
5726
|
discoveryPromotePreviewSchema,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KRP555VO.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -115,7 +115,7 @@ import {
|
|
|
115
115
|
siteAuditPages,
|
|
116
116
|
siteAuditSnapshots,
|
|
117
117
|
usageCounters
|
|
118
|
-
} from "./chunk-
|
|
118
|
+
} from "./chunk-QLWRJEII.js";
|
|
119
119
|
import {
|
|
120
120
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
121
121
|
AGENT_PROVIDER_IDS,
|
|
@@ -178,7 +178,7 @@ import {
|
|
|
178
178
|
validationError,
|
|
179
179
|
winnabilityClassLabel,
|
|
180
180
|
withRetry
|
|
181
|
-
} from "./chunk-
|
|
181
|
+
} from "./chunk-MV3ZJOI7.js";
|
|
182
182
|
|
|
183
183
|
// src/telemetry.ts
|
|
184
184
|
import crypto from "crypto";
|
|
@@ -5572,6 +5572,7 @@ async function executeDiscoveryRun(opts) {
|
|
|
5572
5572
|
sessionId: opts.sessionId,
|
|
5573
5573
|
project,
|
|
5574
5574
|
icpDescription: opts.icpDescription,
|
|
5575
|
+
buyerDescription: opts.buyerDescription,
|
|
5575
5576
|
dedupThreshold: opts.dedupThreshold,
|
|
5576
5577
|
maxProbes: opts.maxProbes,
|
|
5577
5578
|
probeConcurrency: opts.probeConcurrency,
|
|
@@ -5765,11 +5766,17 @@ function buildLocationConstraint(locations) {
|
|
|
5765
5766
|
function buildSeedPrompt(input) {
|
|
5766
5767
|
const locationConstraint = buildLocationConstraint(input.locations ?? []);
|
|
5767
5768
|
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
5769
|
+
const buyer = input.buyerDescription?.trim() ?? "";
|
|
5770
|
+
const brandIdentities = [...input.project.brandNames ?? [], ...input.project.canonicalDomains];
|
|
5768
5771
|
return [
|
|
5769
5772
|
"You are an AEO (Answer Engine Optimization) analyst expanding a tracked-query basket for a customer.",
|
|
5770
5773
|
"",
|
|
5771
5774
|
`Customer: ${input.project.name} (domains: ${input.project.canonicalDomains.join(", ")})`,
|
|
5772
5775
|
`ICP: ${input.icpDescription}`,
|
|
5776
|
+
...buyer ? [
|
|
5777
|
+
`Buyer: ${buyer}`,
|
|
5778
|
+
"Every query must be one this buyer would plausibly type into an answer engine. Skip queries only an industry insider, investor, or job seeker would ask."
|
|
5779
|
+
] : [],
|
|
5773
5780
|
...locationConstraint.length > 0 ? ["", ...locationConstraint] : [],
|
|
5774
5781
|
"",
|
|
5775
5782
|
"Brainstorm queries a member of this ICP would type into an AI answer engine (Gemini, ChatGPT, Perplexity). Generate candidates across the five intent buckets below \u2014 these are SEMANTICALLY DISTINCT search intents, not stylistic variants. A query should fit one bucket cleanly. Diversity across buckets is the point: it keeps the list from collapsing into near-synonyms of a single intent.",
|
|
@@ -5780,6 +5787,8 @@ function buildSeedPrompt(input) {
|
|
|
5780
5787
|
' 4. Comparative \u2014 the searcher is weighing named alternatives head-to-head. Templates: "X vs Y", "X or Y for Z", "alternatives to X".',
|
|
5781
5788
|
' 5. Transactional \u2014 the searcher is ready to act on a purchase or booking. Templates: "book X", "X pricing", "X discount code", "buy X online".',
|
|
5782
5789
|
"",
|
|
5790
|
+
`Hard rule: NEVER include the customer's own brand name or domain (${brandIdentities.join(", ") || input.project.name}) in any query. A buyer who already knows the business by name is out of scope for this list; the goal is queries where the customer must EARN the mention. Naming competitors or directories is fine (Navigational queries should target directories and platforms, never the customer).`,
|
|
5791
|
+
"",
|
|
5783
5792
|
`Generate EXACTLY ${QUERIES_PER_INTENT_BUCKET} queries per bucket \u2014 ${QUERIES_PER_INTENT_BUCKET * 5} total. Return ONE query per line. Plain text only \u2014 no numbering, bullets, quotes, bucket labels, or commentary.`
|
|
5784
5793
|
].join("\n");
|
|
5785
5794
|
}
|
|
@@ -6473,7 +6482,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
6473
6482
|
return result;
|
|
6474
6483
|
}
|
|
6475
6484
|
async function backfillInsightsCommand(project, opts) {
|
|
6476
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
6485
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-2DQLZUPW.js");
|
|
6477
6486
|
const config = loadConfig();
|
|
6478
6487
|
const db = createClient(config.database);
|
|
6479
6488
|
migrate(db);
|
|
@@ -11938,6 +11947,7 @@ async function createServer(opts) {
|
|
|
11938
11947
|
sessionId: input.sessionId,
|
|
11939
11948
|
projectId: input.projectId,
|
|
11940
11949
|
icpDescription: input.icpDescription,
|
|
11950
|
+
buyerDescription: input.buyerDescription,
|
|
11941
11951
|
dedupThreshold: input.dedupThreshold,
|
|
11942
11952
|
maxProbes: input.maxProbes,
|
|
11943
11953
|
probeConcurrency: input.probeConcurrency,
|
|
@@ -125,6 +125,7 @@ import {
|
|
|
125
125
|
emptyCitationVisibility,
|
|
126
126
|
escapeLikePattern,
|
|
127
127
|
extractAnswerMentions,
|
|
128
|
+
filterBrandedSeedCandidates,
|
|
128
129
|
findDuplicateLocationLabels,
|
|
129
130
|
forbidden,
|
|
130
131
|
formatAverageDelta,
|
|
@@ -267,7 +268,7 @@ import {
|
|
|
267
268
|
wordpressSchemaDeployResultDtoSchema,
|
|
268
269
|
wordpressSchemaStatusResultDtoSchema,
|
|
269
270
|
wordpressStatusDtoSchema
|
|
270
|
-
} from "./chunk-
|
|
271
|
+
} from "./chunk-MV3ZJOI7.js";
|
|
271
272
|
|
|
272
273
|
// src/intelligence-service.ts
|
|
273
274
|
import { eq as eq38, desc as desc18, asc as asc6, and as and28, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
@@ -1141,6 +1142,12 @@ var discoverySessions = sqliteTable("discovery_sessions", {
|
|
|
1141
1142
|
// seed time; nullable for legacy sessions. No gate/warning math reads these.
|
|
1142
1143
|
seedFromAnswerCount: integer("seed_from_answer_count"),
|
|
1143
1144
|
seedFromGroundingCount: integer("seed_from_grounding_count"),
|
|
1145
|
+
// Diagnostics: raw candidates dropped by the branded self-query filter
|
|
1146
|
+
// before seed_count_raw was recorded. Nullable for legacy sessions.
|
|
1147
|
+
seedBrandFilteredCount: integer("seed_brand_filtered_count"),
|
|
1148
|
+
// Buyer definition the session was seeded with; part of session identity
|
|
1149
|
+
// for in-flight consolidation (same ICP + different buyer never reuses).
|
|
1150
|
+
buyerDescription: text("buyer_description"),
|
|
1144
1151
|
dedupThreshold: real("dedup_threshold"),
|
|
1145
1152
|
probeCount: integer("probe_count"),
|
|
1146
1153
|
citedCount: integer("cited_count"),
|
|
@@ -3390,6 +3397,26 @@ var MIGRATION_VERSIONS = [
|
|
|
3390
3397
|
`ALTER TABLE discovery_sessions ADD COLUMN seed_from_answer_count INTEGER`,
|
|
3391
3398
|
`ALTER TABLE discovery_sessions ADD COLUMN seed_from_grounding_count INTEGER`
|
|
3392
3399
|
]
|
|
3400
|
+
},
|
|
3401
|
+
{
|
|
3402
|
+
version: 89,
|
|
3403
|
+
name: "discovery-session-brand-filter-count",
|
|
3404
|
+
statements: [
|
|
3405
|
+
// Diagnostics: raw candidates dropped by the branded self-query filter
|
|
3406
|
+
// before seed_count_raw was recorded. Nullable — legacy sessions stay
|
|
3407
|
+
// null. Idempotent (the runner swallows the duplicate-column error).
|
|
3408
|
+
`ALTER TABLE discovery_sessions ADD COLUMN seed_brand_filtered_count INTEGER`
|
|
3409
|
+
]
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
version: 90,
|
|
3413
|
+
name: "discovery-session-buyer-description",
|
|
3414
|
+
statements: [
|
|
3415
|
+
// Buyer definition the session was seeded with. Part of the in-flight
|
|
3416
|
+
// consolidation identity (a request with a different buyer must never
|
|
3417
|
+
// reuse another buyer's session) and auditability for seed provenance.
|
|
3418
|
+
`ALTER TABLE discovery_sessions ADD COLUMN buyer_description TEXT`
|
|
3419
|
+
]
|
|
3393
3420
|
}
|
|
3394
3421
|
];
|
|
3395
3422
|
function rebuildBacklinkTableWithSource(tx, table) {
|
|
@@ -18056,6 +18083,7 @@ var routeCatalog = [
|
|
|
18056
18083
|
type: "object",
|
|
18057
18084
|
properties: {
|
|
18058
18085
|
icpDescription: { type: "string", description: "Free-text ICP. Required if the project does not have spec.icpDescription stored." },
|
|
18086
|
+
buyerDescription: { type: "string", description: "Who evaluates or buys the offering, separate from the ICP. When present, every generated seed query is anchored on this buyer and the seed prompt enforces buyer-fit." },
|
|
18059
18087
|
dedupThreshold: { type: "number", description: "Cosine similarity threshold for clustering. Defaults to 0.95." },
|
|
18060
18088
|
maxProbes: { type: "integer", description: "Max canonical queries to probe in this session. Default 100, hard cap 500." },
|
|
18061
18089
|
probeConcurrency: {
|
|
@@ -33729,7 +33757,7 @@ async function doctorRoutes(app, opts) {
|
|
|
33729
33757
|
|
|
33730
33758
|
// ../api-routes/src/discovery/routes.ts
|
|
33731
33759
|
import crypto27 from "crypto";
|
|
33732
|
-
import { and as and26, desc as desc16, eq as eq35, gte as gte6, inArray as inArray12 } from "drizzle-orm";
|
|
33760
|
+
import { and as and26, desc as desc16, eq as eq35, gte as gte6, inArray as inArray12, isNull } from "drizzle-orm";
|
|
33733
33761
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
33734
33762
|
async function discoveryRoutes(app, opts) {
|
|
33735
33763
|
app.post("/projects/:name/discover/run", async (request, reply) => {
|
|
@@ -33764,6 +33792,10 @@ async function discoveryRoutes(app, opts) {
|
|
|
33764
33792
|
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and26(
|
|
33765
33793
|
eq35(discoverySessions.projectId, project.id),
|
|
33766
33794
|
eq35(discoverySessions.icpDescription, icpDescription),
|
|
33795
|
+
// Buyer is part of session identity: it changes the seed prompt's
|
|
33796
|
+
// semantics, so a request with a different (or no) buyer must start
|
|
33797
|
+
// its own session, never adopt another buyer's probes.
|
|
33798
|
+
parsed.data.buyerDescription == null ? isNull(discoverySessions.buyerDescription) : eq35(discoverySessions.buyerDescription, parsed.data.buyerDescription),
|
|
33767
33799
|
inArray12(discoverySessions.status, [
|
|
33768
33800
|
DiscoverySessionStatuses.queued,
|
|
33769
33801
|
DiscoverySessionStatuses.seeding,
|
|
@@ -33782,6 +33814,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
33782
33814
|
runId,
|
|
33783
33815
|
status: DiscoverySessionStatuses.queued,
|
|
33784
33816
|
icpDescription,
|
|
33817
|
+
buyerDescription: parsed.data.buyerDescription ?? null,
|
|
33785
33818
|
dedupThreshold: parsed.data.dedupThreshold,
|
|
33786
33819
|
competitorMap: [],
|
|
33787
33820
|
createdAt: now
|
|
@@ -33816,6 +33849,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
33816
33849
|
sessionId: decision.sessionId,
|
|
33817
33850
|
projectId: project.id,
|
|
33818
33851
|
icpDescription,
|
|
33852
|
+
buyerDescription: parsed.data.buyerDescription,
|
|
33819
33853
|
dedupThreshold: parsed.data.dedupThreshold,
|
|
33820
33854
|
maxProbes: parsed.data.maxProbes,
|
|
33821
33855
|
probeConcurrency: parsed.data.probeConcurrency,
|
|
@@ -34070,6 +34104,8 @@ function serializeSession(row) {
|
|
|
34070
34104
|
seedCount: row.seedCount ?? null,
|
|
34071
34105
|
seedFromAnswerCount: row.seedFromAnswerCount ?? null,
|
|
34072
34106
|
seedFromGroundingCount: row.seedFromGroundingCount ?? null,
|
|
34107
|
+
seedBrandFilteredCount: row.seedBrandFilteredCount ?? null,
|
|
34108
|
+
buyerDescription: row.buyerDescription ?? null,
|
|
34073
34109
|
dedupThreshold: row.dedupThreshold ?? null,
|
|
34074
34110
|
probeCount: row.probeCount ?? null,
|
|
34075
34111
|
citedCount: row.citedCount ?? null,
|
|
@@ -34176,9 +34212,15 @@ async function executeDiscovery(opts) {
|
|
|
34176
34212
|
const seedResult = await opts.deps.seed({
|
|
34177
34213
|
project: opts.project,
|
|
34178
34214
|
icpDescription: opts.icpDescription,
|
|
34215
|
+
buyerDescription: opts.buyerDescription,
|
|
34179
34216
|
locations: opts.locations ?? []
|
|
34180
34217
|
});
|
|
34181
|
-
const
|
|
34218
|
+
const { kept: unbrandedCandidates, droppedBranded } = filterBrandedSeedCandidates({
|
|
34219
|
+
candidates: seedResult.candidates,
|
|
34220
|
+
brandNames: opts.project.brandNames ?? [],
|
|
34221
|
+
canonicalDomains: opts.project.canonicalDomains
|
|
34222
|
+
});
|
|
34223
|
+
const rawCandidates = dedupeStrings(unbrandedCandidates);
|
|
34182
34224
|
const seedCountRaw = rawCandidates.length;
|
|
34183
34225
|
const canonicals = await pickCanonicals(
|
|
34184
34226
|
rawCandidates,
|
|
@@ -34201,6 +34243,7 @@ async function executeDiscovery(opts) {
|
|
|
34201
34243
|
// the seed dep does not report the split. Not consumed by any gate.
|
|
34202
34244
|
seedFromAnswerCount: seedResult.fromAnswerCount ?? null,
|
|
34203
34245
|
seedFromGroundingCount: seedResult.fromGroundingCount ?? null,
|
|
34246
|
+
seedBrandFilteredCount: droppedBranded.length,
|
|
34204
34247
|
warning
|
|
34205
34248
|
}).where(eq36(discoverySessions.id, opts.sessionId)).run();
|
|
34206
34249
|
const probeResults = await mapWithConcurrency(
|
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setTelemetrySource,
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackEvent
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-NDXV6EYD.js";
|
|
33
33
|
import {
|
|
34
34
|
CliError,
|
|
35
35
|
EXIT_SYSTEM_ERROR,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
saveConfig,
|
|
47
47
|
saveConfigPatch,
|
|
48
48
|
usageError
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-KRP555VO.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-QLWRJEII.js";
|
|
58
58
|
import {
|
|
59
59
|
BacklinkSources,
|
|
60
60
|
CcReleaseSyncStatuses,
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
providerQuotaPolicySchema,
|
|
81
81
|
resolveProviderInput,
|
|
82
82
|
winnabilityClassSchema
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-MV3ZJOI7.js";
|
|
84
84
|
|
|
85
85
|
// src/cli.ts
|
|
86
86
|
import { pathToFileURL } from "url";
|
|
@@ -1655,6 +1655,7 @@ function getClient4() {
|
|
|
1655
1655
|
function buildRunBody(opts, icpDescription) {
|
|
1656
1656
|
const body = {};
|
|
1657
1657
|
if (icpDescription) body.icpDescription = icpDescription;
|
|
1658
|
+
if (opts.buyer !== void 0 && opts.buyer.trim() !== "") body.buyerDescription = opts.buyer;
|
|
1658
1659
|
if (opts.dedupThreshold !== void 0) body.dedupThreshold = opts.dedupThreshold;
|
|
1659
1660
|
if (opts.maxProbes !== void 0) body.maxProbes = opts.maxProbes;
|
|
1660
1661
|
if (opts.probeConcurrency !== void 0) body.probeConcurrency = opts.probeConcurrency;
|
|
@@ -2057,9 +2058,10 @@ Usage: ${usage}`,
|
|
|
2057
2058
|
var DISCOVER_CLI_COMMANDS = [
|
|
2058
2059
|
{
|
|
2059
2060
|
path: ["discover", "run"],
|
|
2060
|
-
usage: 'canonry discover run <project> [--icp "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2061
|
+
usage: 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2061
2062
|
options: {
|
|
2062
2063
|
icp: stringOption(),
|
|
2064
|
+
buyer: stringOption(),
|
|
2063
2065
|
"icp-angle": multiStringOption(),
|
|
2064
2066
|
locations: multiStringOption(),
|
|
2065
2067
|
"dedup-threshold": stringOption(),
|
|
@@ -2068,10 +2070,11 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2068
2070
|
wait: { type: "boolean", default: false }
|
|
2069
2071
|
},
|
|
2070
2072
|
run: async (input) => {
|
|
2071
|
-
const usage = 'canonry discover run <project> [--icp "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2073
|
+
const usage = 'canonry discover run <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2072
2074
|
const project = requireProject(input, "discover.run", usage);
|
|
2073
2075
|
await discoverRun(project, {
|
|
2074
2076
|
icp: getString(input.values, "icp"),
|
|
2077
|
+
buyer: getString(input.values, "buyer"),
|
|
2075
2078
|
icpAngles: getStringArray(input.values, "icp-angle"),
|
|
2076
2079
|
locations: parseLocationsOption(input.values),
|
|
2077
2080
|
dedupThreshold: parseFloatOption(input.values, "dedup-threshold", usage),
|
|
@@ -2092,9 +2095,10 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2092
2095
|
},
|
|
2093
2096
|
{
|
|
2094
2097
|
path: ["discover", "seed"],
|
|
2095
|
-
usage: 'canonry discover seed <project> [--icp "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2098
|
+
usage: 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]',
|
|
2096
2099
|
options: {
|
|
2097
2100
|
icp: stringOption(),
|
|
2101
|
+
buyer: stringOption(),
|
|
2098
2102
|
"icp-angle": multiStringOption(),
|
|
2099
2103
|
locations: multiStringOption(),
|
|
2100
2104
|
"dedup-threshold": stringOption(),
|
|
@@ -2103,10 +2107,11 @@ var DISCOVER_CLI_COMMANDS = [
|
|
|
2103
2107
|
wait: { type: "boolean", default: false }
|
|
2104
2108
|
},
|
|
2105
2109
|
run: async (input) => {
|
|
2106
|
-
const usage = 'canonry discover seed <project> [--icp "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2110
|
+
const usage = 'canonry discover seed <project> [--icp "..."] [--buyer "..."] [--icp-angle "..."] [--locations michigan,florida] [--dedup-threshold 0.95] [--max-probes 100] [--probe-concurrency 3] [--wait] [--format json]';
|
|
2107
2111
|
const project = requireProject(input, "discover.seed", usage);
|
|
2108
2112
|
await discoverSeed(project, {
|
|
2109
2113
|
icp: getString(input.values, "icp"),
|
|
2114
|
+
buyer: getString(input.values, "buyer"),
|
|
2110
2115
|
icpAngles: getStringArray(input.values, "icp-angle"),
|
|
2111
2116
|
locations: parseLocationsOption(input.values),
|
|
2112
2117
|
dedupThreshold: parseFloatOption(input.values, "dedup-threshold", usage),
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NDXV6EYD.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-KRP555VO.js";
|
|
7
|
+
import "./chunk-QLWRJEII.js";
|
|
8
|
+
import "./chunk-MV3ZJOI7.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
|
11
11
|
loadConfig
|
package/dist/mcp.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
PACKAGE_VERSION,
|
|
4
4
|
canonryMcpTools,
|
|
5
5
|
createApiClient
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KRP555VO.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MV3ZJOI7.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp/cli.ts
|
|
12
12
|
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.
|
|
3
|
+
"version": "4.107.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",
|
|
@@ -66,25 +66,25 @@
|
|
|
66
66
|
"tsup": "^8.5.1",
|
|
67
67
|
"tsx": "^4.19.0",
|
|
68
68
|
"@ainyc/canonry-api-client": "0.0.0",
|
|
69
|
-
"@ainyc/canonry-api-routes": "0.0.0",
|
|
70
|
-
"@ainyc/canonry-db": "0.0.0",
|
|
71
69
|
"@ainyc/canonry-config": "0.0.0",
|
|
70
|
+
"@ainyc/canonry-api-routes": "0.0.0",
|
|
72
71
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
72
|
+
"@ainyc/canonry-db": "0.0.0",
|
|
73
73
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
74
74
|
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
75
75
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
76
76
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
77
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
77
78
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
79
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
78
80
|
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
79
|
-
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
80
81
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
81
82
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
83
|
+
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
82
84
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
83
|
-
"@ainyc/canonry-
|
|
85
|
+
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
84
86
|
"@ainyc/canonry-provider-local": "0.0.0",
|
|
85
|
-
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
86
87
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
87
|
-
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
88
88
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
89
89
|
},
|
|
90
90
|
"scripts": {
|