@ainyc/canonry 3.3.9 → 3.4.6
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/{index-C1kUp1aS.js → index-C6qeFqvR.js} +87 -87
- package/assets/index.html +1 -1
- package/dist/{chunk-ZCPZOVVE.js → chunk-K33FVWFW.js} +167 -6
- package/dist/{chunk-P6D3O5JB.js → chunk-UJRPODX3.js} +574 -492
- package/dist/chunk-VIUWGDDU.js +1919 -0
- package/dist/{chunk-24C7RMIS.js → chunk-ZYESHCMF.js} +76 -1967
- package/dist/cli.js +93 -33
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-FNJTFSI3.js → intelligence-service-NGA6RLCH.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
- package/dist/chunk-MLKGABMK.js +0 -9
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-C6qeFqvR.js"></script>
|
|
16
16
|
<link rel="stylesheet" crossorigin href="./assets/index-JG7aBJrz.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ContentActions,
|
|
3
|
+
RunKinds,
|
|
2
4
|
__export
|
|
3
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-VIUWGDDU.js";
|
|
4
6
|
|
|
5
7
|
// src/intelligence-service.ts
|
|
6
|
-
import { eq, desc, asc, and, or } from "drizzle-orm";
|
|
8
|
+
import { eq, desc, asc, and, or, inArray } from "drizzle-orm";
|
|
7
9
|
|
|
8
10
|
// ../db/src/client.ts
|
|
9
11
|
import { mkdirSync } from "fs";
|
|
@@ -1946,6 +1948,103 @@ function clamp012(value) {
|
|
|
1946
1948
|
return value;
|
|
1947
1949
|
}
|
|
1948
1950
|
|
|
1951
|
+
// ../intelligence/src/next-steps.ts
|
|
1952
|
+
var TOP_N = 5;
|
|
1953
|
+
var IMMEDIATE_HORIZON = 3;
|
|
1954
|
+
var ACTION_TITLE = {
|
|
1955
|
+
[ContentActions.create]: (q) => `Create a page targeting "${q}"`,
|
|
1956
|
+
[ContentActions.refresh]: (q) => `Refresh the page targeting "${q}"`,
|
|
1957
|
+
[ContentActions.expand]: (q) => `Expand coverage of "${q}"`,
|
|
1958
|
+
[ContentActions["add-schema"]]: (q) => `Add structured data to the page targeting "${q}"`
|
|
1959
|
+
};
|
|
1960
|
+
function mapOpportunitiesToNextSteps(opportunities, existing) {
|
|
1961
|
+
if (existing.length > 0) return existing;
|
|
1962
|
+
if (opportunities.length === 0) return [];
|
|
1963
|
+
return opportunities.slice(0, TOP_N).map((opp, idx) => ({
|
|
1964
|
+
horizon: idx < IMMEDIATE_HORIZON ? "immediate" : "short-term",
|
|
1965
|
+
title: ACTION_TITLE[opp.action](opp.query),
|
|
1966
|
+
rationale: `Score ${Math.round(opp.score)} \xB7 demand ${opp.demandSource} \xB7 ${opp.actionConfidence} confidence.`
|
|
1967
|
+
}));
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// ../intelligence/src/insight-severity.ts
|
|
1971
|
+
var SEVERITY_THRESHOLDS = {
|
|
1972
|
+
highTrafficImpressions: 100,
|
|
1973
|
+
recurrenceCount: 2,
|
|
1974
|
+
mediumTrafficImpressions: 10
|
|
1975
|
+
};
|
|
1976
|
+
function classifyRegressionSeverity(signals) {
|
|
1977
|
+
const { gscImpressions, recurrenceCount } = signals;
|
|
1978
|
+
if (gscImpressions === void 0 && recurrenceCount === void 0) return "high";
|
|
1979
|
+
const isHighTraffic = gscImpressions !== void 0 && gscImpressions >= SEVERITY_THRESHOLDS.highTrafficImpressions;
|
|
1980
|
+
const isRecurring = recurrenceCount !== void 0 && recurrenceCount >= SEVERITY_THRESHOLDS.recurrenceCount;
|
|
1981
|
+
const isModerateTraffic = gscImpressions !== void 0 && gscImpressions >= SEVERITY_THRESHOLDS.mediumTrafficImpressions;
|
|
1982
|
+
if (isHighTraffic && isRecurring) return "critical";
|
|
1983
|
+
if (isHighTraffic || isRecurring) return "high";
|
|
1984
|
+
if (isModerateTraffic) return "medium";
|
|
1985
|
+
return "low";
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// ../intelligence/src/insight-grouping.ts
|
|
1989
|
+
function groupInsights(insights2, keyFn = (i) => `${i.keyword} ${i.provider} ${i.type}`) {
|
|
1990
|
+
const order = [];
|
|
1991
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1992
|
+
for (const i of insights2) {
|
|
1993
|
+
const key = keyFn(i);
|
|
1994
|
+
const bucket = buckets.get(key);
|
|
1995
|
+
if (bucket) {
|
|
1996
|
+
bucket.push(i);
|
|
1997
|
+
} else {
|
|
1998
|
+
buckets.set(key, [i]);
|
|
1999
|
+
order.push(key);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return order.map((key) => {
|
|
2003
|
+
const sorted = [...buckets.get(key)].sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
2004
|
+
const representative = sorted[sorted.length - 1];
|
|
2005
|
+
return {
|
|
2006
|
+
representative,
|
|
2007
|
+
count: sorted.length,
|
|
2008
|
+
instances: sorted,
|
|
2009
|
+
latest: representative.createdAt
|
|
2010
|
+
};
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
// ../intelligence/src/query-categorize.ts
|
|
2015
|
+
var TRANSACTIONAL_RE = /\b(buy|price|pricing|cost|hire|near me|services?|agency|consultant|company)\b/i;
|
|
2016
|
+
var INFORMATIONAL_RE = /\b(what|how|why|when|guide|tutorial|vs|versus|alternatives?|examples?|definition)\b/i;
|
|
2017
|
+
var MIN_BRAND_TOKEN_LENGTH = 3;
|
|
2018
|
+
function compact(value) {
|
|
2019
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2020
|
+
}
|
|
2021
|
+
function buildBrandTokens(canonicalDomain, displayName) {
|
|
2022
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2023
|
+
const stem = canonicalDomain.toLowerCase().replace(/\.[a-z]{2,}$/, "");
|
|
2024
|
+
const stemCompact = compact(stem);
|
|
2025
|
+
if (stemCompact.length >= MIN_BRAND_TOKEN_LENGTH) seen.add(stemCompact);
|
|
2026
|
+
if (displayName) {
|
|
2027
|
+
const displayCompact = compact(displayName);
|
|
2028
|
+
if (displayCompact.length >= MIN_BRAND_TOKEN_LENGTH) seen.add(displayCompact);
|
|
2029
|
+
}
|
|
2030
|
+
return [...seen];
|
|
2031
|
+
}
|
|
2032
|
+
function categorizeQueryByIntent(query, brandTokens) {
|
|
2033
|
+
const compactQuery = compact(query);
|
|
2034
|
+
if (brandTokens.length > 0 && brandTokens.some((t) => compactQuery.includes(t))) {
|
|
2035
|
+
return "brand";
|
|
2036
|
+
}
|
|
2037
|
+
if (TRANSACTIONAL_RE.test(query)) return "lead-gen";
|
|
2038
|
+
if (INFORMATIONAL_RE.test(query)) return "industry";
|
|
2039
|
+
return "other";
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// ../intelligence/src/trend-stability.ts
|
|
2043
|
+
var MIN_TREND_POINTS = 4;
|
|
2044
|
+
function isTrendBaseline(points) {
|
|
2045
|
+
return points.length < MIN_TREND_POINTS;
|
|
2046
|
+
}
|
|
2047
|
+
|
|
1949
2048
|
// src/intelligence-service.ts
|
|
1950
2049
|
import crypto from "crypto";
|
|
1951
2050
|
|
|
@@ -1987,6 +2086,7 @@ function createLogger(module) {
|
|
|
1987
2086
|
}
|
|
1988
2087
|
|
|
1989
2088
|
// src/intelligence-service.ts
|
|
2089
|
+
var RECURRENCE_LOOKBACK_RUNS = 5;
|
|
1990
2090
|
var log = createLogger("IntelligenceService");
|
|
1991
2091
|
var IntelligenceService = class {
|
|
1992
2092
|
constructor(db) {
|
|
@@ -2040,8 +2140,9 @@ var IntelligenceService = class {
|
|
|
2040
2140
|
citedRate: result.health.overallCitedRate,
|
|
2041
2141
|
insights: result.insights.length
|
|
2042
2142
|
});
|
|
2043
|
-
this.
|
|
2044
|
-
|
|
2143
|
+
const tieredResult = this.tierResult(result, runId, projectId);
|
|
2144
|
+
this.persistResult(tieredResult, runId, projectId);
|
|
2145
|
+
return tieredResult;
|
|
2045
2146
|
}
|
|
2046
2147
|
/**
|
|
2047
2148
|
* Analyze a single run given an explicit previous run (or null for first run).
|
|
@@ -2059,8 +2160,9 @@ var IntelligenceService = class {
|
|
|
2059
2160
|
return result2;
|
|
2060
2161
|
}
|
|
2061
2162
|
const result = analyzeRuns(currentRun, previousRun);
|
|
2062
|
-
this.
|
|
2063
|
-
|
|
2163
|
+
const tieredResult = this.tierResult(result, runRecord.id, runRecord.projectId);
|
|
2164
|
+
this.persistResult(tieredResult, runRecord.id, runRecord.projectId);
|
|
2165
|
+
return tieredResult;
|
|
2064
2166
|
}
|
|
2065
2167
|
/**
|
|
2066
2168
|
* Backfill intelligence for all completed/partial runs of a project.
|
|
@@ -2151,6 +2253,59 @@ var IntelligenceService = class {
|
|
|
2151
2253
|
});
|
|
2152
2254
|
log.info("intelligence.persisted", { runId, insights: result.insights.length });
|
|
2153
2255
|
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Apply severity tiering to the insights of an AnalysisResult and return a
|
|
2258
|
+
* new result. Wraps `applySeverityTiering` so callers (analyzeAndPersist,
|
|
2259
|
+
* analyzeRunWithPrevious) can pass the same tiered shape both into the DB
|
|
2260
|
+
* write and back to the RunCoordinator / webhook dispatcher.
|
|
2261
|
+
*/
|
|
2262
|
+
tierResult(result, runId, projectId) {
|
|
2263
|
+
if (result.insights.length === 0) return result;
|
|
2264
|
+
return { ...result, insights: this.applySeverityTiering(result.insights, runId, projectId) };
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Re-classify each regression insight's severity using GSC traffic +
|
|
2268
|
+
* recurrence signals via the pure `classifyRegressionSeverity` primitive
|
|
2269
|
+
* in @ainyc/canonry-intelligence. Non-regression insights are returned
|
|
2270
|
+
* untouched.
|
|
2271
|
+
*/
|
|
2272
|
+
applySeverityTiering(rawInsights, excludeRunId, projectId) {
|
|
2273
|
+
const regressions = rawInsights.filter((i) => i.type === "regression");
|
|
2274
|
+
if (regressions.length === 0) return rawInsights;
|
|
2275
|
+
const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq(gscSearchData.projectId, projectId)).all();
|
|
2276
|
+
const gscConnected = gscRows.length > 0;
|
|
2277
|
+
const gscImpressionsByKeyword = /* @__PURE__ */ new Map();
|
|
2278
|
+
for (const row of gscRows) {
|
|
2279
|
+
const key = row.query.toLowerCase();
|
|
2280
|
+
gscImpressionsByKeyword.set(key, (gscImpressionsByKeyword.get(key) ?? 0) + row.impressions);
|
|
2281
|
+
}
|
|
2282
|
+
const recentRunIds = this.db.select({ id: runs.id }).from(runs).where(
|
|
2283
|
+
and(
|
|
2284
|
+
eq(runs.projectId, projectId),
|
|
2285
|
+
eq(runs.kind, RunKinds["answer-visibility"]),
|
|
2286
|
+
or(eq(runs.status, "completed"), eq(runs.status, "partial"))
|
|
2287
|
+
)
|
|
2288
|
+
).orderBy(desc(runs.createdAt)).limit(RECURRENCE_LOOKBACK_RUNS + 1).all().map((r) => r.id).filter((id) => id !== excludeRunId).slice(0, RECURRENCE_LOOKBACK_RUNS);
|
|
2289
|
+
const haveHistory = recentRunIds.length > 0;
|
|
2290
|
+
const priorRegressionsByPair = /* @__PURE__ */ new Map();
|
|
2291
|
+
if (haveHistory) {
|
|
2292
|
+
const priorRows = this.db.select({ keyword: insights.keyword, provider: insights.provider }).from(insights).where(and(eq(insights.type, "regression"), inArray(insights.runId, recentRunIds))).all();
|
|
2293
|
+
for (const row of priorRows) {
|
|
2294
|
+
const key = `${row.keyword}:${row.provider}`;
|
|
2295
|
+
priorRegressionsByPair.set(key, (priorRegressionsByPair.get(key) ?? 0) + 1);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
return rawInsights.map((insight) => {
|
|
2299
|
+
if (insight.type !== "regression") return insight;
|
|
2300
|
+
const gscImpressions = gscConnected ? gscImpressionsByKeyword.get(insight.keyword.toLowerCase()) ?? 0 : void 0;
|
|
2301
|
+
const recurrenceCount = haveHistory ? priorRegressionsByPair.get(`${insight.keyword}:${insight.provider}`) ?? 0 : void 0;
|
|
2302
|
+
const severity = classifyRegressionSeverity({
|
|
2303
|
+
gscImpressions,
|
|
2304
|
+
recurrenceCount
|
|
2305
|
+
});
|
|
2306
|
+
return { ...insight, severity };
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2154
2309
|
buildRunData(runId, projectId, completedAt) {
|
|
2155
2310
|
const rows = this.db.select({
|
|
2156
2311
|
keyword: keywords.keyword,
|
|
@@ -2211,6 +2366,12 @@ export {
|
|
|
2211
2366
|
buildContentTargetRows,
|
|
2212
2367
|
buildContentSourceRows,
|
|
2213
2368
|
buildContentGapRows,
|
|
2369
|
+
mapOpportunitiesToNextSteps,
|
|
2370
|
+
groupInsights,
|
|
2371
|
+
buildBrandTokens,
|
|
2372
|
+
categorizeQueryByIntent,
|
|
2373
|
+
MIN_TREND_POINTS,
|
|
2374
|
+
isTrendBaseline,
|
|
2214
2375
|
createLogger,
|
|
2215
2376
|
IntelligenceService
|
|
2216
2377
|
};
|