@canonry/canonry 4.139.0 → 4.141.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/agent-workspace/skills/aero/references/reporting.md +22 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +21 -0
- package/assets/agent-workspace/skills/canonry/references/indexing.md +2 -1
- package/assets/assets/{AuditHistoryPanel-DL9chF3F.js → AuditHistoryPanel-DlyLhzqq.js} +1 -1
- package/assets/assets/{BacklinksPage-sA_tgQJv.js → BacklinksPage-DNxtwQBF.js} +1 -1
- package/assets/assets/{ChartPrimitives-D7nLxIbe.js → ChartPrimitives-BStLR_NC.js} +1 -1
- package/assets/assets/{HistoryPage-CLWbcu5m.js → HistoryPage-CIcCgoWK.js} +1 -1
- package/assets/assets/{ProjectPage-C_oXMCNU.js → ProjectPage-BSI3Qum9.js} +8 -8
- package/assets/assets/{RunRow-DQ4icEiv.js → RunRow-C0mFtLlc.js} +1 -1
- package/assets/assets/{RunsPage-D5sizm9T.js → RunsPage-C-yo0yoj.js} +1 -1
- package/assets/assets/{SettingsPage-XdQjvWsg.js → SettingsPage-jg4MXOuo.js} +1 -1
- package/assets/assets/{TrafficPage-C12osXDa.js → TrafficPage-CnYHIg8t.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CXAiNK8Y.js → TrafficSourceDetailPage-CEdyQT2P.js} +1 -1
- package/assets/assets/{arrow-left-LaI5a16g.js → arrow-left-DUv0imIl.js} +1 -1
- package/assets/assets/{extract-error-message-CYWaTM3m.js → extract-error-message-CCOGDwlm.js} +1 -1
- package/assets/assets/index-BDo9nl4O.css +1 -0
- package/assets/assets/index-D6EP4dQs.js +213 -0
- package/assets/assets/{trash-2-DoH00q7m.js → trash-2-1Yg9W-vs.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-IGU67QFM.js → chunk-2LUSZFB6.js} +122 -19
- package/dist/{chunk-DLKUDNDA.js → chunk-HXXO2AF5.js} +51 -4
- package/dist/{chunk-OA2EGCUS.js → chunk-OJOA425V.js} +42 -1
- package/dist/{chunk-IYTHIAAE.js → chunk-ZCFQGF3V.js} +46 -4
- package/dist/cli.js +145 -17
- package/dist/index.d.ts +12 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-SMYVN3KN.js → intelligence-service-4HQIUC7U.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
- package/assets/assets/index-CguwySA5.js +0 -213
- package/assets/assets/index-CntuM6Th.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as a}from"./index-
|
|
1
|
+
import{c as a}from"./index-D6EP4dQs.js";const t=[["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"}]],h=a("circle-question-mark",t);const e=[["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"}]],d=a("download",e);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],p=a("external-link",o);const c=[["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"}]],s=a("trash-2",c);export{h as C,d as D,p as E,s as T};
|
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-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-D6EP4dQs.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-DSxTZhaH.js">
|
|
19
19
|
<link rel="modulepreload" crossorigin href="./assets/vendor-markdown-6dwjPOKK.js">
|
|
20
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
20
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BDo9nl4O.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
|
@@ -240,11 +240,13 @@ import {
|
|
|
240
240
|
gscDeindexedRowSchema,
|
|
241
241
|
gscDiscoverSitemapsResponseDtoSchema,
|
|
242
242
|
gscPerformanceDailyDtoSchema,
|
|
243
|
-
|
|
243
|
+
gscPerformanceOrderBySchema,
|
|
244
|
+
gscPerformanceResponseDtoSchema,
|
|
244
245
|
gscSiteListResponseDtoSchema,
|
|
245
246
|
gscSitemapListResponseDtoSchema,
|
|
246
247
|
gscSubmitSitemapsRequestDtoSchema,
|
|
247
248
|
gscSubmitSitemapsResponseDtoSchema,
|
|
249
|
+
gscTopPagesDtoSchema,
|
|
248
250
|
gscUrlInspectionDtoSchema,
|
|
249
251
|
hasLocationLabel,
|
|
250
252
|
healthSnapshotDtoSchema,
|
|
@@ -378,7 +380,7 @@ import {
|
|
|
378
380
|
wordpressSchemaDeployResultDtoSchema,
|
|
379
381
|
wordpressSchemaStatusResultDtoSchema,
|
|
380
382
|
wordpressStatusDtoSchema
|
|
381
|
-
} from "./chunk-
|
|
383
|
+
} from "./chunk-OJOA425V.js";
|
|
382
384
|
|
|
383
385
|
// src/intelligence-service.ts
|
|
384
386
|
import { eq as eq45, desc as desc22, asc as asc9, and as and36, ne as ne6, or as or10, inArray as inArray15, gte as gte12, lte as lte8 } from "drizzle-orm";
|
|
@@ -17766,7 +17768,8 @@ var SCHEMA_TABLE = {
|
|
|
17766
17768
|
GscCoverageSummaryDto: gscCoverageSummaryDtoSchema,
|
|
17767
17769
|
GscDeindexedRowDto: gscDeindexedRowSchema,
|
|
17768
17770
|
GscPerformanceDailyDto: gscPerformanceDailyDtoSchema,
|
|
17769
|
-
|
|
17771
|
+
GscPerformanceResponseDto: gscPerformanceResponseDtoSchema,
|
|
17772
|
+
GscTopPagesDto: gscTopPagesDtoSchema,
|
|
17770
17773
|
GscDiscoverSitemapsResponseDto: gscDiscoverSitemapsResponseDtoSchema,
|
|
17771
17774
|
GscSiteListResponseDto: gscSiteListResponseDtoSchema,
|
|
17772
17775
|
GscSubmitSitemapsResponseDto: gscSubmitSitemapsResponseDtoSchema,
|
|
@@ -19717,15 +19720,22 @@ var routeCatalog = [
|
|
|
19717
19720
|
{ name: "endDate", in: "query", description: "Filter by end date.", schema: stringSchema },
|
|
19718
19721
|
{ name: "query", in: "query", description: "Filter by search query.", schema: stringSchema },
|
|
19719
19722
|
{ name: "page", in: "query", description: "Filter by page URL.", schema: stringSchema },
|
|
19723
|
+
{
|
|
19724
|
+
name: "orderBy",
|
|
19725
|
+
in: "query",
|
|
19726
|
+
description: "Row ordering, always descending. Defaults to clicks. Use date for time-series reads.",
|
|
19727
|
+
schema: { type: "string", enum: ["clicks", "impressions", "date"] }
|
|
19728
|
+
},
|
|
19720
19729
|
limitQueryParameter,
|
|
19721
19730
|
offsetQueryParameter,
|
|
19722
19731
|
analyticsWindowParameter
|
|
19723
19732
|
],
|
|
19724
19733
|
responses: {
|
|
19725
|
-
//
|
|
19726
|
-
//
|
|
19727
|
-
//
|
|
19728
|
-
200:
|
|
19734
|
+
// Envelope, not a bare array: `totalMatching` / `truncated` are how a
|
|
19735
|
+
// caller tells a page from a complete answer, and `latestAvailableDate`
|
|
19736
|
+
// is how it tells "no data" from "asked past the GSC reporting lag".
|
|
19737
|
+
200: jsonResponse("GSC performance page plus match count and data freshness.", "GscPerformanceResponseDto"),
|
|
19738
|
+
400: errorResponse("Invalid orderBy value."),
|
|
19729
19739
|
404: errorResponse("Project not found.")
|
|
19730
19740
|
}
|
|
19731
19741
|
},
|
|
@@ -19745,6 +19755,24 @@ var routeCatalog = [
|
|
|
19745
19755
|
404: errorResponse("Project not found.")
|
|
19746
19756
|
}
|
|
19747
19757
|
},
|
|
19758
|
+
{
|
|
19759
|
+
method: "get",
|
|
19760
|
+
path: "/api/v1/projects/{name}/google/gsc/top-pages",
|
|
19761
|
+
summary: "Get top GSC pages ranked by clicks",
|
|
19762
|
+
description: "One row per page, aggregated in SQL and ranked by summed clicks descending. The rows are a RANKING built from the dimensioned search-data table; `totals` is NOT their sum. Google withholds rare queries (summed clicks under-count) and fans one impression across every query/page/country/device combination (summed impressions over-count), so `totals` is read from the un-dimensioned property-level daily table and labelled `totalsSource: property-daily`. It is null when that table has no rows in the window.",
|
|
19763
|
+
tags: ["google"],
|
|
19764
|
+
parameters: [
|
|
19765
|
+
nameParameter,
|
|
19766
|
+
{ name: "startDate", in: "query", description: "Filter by start date.", schema: stringSchema },
|
|
19767
|
+
{ name: "endDate", in: "query", description: "Filter by end date.", schema: stringSchema },
|
|
19768
|
+
limitQueryParameter,
|
|
19769
|
+
analyticsWindowParameter
|
|
19770
|
+
],
|
|
19771
|
+
responses: {
|
|
19772
|
+
200: jsonResponse("Ranked pages plus the property-level window total.", "GscTopPagesDto"),
|
|
19773
|
+
404: errorResponse("Project not found.")
|
|
19774
|
+
}
|
|
19775
|
+
},
|
|
19748
19776
|
{
|
|
19749
19777
|
method: "post",
|
|
19750
19778
|
path: "/api/v1/projects/{name}/google/gsc/inspect",
|
|
@@ -25773,6 +25801,13 @@ async function googleRoutes(app, opts) {
|
|
|
25773
25801
|
app.get("/projects/:name/google/gsc/performance", async (request) => {
|
|
25774
25802
|
const project = resolveProject(app.db, request.params.name);
|
|
25775
25803
|
const { startDate, endDate, query, page, limit, offset } = request.query;
|
|
25804
|
+
const parsedOrderBy = gscPerformanceOrderBySchema.safeParse(request.query.orderBy ?? "clicks");
|
|
25805
|
+
if (!parsedOrderBy.success) {
|
|
25806
|
+
throw validationError(
|
|
25807
|
+
`orderBy must be one of: ${gscPerformanceOrderBySchema.options.join(", ")}`
|
|
25808
|
+
);
|
|
25809
|
+
}
|
|
25810
|
+
const orderBy = parsedOrderBy.data;
|
|
25776
25811
|
const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
|
|
25777
25812
|
const conditions = [eq27(gscSearchData.projectId, project.id)];
|
|
25778
25813
|
if (startDate) conditions.push(sql12`${gscSearchData.date} >= ${startDate}`);
|
|
@@ -25782,18 +25817,34 @@ async function googleRoutes(app, opts) {
|
|
|
25782
25817
|
if (page) conditions.push(sql12`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
|
|
25783
25818
|
const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
|
|
25784
25819
|
const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
|
|
25785
|
-
const
|
|
25786
|
-
|
|
25787
|
-
|
|
25788
|
-
|
|
25789
|
-
|
|
25790
|
-
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
|
|
25820
|
+
const orderColumns = {
|
|
25821
|
+
clicks: [desc13(gscSearchData.clicks), desc13(gscSearchData.date), gscSearchData.query],
|
|
25822
|
+
impressions: [desc13(gscSearchData.impressions), desc13(gscSearchData.date), gscSearchData.query],
|
|
25823
|
+
date: [desc13(gscSearchData.date), desc13(gscSearchData.clicks), gscSearchData.query]
|
|
25824
|
+
}[orderBy];
|
|
25825
|
+
const rows = app.db.select().from(gscSearchData).where(and21(...conditions)).orderBy(...orderColumns).limit(limitVal).offset(offsetVal).all();
|
|
25826
|
+
const totalMatching = app.db.select({ total: sql12`COUNT(*)` }).from(gscSearchData).where(and21(...conditions)).get()?.total ?? 0;
|
|
25827
|
+
const latestAvailableDate = app.db.select({ latest: sql12`MAX(${gscSearchData.date})` }).from(gscSearchData).where(eq27(gscSearchData.projectId, project.id)).get()?.latest ?? null;
|
|
25828
|
+
return {
|
|
25829
|
+
rows: rows.map((r) => ({
|
|
25830
|
+
date: r.date,
|
|
25831
|
+
query: r.query,
|
|
25832
|
+
page: r.page,
|
|
25833
|
+
country: r.country,
|
|
25834
|
+
device: r.device,
|
|
25835
|
+
clicks: r.clicks,
|
|
25836
|
+
impressions: r.impressions,
|
|
25837
|
+
ctr: parseFloat(r.ctr),
|
|
25838
|
+
position: parseFloat(r.position)
|
|
25839
|
+
})),
|
|
25840
|
+
totalMatching,
|
|
25841
|
+
// Account for the offset: `rows.length < totalMatching` alone reports
|
|
25842
|
+
// truncated=true for a page that sits past the end, where there is
|
|
25843
|
+
// nothing further to fetch. Truncation means "more rows follow this
|
|
25844
|
+
// page", so it has to measure from where this page ends.
|
|
25845
|
+
truncated: offsetVal + rows.length < totalMatching,
|
|
25846
|
+
latestAvailableDate
|
|
25847
|
+
};
|
|
25797
25848
|
});
|
|
25798
25849
|
app.get("/projects/:name/google/gsc/performance/daily", async (request) => {
|
|
25799
25850
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -25837,6 +25888,56 @@ async function googleRoutes(app, opts) {
|
|
|
25837
25888
|
daily
|
|
25838
25889
|
};
|
|
25839
25890
|
});
|
|
25891
|
+
app.get("/projects/:name/google/gsc/top-pages", async (request) => {
|
|
25892
|
+
const project = resolveProject(app.db, request.params.name);
|
|
25893
|
+
const { startDate, endDate, limit } = request.query;
|
|
25894
|
+
const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
|
|
25895
|
+
const conditions = [eq27(gscSearchData.projectId, project.id)];
|
|
25896
|
+
if (startDate) conditions.push(sql12`${gscSearchData.date} >= ${startDate}`);
|
|
25897
|
+
else if (cutoffDate) conditions.push(sql12`${gscSearchData.date} >= ${cutoffDate}`);
|
|
25898
|
+
if (endDate) conditions.push(sql12`${gscSearchData.date} <= ${endDate}`);
|
|
25899
|
+
const limitVal = Math.max(parseInt(limit ?? "50", 10) || 0, 1);
|
|
25900
|
+
const rows = app.db.select({
|
|
25901
|
+
page: gscSearchData.page,
|
|
25902
|
+
clicks: sql12`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
|
|
25903
|
+
impressions: sql12`COALESCE(SUM(${gscSearchData.impressions}), 0)`
|
|
25904
|
+
}).from(gscSearchData).where(and21(...conditions)).groupBy(gscSearchData.page).orderBy(desc13(sql12`SUM(${gscSearchData.clicks})`), desc13(sql12`SUM(${gscSearchData.impressions})`)).limit(limitVal).all();
|
|
25905
|
+
const windowStart = startDate ?? cutoffDate ?? "";
|
|
25906
|
+
const windowEnd = endDate ?? "9999-12-31";
|
|
25907
|
+
const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
|
|
25908
|
+
const totalClicks = dailyTotals.reduce((sum, d) => sum + d.clicks, 0);
|
|
25909
|
+
const totalImpressions = dailyTotals.reduce((sum, d) => sum + d.impressions, 0);
|
|
25910
|
+
const rankedSpan = app.db.select({
|
|
25911
|
+
first: sql12`MIN(${gscSearchData.date})`,
|
|
25912
|
+
last: sql12`MAX(${gscSearchData.date})`
|
|
25913
|
+
}).from(gscSearchData).where(and21(...conditions)).get();
|
|
25914
|
+
const coveredFrom = dailyTotals.length > 0 ? dailyTotals[0].date : null;
|
|
25915
|
+
const coveredThrough = dailyTotals.length > 0 ? dailyTotals[dailyTotals.length - 1].date : null;
|
|
25916
|
+
const totalsComplete = Boolean(
|
|
25917
|
+
coveredFrom && coveredThrough && rankedSpan?.first && rankedSpan?.last && coveredFrom <= rankedSpan.first && coveredThrough >= rankedSpan.last
|
|
25918
|
+
);
|
|
25919
|
+
return {
|
|
25920
|
+
rows: rows.map((r) => ({
|
|
25921
|
+
page: r.page,
|
|
25922
|
+
clicks: r.clicks,
|
|
25923
|
+
impressions: r.impressions,
|
|
25924
|
+
ctr: r.impressions > 0 ? r.clicks / r.impressions : 0
|
|
25925
|
+
})),
|
|
25926
|
+
totals: dailyTotals.length > 0 ? {
|
|
25927
|
+
clicks: totalClicks,
|
|
25928
|
+
impressions: totalImpressions,
|
|
25929
|
+
ctr: totalImpressions > 0 ? totalClicks / totalImpressions : 0,
|
|
25930
|
+
days: dailyTotals.length,
|
|
25931
|
+
coveredFrom,
|
|
25932
|
+
coveredThrough,
|
|
25933
|
+
// False when the property-level totals span less than the rows above.
|
|
25934
|
+
complete: totalsComplete
|
|
25935
|
+
} : null,
|
|
25936
|
+
totalsSource: "property-daily",
|
|
25937
|
+
rankedFrom: rankedSpan?.first ?? null,
|
|
25938
|
+
rankedThrough: rankedSpan?.last ?? null
|
|
25939
|
+
};
|
|
25940
|
+
});
|
|
25840
25941
|
app.post("/projects/:name/google/gsc/inspect", async (request) => {
|
|
25841
25942
|
const { clientId: googleClientId, clientSecret: googleClientSecret } = getAuthConfig();
|
|
25842
25943
|
if (!googleClientId || !googleClientSecret) {
|
|
@@ -45946,6 +46047,8 @@ export {
|
|
|
45946
46047
|
extractRecommendedCompetitors,
|
|
45947
46048
|
getPlaceDetails,
|
|
45948
46049
|
hashPlaceDetails,
|
|
46050
|
+
latestQueryBasketRevision,
|
|
46051
|
+
ensureCurrentQueryBasketRevision,
|
|
45949
46052
|
queueRunIfProjectIdle,
|
|
45950
46053
|
nextRunFromCron,
|
|
45951
46054
|
resolveWebhookTarget,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZCFQGF3V.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
doctorHealthState,
|
|
54
54
|
downloadFile,
|
|
55
55
|
dropLegacyCredentialColumns,
|
|
56
|
+
ensureCurrentQueryBasketRevision,
|
|
56
57
|
executeDiscovery,
|
|
57
58
|
extractLegacyCredentials,
|
|
58
59
|
extractRecommendedCompetitors,
|
|
@@ -88,6 +89,7 @@ import {
|
|
|
88
89
|
installDuckdb,
|
|
89
90
|
isDuckdbInstalled,
|
|
90
91
|
isValidReleaseId,
|
|
92
|
+
latestQueryBasketRevision,
|
|
91
93
|
listCachedReleases,
|
|
92
94
|
listLocations,
|
|
93
95
|
listMonthlyKeywords,
|
|
@@ -121,7 +123,7 @@ import {
|
|
|
121
123
|
siteAuditSnapshots,
|
|
122
124
|
toAlertView,
|
|
123
125
|
usageCounters
|
|
124
|
-
} from "./chunk-
|
|
126
|
+
} from "./chunk-2LUSZFB6.js";
|
|
125
127
|
import {
|
|
126
128
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
127
129
|
AGENT_PROVIDER_IDS,
|
|
@@ -208,7 +210,7 @@ import {
|
|
|
208
210
|
validationError,
|
|
209
211
|
winnabilityClassLabel,
|
|
210
212
|
withRetry
|
|
211
|
-
} from "./chunk-
|
|
213
|
+
} from "./chunk-OJOA425V.js";
|
|
212
214
|
|
|
213
215
|
// src/telemetry.ts
|
|
214
216
|
import crypto from "crypto";
|
|
@@ -7176,7 +7178,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
7176
7178
|
return result;
|
|
7177
7179
|
}
|
|
7178
7180
|
async function backfillInsightsCommand(project, opts) {
|
|
7179
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
7181
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4HQIUC7U.js");
|
|
7180
7182
|
const config = loadConfig();
|
|
7181
7183
|
const db = createClient(config.database);
|
|
7182
7184
|
migrate(db);
|
|
@@ -8151,9 +8153,36 @@ var Scheduler = class {
|
|
|
8151
8153
|
}
|
|
8152
8154
|
log13.info("health-schedule.seeded", { projectCount: projectsWithoutHealth.length, cron: DEFAULT_HEALTH_CRON });
|
|
8153
8155
|
}
|
|
8156
|
+
/**
|
|
8157
|
+
* Record each project's current query set as basket revision 1 if it has
|
|
8158
|
+
* never been recorded. Minting normally happens when a sweep is queued, but a
|
|
8159
|
+
* project whose sweeps are manual (the common cadence: twice a month) would
|
|
8160
|
+
* otherwise keep its analytics on the pre-basket date heuristic for weeks
|
|
8161
|
+
* after the feature ships — with the chart still hiding exactly the history
|
|
8162
|
+
* the basket exists to recover. Boot is the moment the current set is known
|
|
8163
|
+
* and nothing has to be guessed, and ensureCurrentQueryBasketRevision is
|
|
8164
|
+
* idempotent, so restarts are no-ops and an unchanged set never churns
|
|
8165
|
+
* revisions. Runs are NOT stamped retroactively: the revision describes the
|
|
8166
|
+
* set as of now, and historical runs keep their null stamp.
|
|
8167
|
+
*/
|
|
8168
|
+
ensureQueryBaskets() {
|
|
8169
|
+
const allProjects = this.db.select({ id: projects.id }).from(projects).all();
|
|
8170
|
+
let minted = 0;
|
|
8171
|
+
for (const project of allProjects) {
|
|
8172
|
+
try {
|
|
8173
|
+
const previous = latestQueryBasketRevision(this.db, project.id)?.revision ?? null;
|
|
8174
|
+
const current = ensureCurrentQueryBasketRevision(this.db, project.id);
|
|
8175
|
+
if (current !== null && current.revision !== previous) minted += 1;
|
|
8176
|
+
} catch (err) {
|
|
8177
|
+
log13.warn("query-basket.mint-failed", { projectId: project.id, err: String(err) });
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
if (minted > 0) log13.info("query-basket.ensured", { projectCount: minted });
|
|
8181
|
+
}
|
|
8154
8182
|
/** Load all enabled schedules from DB and register cron jobs. */
|
|
8155
8183
|
start() {
|
|
8156
8184
|
this.ensureHealthSchedules();
|
|
8185
|
+
this.ensureQueryBaskets();
|
|
8157
8186
|
const allSchedules = this.db.select().from(schedules).where(eq14(schedules.enabled, true)).all();
|
|
8158
8187
|
for (const schedule of allSchedules) {
|
|
8159
8188
|
const missedRunAt = schedule.nextRunAt;
|
|
@@ -11979,6 +12008,12 @@ function parseBooleanEnv(value) {
|
|
|
11979
12008
|
function resolveDashboardRequirePassword(env, config) {
|
|
11980
12009
|
return parseBooleanEnv(env.CANONRY_DASHBOARD_REQUIRE_PASSWORD) ?? config.dashboard?.requirePassword ?? true;
|
|
11981
12010
|
}
|
|
12011
|
+
function resolveDashboardShowResourceLinks(env, config) {
|
|
12012
|
+
return parseBooleanEnv(env.CANONRY_DASHBOARD_SHOW_RESOURCE_LINKS) ?? config.dashboard?.showResourceLinks ?? true;
|
|
12013
|
+
}
|
|
12014
|
+
function resolveDashboardShowUpdateNotification(env, config) {
|
|
12015
|
+
return parseBooleanEnv(env.CANONRY_DASHBOARD_SHOW_UPDATE_NOTIFICATION) ?? config.dashboard?.showUpdateNotification ?? true;
|
|
12016
|
+
}
|
|
11982
12017
|
var API_ADAPTERS = [
|
|
11983
12018
|
geminiAdapter,
|
|
11984
12019
|
openaiAdapter,
|
|
@@ -12843,6 +12878,8 @@ async function createServer(opts) {
|
|
|
12843
12878
|
);
|
|
12844
12879
|
}
|
|
12845
12880
|
const dashboardRequirePassword = resolveDashboardRequirePassword(process.env, opts.config);
|
|
12881
|
+
const dashboardShowResourceLinks = resolveDashboardShowResourceLinks(process.env, opts.config);
|
|
12882
|
+
const dashboardShowUpdateNotification = resolveDashboardShowUpdateNotification(process.env, opts.config);
|
|
12846
12883
|
app.log.info(
|
|
12847
12884
|
{ dashboardRequirePassword },
|
|
12848
12885
|
"Dashboard password gate resolved"
|
|
@@ -13592,6 +13629,16 @@ async function createServer(opts) {
|
|
|
13592
13629
|
const injectConfig = (html, projectTabsOverride, themeOverride, renderTokenOverride) => {
|
|
13593
13630
|
const clientConfig = {};
|
|
13594
13631
|
if (basePath) clientConfig.basePath = basePath;
|
|
13632
|
+
const dashboardConfig = {};
|
|
13633
|
+
if (!dashboardShowResourceLinks) {
|
|
13634
|
+
dashboardConfig.showResourceLinks = false;
|
|
13635
|
+
}
|
|
13636
|
+
if (!dashboardShowUpdateNotification) {
|
|
13637
|
+
dashboardConfig.showUpdateNotification = false;
|
|
13638
|
+
}
|
|
13639
|
+
if (Object.keys(dashboardConfig).length > 0) {
|
|
13640
|
+
clientConfig.dashboard = dashboardConfig;
|
|
13641
|
+
}
|
|
13595
13642
|
if (embed.enabled) {
|
|
13596
13643
|
const embedClient = embedClientConfigForRequest(embed, projectTabsOverride, themeOverride, renderTokenOverride);
|
|
13597
13644
|
if (embedClient) clientConfig.embed = embedClient;
|
|
@@ -4408,6 +4408,13 @@ var gscSearchDataDtoSchema = z11.object({
|
|
|
4408
4408
|
ctr: z11.number(),
|
|
4409
4409
|
position: z11.number()
|
|
4410
4410
|
});
|
|
4411
|
+
var gscPerformanceOrderBySchema = z11.enum(["clicks", "impressions", "date"]);
|
|
4412
|
+
var gscPerformanceResponseDtoSchema = z11.object({
|
|
4413
|
+
rows: z11.array(gscSearchDataDtoSchema),
|
|
4414
|
+
totalMatching: z11.number(),
|
|
4415
|
+
truncated: z11.boolean(),
|
|
4416
|
+
latestAvailableDate: z11.string().nullable()
|
|
4417
|
+
});
|
|
4411
4418
|
var gscPerformanceDailyPointSchema = z11.object({
|
|
4412
4419
|
date: z11.string(),
|
|
4413
4420
|
clicks: z11.number(),
|
|
@@ -4423,6 +4430,38 @@ var gscPerformanceDailyDtoSchema = z11.object({
|
|
|
4423
4430
|
}),
|
|
4424
4431
|
daily: z11.array(gscPerformanceDailyPointSchema)
|
|
4425
4432
|
});
|
|
4433
|
+
var gscTopPageRowSchema = z11.object({
|
|
4434
|
+
page: z11.string(),
|
|
4435
|
+
clicks: z11.number(),
|
|
4436
|
+
impressions: z11.number(),
|
|
4437
|
+
ctr: z11.number()
|
|
4438
|
+
});
|
|
4439
|
+
var gscTopPagesDtoSchema = z11.object({
|
|
4440
|
+
rows: z11.array(gscTopPageRowSchema),
|
|
4441
|
+
totals: z11.object({
|
|
4442
|
+
clicks: z11.number(),
|
|
4443
|
+
impressions: z11.number(),
|
|
4444
|
+
ctr: z11.number(),
|
|
4445
|
+
days: z11.number(),
|
|
4446
|
+
/** First date the property-level totals actually cover. */
|
|
4447
|
+
coveredFrom: z11.string().nullable(),
|
|
4448
|
+
/** Last date the property-level totals actually cover. */
|
|
4449
|
+
coveredThrough: z11.string().nullable(),
|
|
4450
|
+
/**
|
|
4451
|
+
* False when the totals span less than the ranked rows above.
|
|
4452
|
+
*
|
|
4453
|
+
* The dimensioned and property-level tables sync independently, so a
|
|
4454
|
+
* 30-day totals sync can sit next to months of dimensioned rows. Printing
|
|
4455
|
+
* both as one period would misstate the window the totals belong to.
|
|
4456
|
+
*/
|
|
4457
|
+
complete: z11.boolean()
|
|
4458
|
+
}).nullable(),
|
|
4459
|
+
totalsSource: z11.literal("property-daily"),
|
|
4460
|
+
/** First date the ranked rows cover, so a caller can compare spans. */
|
|
4461
|
+
rankedFrom: z11.string().nullable(),
|
|
4462
|
+
/** Last date the ranked rows cover. */
|
|
4463
|
+
rankedThrough: z11.string().nullable()
|
|
4464
|
+
});
|
|
4426
4465
|
var gscUrlInspectionDtoSchema = z11.object({
|
|
4427
4466
|
id: z11.string(),
|
|
4428
4467
|
url: z11.string(),
|
|
@@ -10786,8 +10825,10 @@ export {
|
|
|
10786
10825
|
evaluateModelPointerExposure,
|
|
10787
10826
|
buildModelChangeNotice,
|
|
10788
10827
|
googleConnectionDtoSchema,
|
|
10789
|
-
|
|
10828
|
+
gscPerformanceOrderBySchema,
|
|
10829
|
+
gscPerformanceResponseDtoSchema,
|
|
10790
10830
|
gscPerformanceDailyDtoSchema,
|
|
10831
|
+
gscTopPagesDtoSchema,
|
|
10791
10832
|
gscUrlInspectionDtoSchema,
|
|
10792
10833
|
gscDeindexedRowSchema,
|
|
10793
10834
|
gscCoverageSummaryDtoSchema,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
discoveryRunRequestSchema,
|
|
24
24
|
gaMeasurementAnalysisWindowSchema,
|
|
25
25
|
gaMeasurementHostScopeSchema,
|
|
26
|
+
gscPerformanceOrderBySchema,
|
|
26
27
|
keywordBatchRequestSchema,
|
|
27
28
|
keywordGenerateRequestSchema,
|
|
28
29
|
notificationCreateRequestSchema,
|
|
@@ -42,7 +43,7 @@ import {
|
|
|
42
43
|
trafficConnectWordpressRequestSchema,
|
|
43
44
|
trafficEventKindSchema,
|
|
44
45
|
trafficSeriesGranularitySchema
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-OJOA425V.js";
|
|
46
47
|
|
|
47
48
|
// src/config.ts
|
|
48
49
|
import fs from "fs";
|
|
@@ -2067,6 +2068,18 @@ var getApiV1ProjectsByNameGoogleGscPerformanceDaily = (options) => {
|
|
|
2067
2068
|
...options
|
|
2068
2069
|
});
|
|
2069
2070
|
};
|
|
2071
|
+
var getApiV1ProjectsByNameGoogleGscTopPages = (options) => {
|
|
2072
|
+
return (options.client ?? client).get({
|
|
2073
|
+
security: [
|
|
2074
|
+
{
|
|
2075
|
+
scheme: "bearer",
|
|
2076
|
+
type: "http"
|
|
2077
|
+
}
|
|
2078
|
+
],
|
|
2079
|
+
url: "/api/v1/projects/{name}/google/gsc/top-pages",
|
|
2080
|
+
...options
|
|
2081
|
+
});
|
|
2082
|
+
};
|
|
2070
2083
|
var postApiV1ProjectsByNameGoogleGscInspect = (options) => {
|
|
2071
2084
|
return (options.client ?? client).post({
|
|
2072
2085
|
security: [
|
|
@@ -5139,6 +5152,15 @@ var ApiClient = class {
|
|
|
5139
5152
|
})
|
|
5140
5153
|
);
|
|
5141
5154
|
}
|
|
5155
|
+
async gscTopPages(project, params) {
|
|
5156
|
+
return this.invoke(
|
|
5157
|
+
() => getApiV1ProjectsByNameGoogleGscTopPages({
|
|
5158
|
+
client: this.heyClient,
|
|
5159
|
+
path: { name: project },
|
|
5160
|
+
query: params
|
|
5161
|
+
})
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5142
5164
|
async gscInspect(project, url) {
|
|
5143
5165
|
return this.invoke(
|
|
5144
5166
|
() => postApiV1ProjectsByNameGoogleGscInspect({
|
|
@@ -6140,7 +6162,9 @@ var gscPerformanceInputSchema = z2.object({
|
|
|
6140
6162
|
endDate: z2.string().optional(),
|
|
6141
6163
|
query: z2.string().optional(),
|
|
6142
6164
|
page: z2.string().optional(),
|
|
6143
|
-
limit: z2.number().int().positive().max(
|
|
6165
|
+
limit: z2.number().int().positive().max(5e3).optional(),
|
|
6166
|
+
offset: z2.number().int().nonnegative().optional(),
|
|
6167
|
+
orderBy: gscPerformanceOrderBySchema.optional(),
|
|
6144
6168
|
window: analyticsWindowSchema.optional()
|
|
6145
6169
|
});
|
|
6146
6170
|
var gscPerformanceDailyInputSchema = z2.object({
|
|
@@ -6149,6 +6173,13 @@ var gscPerformanceDailyInputSchema = z2.object({
|
|
|
6149
6173
|
endDate: z2.string().optional(),
|
|
6150
6174
|
window: analyticsWindowSchema.optional()
|
|
6151
6175
|
});
|
|
6176
|
+
var gscTopPagesInputSchema = z2.object({
|
|
6177
|
+
project: projectNameSchema,
|
|
6178
|
+
startDate: z2.string().optional(),
|
|
6179
|
+
endDate: z2.string().optional(),
|
|
6180
|
+
limit: z2.number().int().positive().max(500).optional(),
|
|
6181
|
+
window: analyticsWindowSchema.optional()
|
|
6182
|
+
});
|
|
6152
6183
|
var gscInspectionsInputSchema = z2.object({
|
|
6153
6184
|
project: projectNameSchema,
|
|
6154
6185
|
url: z2.string().optional(),
|
|
@@ -7074,13 +7105,13 @@ var canonryMcpTools = [
|
|
|
7074
7105
|
defineTool({
|
|
7075
7106
|
name: "canonry_gsc_performance",
|
|
7076
7107
|
title: "Get GSC performance",
|
|
7077
|
-
description: "Get stored Google Search Console performance rows for a Canonry project.",
|
|
7108
|
+
description: "Get stored Google Search Console performance rows for a Canonry project. Rows are ordered by clicks descending unless orderBy says otherwise, and the response reports totalMatching / truncated / latestAvailableDate. Never sum these rows for a property total, use canonry_gsc_performance_daily.",
|
|
7078
7109
|
access: "read",
|
|
7079
7110
|
tier: "gsc",
|
|
7080
7111
|
inputSchema: gscPerformanceInputSchema,
|
|
7081
7112
|
annotations: readAnnotations(),
|
|
7082
7113
|
openApiOperations: ["GET /api/v1/projects/{name}/google/gsc/performance"],
|
|
7083
|
-
handler: (client2, input) => client2.gscPerformance(input.project, compactStringParams(input, ["startDate", "endDate", "query", "page", "limit", "window"]))
|
|
7114
|
+
handler: (client2, input) => client2.gscPerformance(input.project, compactStringParams(input, ["startDate", "endDate", "query", "page", "limit", "offset", "orderBy", "window"]))
|
|
7084
7115
|
}),
|
|
7085
7116
|
defineTool({
|
|
7086
7117
|
name: "canonry_gsc_performance_daily",
|
|
@@ -7093,6 +7124,17 @@ var canonryMcpTools = [
|
|
|
7093
7124
|
openApiOperations: ["GET /api/v1/projects/{name}/google/gsc/performance/daily"],
|
|
7094
7125
|
handler: (client2, input) => client2.gscPerformanceDaily(input.project, compactStringParams(input, ["startDate", "endDate", "window"]))
|
|
7095
7126
|
}),
|
|
7127
|
+
defineTool({
|
|
7128
|
+
name: "canonry_gsc_top_pages",
|
|
7129
|
+
title: "Get top GSC pages",
|
|
7130
|
+
description: 'Get the project\'s pages ranked by summed GSC clicks, aggregated in SQL. The rows are a RANKING and their clicks/impressions do NOT add up to the site total: Google withholds rare queries and repeats one impression per ranking page. Read the window total from the returned `totals` block (labelled totalsSource "property-daily"), which is null when no property-level figure covers the window. Never sum the rows.',
|
|
7131
|
+
access: "read",
|
|
7132
|
+
tier: "gsc",
|
|
7133
|
+
inputSchema: gscTopPagesInputSchema,
|
|
7134
|
+
annotations: readAnnotations(),
|
|
7135
|
+
openApiOperations: ["GET /api/v1/projects/{name}/google/gsc/top-pages"],
|
|
7136
|
+
handler: (client2, input) => client2.gscTopPages(input.project, compactStringParams(input, ["startDate", "endDate", "limit", "window"]))
|
|
7137
|
+
}),
|
|
7096
7138
|
defineTool({
|
|
7097
7139
|
name: "canonry_gsc_inspections",
|
|
7098
7140
|
title: "List GSC inspections",
|