@ainyc/canonry 4.89.0 → 4.91.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/regression-playbook.md +2 -2
- package/assets/agent-workspace/skills/canonry/SKILL.md +1 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +8 -4
- package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +11 -11
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +4 -1
- package/assets/assets/{BacklinksPage-Cl3V55iK.js → BacklinksPage-DHC1po5Q.js} +1 -1
- package/assets/assets/{ChartPrimitives-D4D_lSIp.js → ChartPrimitives-Cu2xEVle.js} +1 -1
- package/assets/assets/{ProjectPage-Dwwcddk2.js → ProjectPage-DXYcaMVO.js} +1 -1
- package/assets/assets/{RunRow-CNjwUkqj.js → RunRow-BPqbBlXl.js} +1 -1
- package/assets/assets/{RunsPage-owFYxiRU.js → RunsPage-BJx3_Nel.js} +1 -1
- package/assets/assets/{SettingsPage-D3rc2Hze.js → SettingsPage-CXTIHdsv.js} +1 -1
- package/assets/assets/{TrafficPage-BDkhe0Iz.js → TrafficPage-iibCIBqX.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CUw6Odt1.js → TrafficSourceDetailPage-WU30E_59.js} +1 -1
- package/assets/assets/{arrow-left-BAOMqvGW.js → arrow-left-Ds_f14R8.js} +1 -1
- package/assets/assets/{extract-error-message-BO0DeR7V.js → extract-error-message-D250utEX.js} +1 -1
- package/assets/assets/{index-BbnglhBg.js → index-qiysJOcG.js} +57 -57
- package/assets/assets/{trash-2-RQK6oqQJ.js → trash-2-CcVWAeec.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-MUPIPQBB.js → chunk-2SO5ZS4S.js} +6 -4
- package/dist/{chunk-PRKBJNAG.js → chunk-6RVUSM4Y.js} +3 -3
- package/dist/{chunk-WEV4RH25.js → chunk-AFSVCKFI.js} +130 -13
- package/dist/{chunk-LRO4CLPL.js → chunk-WMBOHP7E.js} +28 -0
- package/dist/cli.js +7 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-SO46DMGR.js → intelligence-service-MHTYZ632.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-qiysJOcG.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-qiysJOcG.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dq7p98wZ.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-B57xfQbP.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-ClRVR6aX.js">
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
loadConfig,
|
|
10
10
|
loadConfigRaw,
|
|
11
11
|
saveConfigPatch
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6RVUSM4Y.js";
|
|
13
13
|
import {
|
|
14
14
|
CC_CACHE_DIR,
|
|
15
15
|
DUCKDB_SPEC,
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
siteAuditPages,
|
|
105
105
|
siteAuditSnapshots,
|
|
106
106
|
usageCounters
|
|
107
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-AFSVCKFI.js";
|
|
108
108
|
import {
|
|
109
109
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
110
110
|
AGENT_PROVIDER_IDS,
|
|
@@ -164,7 +164,7 @@ import {
|
|
|
164
164
|
validationError,
|
|
165
165
|
winnabilityClassLabel,
|
|
166
166
|
withRetry
|
|
167
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-WMBOHP7E.js";
|
|
168
168
|
|
|
169
169
|
// src/telemetry.ts
|
|
170
170
|
import crypto from "crypto";
|
|
@@ -4067,6 +4067,8 @@ async function executeGbpSync(db, runId, projectId, opts) {
|
|
|
4067
4067
|
syncedAt: insertNow,
|
|
4068
4068
|
syncRunId: runId
|
|
4069
4069
|
}).run();
|
|
4070
|
+
} else if (lodging !== null && latestLodging) {
|
|
4071
|
+
tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq3(gbpLodgingSnapshots.id, latestLodging.id)).run();
|
|
4070
4072
|
}
|
|
4071
4073
|
if (placeToWrite) {
|
|
4072
4074
|
tx.insert(gbpPlaceDetails).values({
|
|
@@ -6321,7 +6323,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
6321
6323
|
return result;
|
|
6322
6324
|
}
|
|
6323
6325
|
async function backfillInsightsCommand(project, opts) {
|
|
6324
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
6326
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-MHTYZ632.js");
|
|
6325
6327
|
const config = loadConfig();
|
|
6326
6328
|
const db = createClient(config.database);
|
|
6327
6329
|
migrate(db);
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
trafficConnectVercelRequestSchema,
|
|
24
24
|
trafficConnectWordpressRequestSchema,
|
|
25
25
|
trafficEventKindSchema
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-WMBOHP7E.js";
|
|
27
27
|
|
|
28
28
|
// src/config.ts
|
|
29
29
|
import fs from "fs";
|
|
@@ -6291,7 +6291,7 @@ var canonryMcpTools = [
|
|
|
6291
6291
|
defineTool({
|
|
6292
6292
|
name: "canonry_gbp_lodging",
|
|
6293
6293
|
title: "Get GBP lodging attributes",
|
|
6294
|
-
description:
|
|
6294
|
+
description: 'List the latest Google Business Profile lodging snapshot per location (hotel structured attributes). populatedGroupCount=0 means the Lodging API returned no structured attributes, which is common even for complete hotels because the owner-set "Hotel details" amenity panel is a separate surface the API does not expose. Treat it as a "verify the Hotel details panel", not a confirmed gap.',
|
|
6295
6295
|
access: "read",
|
|
6296
6296
|
tier: "gbp",
|
|
6297
6297
|
inputSchema: gbpLocationScopedInputSchema,
|
|
@@ -6313,7 +6313,7 @@ var canonryMcpTools = [
|
|
|
6313
6313
|
defineTool({
|
|
6314
6314
|
name: "canonry_gbp_summary",
|
|
6315
6315
|
title: "Get GBP local-AEO summary",
|
|
6316
|
-
description: "Composite Google Business Profile summary for a project: performance totals + 7d deltas, keyword thresholded %, place-action CTA presence, and
|
|
6316
|
+
description: "Composite Google Business Profile summary for a project: performance totals + 7d deltas, keyword thresholded %, place-action CTA presence, lodging coverage, and owner-content profile completeness (how many selected locations populate secondary categories / description / service area / hours / phone, plus closed-status counts). All derived numbers computed server-side.",
|
|
6317
6317
|
access: "read",
|
|
6318
6318
|
tier: "gbp",
|
|
6319
6319
|
inputSchema: gbpLocationScopedInputSchema,
|
|
@@ -258,7 +258,7 @@ import {
|
|
|
258
258
|
wordpressSchemaDeployResultDtoSchema,
|
|
259
259
|
wordpressSchemaStatusResultDtoSchema,
|
|
260
260
|
wordpressStatusDtoSchema
|
|
261
|
-
} from "./chunk-
|
|
261
|
+
} from "./chunk-WMBOHP7E.js";
|
|
262
262
|
|
|
263
263
|
// src/intelligence-service.ts
|
|
264
264
|
import { eq as eq37, desc as desc18, asc as asc5, and as and27, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
@@ -1170,6 +1170,16 @@ var gbpLocations = sqliteTable("gbp_locations", {
|
|
|
1170
1170
|
// rendered-listing data. Null when Google has not assigned a Place ID.
|
|
1171
1171
|
placeId: text("place_id"),
|
|
1172
1172
|
mapsUri: text("maps_uri"),
|
|
1173
|
+
// Owner-authored profile content from the Business Information v1 Location
|
|
1174
|
+
// resource — the entity-anchor + qualifier signals AI answer engines weight.
|
|
1175
|
+
// `serviceArea` / `regularHours` are stored verbatim (presence + raw shape).
|
|
1176
|
+
additionalCategories: text("additional_categories", { mode: "json" }).$type(),
|
|
1177
|
+
description: text("description"),
|
|
1178
|
+
serviceArea: text("service_area", { mode: "json" }).$type(),
|
|
1179
|
+
regularHours: text("regular_hours", { mode: "json" }).$type(),
|
|
1180
|
+
primaryPhone: text("primary_phone"),
|
|
1181
|
+
openStatus: text("open_status"),
|
|
1182
|
+
openingDate: text("opening_date"),
|
|
1173
1183
|
selected: integer("selected", { mode: "boolean" }).notNull().default(true),
|
|
1174
1184
|
syncedAt: text("synced_at"),
|
|
1175
1185
|
createdAt: text("created_at").notNull(),
|
|
@@ -3134,6 +3144,28 @@ var MIGRATION_VERSIONS = [
|
|
|
3134
3144
|
db.run(sql.raw(`ALTER TABLE health_snapshots ADD COLUMN mentioned_pairs INTEGER`));
|
|
3135
3145
|
}
|
|
3136
3146
|
}
|
|
3147
|
+
},
|
|
3148
|
+
{
|
|
3149
|
+
version: 81,
|
|
3150
|
+
name: "gbp-locations-owner-content",
|
|
3151
|
+
statements: [],
|
|
3152
|
+
run: (db) => {
|
|
3153
|
+
if (!tableExists(db, "gbp_locations")) return;
|
|
3154
|
+
const cols = [
|
|
3155
|
+
"additional_categories",
|
|
3156
|
+
"description",
|
|
3157
|
+
"service_area",
|
|
3158
|
+
"regular_hours",
|
|
3159
|
+
"primary_phone",
|
|
3160
|
+
"open_status",
|
|
3161
|
+
"opening_date"
|
|
3162
|
+
];
|
|
3163
|
+
for (const col of cols) {
|
|
3164
|
+
if (!columnExists(db, "gbp_locations", col)) {
|
|
3165
|
+
db.run(sql.raw(`ALTER TABLE gbp_locations ADD COLUMN ${col} TEXT`));
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3137
3169
|
}
|
|
3138
3170
|
];
|
|
3139
3171
|
function rebuildBacklinkTableWithSource(tx, table) {
|
|
@@ -5217,22 +5249,22 @@ function analyzeGbp(signals) {
|
|
|
5217
5249
|
drafts.push({
|
|
5218
5250
|
...base,
|
|
5219
5251
|
type: "gbp-listing-discrepancy",
|
|
5220
|
-
severity: "
|
|
5221
|
-
title: `${loc.displayName}: public listing
|
|
5252
|
+
severity: "medium",
|
|
5253
|
+
title: `${loc.displayName}: public listing advertises ${loc.placesAmenities.length} amenit${loc.placesAmenities.length === 1 ? "y" : "ies"} canonry can\u2019t confirm via the GBP API`,
|
|
5222
5254
|
recommendation: {
|
|
5223
|
-
action:
|
|
5224
|
-
reason: `Google\u2019s rendered listing advertises ${amenityList}
|
|
5255
|
+
action: 'Verify these amenities are set in the Google Business Profile "Hotel details" panel so the structured profile matches what the public listing advertises.',
|
|
5256
|
+
reason: `Google\u2019s rendered listing advertises ${amenityList}, but the GBP Lodging API returns no structured attributes for this location. The Lodging API does not expose the owner-set "Hotel details" amenity panel, so the amenities may already be set there and simply not be readable via the API. Verify in Hotel details: if any are missing, add them, since the structured attributes are the amenity data you directly control and that AI answer engines cite.`
|
|
5225
5257
|
}
|
|
5226
5258
|
});
|
|
5227
5259
|
} else {
|
|
5228
5260
|
drafts.push({
|
|
5229
5261
|
...base,
|
|
5230
5262
|
type: "gbp-lodging-gap",
|
|
5231
|
-
severity: "
|
|
5232
|
-
title: `${loc.displayName}: lodging
|
|
5263
|
+
severity: "low",
|
|
5264
|
+
title: `${loc.displayName}: structured lodging attributes not readable via the GBP API`,
|
|
5233
5265
|
recommendation: {
|
|
5234
|
-
action:
|
|
5235
|
-
reason:
|
|
5266
|
+
action: 'Verify the hotel amenities in the Google Business Profile "Hotel details" panel. If they are already set there, no change is needed.',
|
|
5267
|
+
reason: 'The GBP Lodging API returns no structured attributes for this location. That resource is commonly empty even for complete hotels, because the owner-set "Hotel details" amenity panel (breakfast, wifi, parking, accessibility, and the like) writes to a separate surface the Lodging API does not expose. Treat this as a verify, not a confirmed gap: confirm the amenities are set in Hotel details, the amenity source you directly control and that AI answer engines cite.'
|
|
5236
5268
|
}
|
|
5237
5269
|
});
|
|
5238
5270
|
}
|
|
@@ -18596,6 +18628,38 @@ function summarizeLodging(rows) {
|
|
|
18596
18628
|
emptyLodgingCount: rows.length - populatedLodgingCount
|
|
18597
18629
|
};
|
|
18598
18630
|
}
|
|
18631
|
+
function summarizeProfileCompleteness(rows) {
|
|
18632
|
+
let withSecondaryCategories = 0;
|
|
18633
|
+
let secondaryCategoryTotal = 0;
|
|
18634
|
+
let withDescription = 0;
|
|
18635
|
+
let withServiceArea = 0;
|
|
18636
|
+
let withHours = 0;
|
|
18637
|
+
let withPrimaryPhone = 0;
|
|
18638
|
+
let permanentlyClosed = 0;
|
|
18639
|
+
let temporarilyClosed = 0;
|
|
18640
|
+
for (const r of rows) {
|
|
18641
|
+
const secondaryCount = r.additionalCategories?.length ?? 0;
|
|
18642
|
+
if (secondaryCount > 0) withSecondaryCategories++;
|
|
18643
|
+
secondaryCategoryTotal += secondaryCount;
|
|
18644
|
+
if ((r.description ?? "").trim().length > 0) withDescription++;
|
|
18645
|
+
if (r.serviceArea != null) withServiceArea++;
|
|
18646
|
+
if (r.regularHours != null) withHours++;
|
|
18647
|
+
if ((r.primaryPhone ?? "").trim().length > 0) withPrimaryPhone++;
|
|
18648
|
+
if (r.openStatus === "CLOSED_PERMANENTLY") permanentlyClosed++;
|
|
18649
|
+
if (r.openStatus === "CLOSED_TEMPORARILY") temporarilyClosed++;
|
|
18650
|
+
}
|
|
18651
|
+
return {
|
|
18652
|
+
locationCount: rows.length,
|
|
18653
|
+
withSecondaryCategories,
|
|
18654
|
+
secondaryCategoryTotal,
|
|
18655
|
+
withDescription,
|
|
18656
|
+
withServiceArea,
|
|
18657
|
+
withHours,
|
|
18658
|
+
withPrimaryPhone,
|
|
18659
|
+
permanentlyClosed,
|
|
18660
|
+
temporarilyClosed
|
|
18661
|
+
};
|
|
18662
|
+
}
|
|
18599
18663
|
function buildGbpSummary(input) {
|
|
18600
18664
|
const freshness = computeFreshness(input.dailyMetrics, input.asOfDate);
|
|
18601
18665
|
const anchor = freshness.dataThroughDate ?? input.asOfDate;
|
|
@@ -18612,7 +18676,8 @@ function buildGbpSummary(input) {
|
|
|
18612
18676
|
timeseries: buildTimeseries(input.dailyMetrics, freshness),
|
|
18613
18677
|
keywords: computeKeywordCoverage(input.keywords),
|
|
18614
18678
|
placeActions: summarizePlaceActions(input.placeActions),
|
|
18615
|
-
lodging: summarizeLodging(input.lodging)
|
|
18679
|
+
lodging: summarizeLodging(input.lodging),
|
|
18680
|
+
profileCompleteness: summarizeProfileCompleteness(input.locationProfiles)
|
|
18616
18681
|
};
|
|
18617
18682
|
}
|
|
18618
18683
|
|
|
@@ -19710,6 +19775,15 @@ var GBP_LOCATIONS_DEFAULT_READ_MASK = [
|
|
|
19710
19775
|
"storefrontAddress",
|
|
19711
19776
|
"websiteUri",
|
|
19712
19777
|
"categories.primaryCategory.displayName",
|
|
19778
|
+
// Owner-authored profile content — the entity-anchor and qualifier signals AI
|
|
19779
|
+
// answer engines weight most. All ride the same Business Information v1
|
|
19780
|
+
// Location resource canonry already reads (no new auth / API enablement).
|
|
19781
|
+
"categories.additionalCategories.displayName",
|
|
19782
|
+
"profile.description",
|
|
19783
|
+
"serviceArea",
|
|
19784
|
+
"regularHours",
|
|
19785
|
+
"phoneNumbers",
|
|
19786
|
+
"openInfo",
|
|
19713
19787
|
"metadata.placeId",
|
|
19714
19788
|
"metadata.mapsUri"
|
|
19715
19789
|
].join(",");
|
|
@@ -19847,6 +19921,26 @@ function formatStorefrontAddress(loc) {
|
|
|
19847
19921
|
].filter((p) => Boolean(p));
|
|
19848
19922
|
return parts.length ? parts.join(", ") : null;
|
|
19849
19923
|
}
|
|
19924
|
+
function buildLocationProfileFields(loc) {
|
|
19925
|
+
return {
|
|
19926
|
+
additionalCategories: (loc.categories?.additionalCategories ?? []).map((c) => c.displayName).filter((n) => Boolean(n)),
|
|
19927
|
+
description: loc.profile?.description ?? null,
|
|
19928
|
+
serviceArea: loc.serviceArea ?? null,
|
|
19929
|
+
regularHours: loc.regularHours ?? null,
|
|
19930
|
+
primaryPhone: loc.phoneNumbers?.primaryPhone ?? null,
|
|
19931
|
+
openStatus: loc.openInfo?.status ?? null,
|
|
19932
|
+
openingDate: formatOpeningDate(loc.openInfo?.openingDate)
|
|
19933
|
+
};
|
|
19934
|
+
}
|
|
19935
|
+
function formatOpeningDate(date) {
|
|
19936
|
+
if (!date?.year) return null;
|
|
19937
|
+
let out = String(date.year);
|
|
19938
|
+
if (date.month) {
|
|
19939
|
+
out += `-${String(date.month).padStart(2, "0")}`;
|
|
19940
|
+
if (date.day) out += `-${String(date.day).padStart(2, "0")}`;
|
|
19941
|
+
}
|
|
19942
|
+
return out;
|
|
19943
|
+
}
|
|
19850
19944
|
|
|
19851
19945
|
// ../integration-google-business-profile/src/performance-client.ts
|
|
19852
19946
|
function pad2(n) {
|
|
@@ -20863,6 +20957,13 @@ async function googleRoutes(app, opts) {
|
|
|
20863
20957
|
websiteUri: row.websiteUri ?? null,
|
|
20864
20958
|
placeId: row.placeId ?? null,
|
|
20865
20959
|
mapsUri: row.mapsUri ?? null,
|
|
20960
|
+
additionalCategories: row.additionalCategories ?? [],
|
|
20961
|
+
description: row.description ?? null,
|
|
20962
|
+
serviceArea: row.serviceArea ?? null,
|
|
20963
|
+
regularHours: row.regularHours ?? null,
|
|
20964
|
+
primaryPhone: row.primaryPhone ?? null,
|
|
20965
|
+
openStatus: row.openStatus ?? null,
|
|
20966
|
+
openingDate: row.openingDate ?? null,
|
|
20866
20967
|
selected: Boolean(row.selected),
|
|
20867
20968
|
syncedAt: row.syncedAt ?? null,
|
|
20868
20969
|
createdAt: row.createdAt,
|
|
@@ -20958,6 +21059,7 @@ async function googleRoutes(app, opts) {
|
|
|
20958
21059
|
if (switching) clearGbpProjectData(tx, project.id);
|
|
20959
21060
|
for (const remote of remoteLocations) {
|
|
20960
21061
|
const existing = tx.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, remote.name))).get();
|
|
21062
|
+
const profile = buildLocationProfileFields(remote);
|
|
20961
21063
|
if (existing) {
|
|
20962
21064
|
tx.update(gbpLocations).set({
|
|
20963
21065
|
accountName,
|
|
@@ -20967,6 +21069,7 @@ async function googleRoutes(app, opts) {
|
|
|
20967
21069
|
websiteUri: remote.websiteUri ?? null,
|
|
20968
21070
|
placeId: remote.metadata?.placeId ?? null,
|
|
20969
21071
|
mapsUri: remote.metadata?.mapsUri ?? null,
|
|
21072
|
+
...profile,
|
|
20970
21073
|
updatedAt: now
|
|
20971
21074
|
}).where(eq21(gbpLocations.id, existing.id)).run();
|
|
20972
21075
|
} else {
|
|
@@ -20981,6 +21084,7 @@ async function googleRoutes(app, opts) {
|
|
|
20981
21084
|
websiteUri: remote.websiteUri ?? null,
|
|
20982
21085
|
placeId: remote.metadata?.placeId ?? null,
|
|
20983
21086
|
mapsUri: remote.metadata?.mapsUri ?? null,
|
|
21087
|
+
...profile,
|
|
20984
21088
|
selected: selectAllNew,
|
|
20985
21089
|
createdAt: now,
|
|
20986
21090
|
updatedAt: now
|
|
@@ -21204,7 +21308,8 @@ async function googleRoutes(app, opts) {
|
|
|
21204
21308
|
dailyMetrics: [],
|
|
21205
21309
|
keywords: [],
|
|
21206
21310
|
placeActions: [],
|
|
21207
|
-
lodging: []
|
|
21311
|
+
lodging: [],
|
|
21312
|
+
locationProfiles: []
|
|
21208
21313
|
});
|
|
21209
21314
|
}
|
|
21210
21315
|
const metricRows = app.db.select().from(gbpDailyMetrics).where(and14(eq21(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
|
|
@@ -21217,6 +21322,14 @@ async function googleRoutes(app, opts) {
|
|
|
21217
21322
|
latestLodgingByLocation.set(row.locationName, { locationName: row.locationName, populatedGroupCount: row.populatedGroupCount });
|
|
21218
21323
|
}
|
|
21219
21324
|
}
|
|
21325
|
+
const profileRows = app.db.select({
|
|
21326
|
+
additionalCategories: gbpLocations.additionalCategories,
|
|
21327
|
+
description: gbpLocations.description,
|
|
21328
|
+
serviceArea: gbpLocations.serviceArea,
|
|
21329
|
+
regularHours: gbpLocations.regularHours,
|
|
21330
|
+
primaryPhone: gbpLocations.primaryPhone,
|
|
21331
|
+
openStatus: gbpLocations.openStatus
|
|
21332
|
+
}).from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), inArray10(gbpLocations.locationName, locationNames))).all();
|
|
21220
21333
|
return buildGbpSummary({
|
|
21221
21334
|
locationName,
|
|
21222
21335
|
locationCount: locationNames.length,
|
|
@@ -21224,7 +21337,8 @@ async function googleRoutes(app, opts) {
|
|
|
21224
21337
|
dailyMetrics: metricRows.map((r) => ({ metric: r.metric, date: r.date, value: r.value })),
|
|
21225
21338
|
keywords: keywordRows.map((r) => ({ valueCount: r.valueCount ?? null, valueThreshold: r.valueThreshold ?? null })),
|
|
21226
21339
|
placeActions: placeActionRows.map((r) => ({ placeActionType: r.placeActionType, providerType: r.providerType ?? null })),
|
|
21227
|
-
lodging: [...latestLodgingByLocation.values()]
|
|
21340
|
+
lodging: [...latestLodgingByLocation.values()],
|
|
21341
|
+
locationProfiles: profileRows
|
|
21228
21342
|
});
|
|
21229
21343
|
});
|
|
21230
21344
|
}
|
|
@@ -34363,7 +34477,10 @@ var IntelligenceService = class {
|
|
|
34363
34477
|
keywords: [],
|
|
34364
34478
|
// keyword coverage is unused here; the trend uses the monthly series below
|
|
34365
34479
|
placeActions: placeActionRows.map((r) => ({ placeActionType: r.placeActionType, providerType: r.providerType ?? null })),
|
|
34366
|
-
lodging: lodgingRow ? [{ locationName, populatedGroupCount: lodgingRow.populatedGroupCount }] : []
|
|
34480
|
+
lodging: lodgingRow ? [{ locationName, populatedGroupCount: lodgingRow.populatedGroupCount }] : [],
|
|
34481
|
+
// The analyzer consumes metric/keyword/place-action/lodging signals, not
|
|
34482
|
+
// the profile-completeness scorecard, so it is left empty here.
|
|
34483
|
+
locationProfiles: []
|
|
34367
34484
|
});
|
|
34368
34485
|
const trend = this.buildGbpKeywordTrend(projectId, locationName);
|
|
34369
34486
|
return {
|
|
@@ -901,6 +901,17 @@ var gbpLocationDtoSchema = z8.object({
|
|
|
901
901
|
// the location is not on Maps). `placeId` is the join key to the Places API.
|
|
902
902
|
placeId: z8.string().nullable(),
|
|
903
903
|
mapsUri: z8.string().nullable(),
|
|
904
|
+
// Owner-authored profile content (Business Information v1 Location resource).
|
|
905
|
+
// These are the entity-anchor + qualifier signals AI answer engines weight:
|
|
906
|
+
// secondary categories, the business description, the service area + hours
|
|
907
|
+
// (stored verbatim as JSON), the primary phone, and the open state.
|
|
908
|
+
additionalCategories: z8.array(z8.string()),
|
|
909
|
+
description: z8.string().nullable(),
|
|
910
|
+
serviceArea: z8.record(z8.string(), z8.unknown()).nullable(),
|
|
911
|
+
regularHours: z8.record(z8.string(), z8.unknown()).nullable(),
|
|
912
|
+
primaryPhone: z8.string().nullable(),
|
|
913
|
+
openStatus: z8.string().nullable(),
|
|
914
|
+
openingDate: z8.string().nullable(),
|
|
904
915
|
selected: z8.boolean(),
|
|
905
916
|
syncedAt: z8.string().nullable(),
|
|
906
917
|
createdAt: z8.string(),
|
|
@@ -1058,6 +1069,19 @@ var gbpSummaryDtoSchema = z8.object({
|
|
|
1058
1069
|
lodgingLocationCount: z8.number().int().nonnegative(),
|
|
1059
1070
|
populatedLodgingCount: z8.number().int().nonnegative(),
|
|
1060
1071
|
emptyLodgingCount: z8.number().int().nonnegative()
|
|
1072
|
+
}),
|
|
1073
|
+
// Owner-content completeness over the in-scope locations: how many populate
|
|
1074
|
+
// each entity-anchor / qualifier field AI answer engines weight.
|
|
1075
|
+
profileCompleteness: z8.object({
|
|
1076
|
+
locationCount: z8.number().int().nonnegative(),
|
|
1077
|
+
withSecondaryCategories: z8.number().int().nonnegative(),
|
|
1078
|
+
secondaryCategoryTotal: z8.number().int().nonnegative(),
|
|
1079
|
+
withDescription: z8.number().int().nonnegative(),
|
|
1080
|
+
withServiceArea: z8.number().int().nonnegative(),
|
|
1081
|
+
withHours: z8.number().int().nonnegative(),
|
|
1082
|
+
withPrimaryPhone: z8.number().int().nonnegative(),
|
|
1083
|
+
permanentlyClosed: z8.number().int().nonnegative(),
|
|
1084
|
+
temporarilyClosed: z8.number().int().nonnegative()
|
|
1061
1085
|
})
|
|
1062
1086
|
});
|
|
1063
1087
|
var GBP_METRIC_LABELS = {
|
|
@@ -4827,6 +4851,7 @@ var TrafficPathClasses = {
|
|
|
4827
4851
|
other: "other"
|
|
4828
4852
|
};
|
|
4829
4853
|
var ROBOTS_BASENAMES = /* @__PURE__ */ new Set(["robots.txt", "llms.txt", "llms-full.txt"]);
|
|
4854
|
+
var INFRA_BASENAMES = /* @__PURE__ */ new Set(["xmlrpc.php", "wp-cron.php"]);
|
|
4830
4855
|
var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4831
4856
|
"css",
|
|
4832
4857
|
"js",
|
|
@@ -4925,6 +4950,9 @@ function classifyTrafficPath(pathNormalized) {
|
|
|
4925
4950
|
if (ROBOTS_BASENAMES.has(basename)) return TrafficPathClasses.robots;
|
|
4926
4951
|
if (lower.endsWith(".xml") || lower.endsWith(".xml.gz")) return TrafficPathClasses.sitemap;
|
|
4927
4952
|
if (SITEMAP_BASENAME.test(basename)) return TrafficPathClasses.sitemap;
|
|
4953
|
+
if (INFRA_BASENAMES.has(basename)) return TrafficPathClasses.other;
|
|
4954
|
+
if (lower === "/wp-json" || lower.startsWith("/wp-json/")) return TrafficPathClasses.other;
|
|
4955
|
+
if (lower.endsWith("/feed") || lower.includes("/feed/")) return TrafficPathClasses.other;
|
|
4928
4956
|
const dot = basename.lastIndexOf(".");
|
|
4929
4957
|
const ext = dot > 0 ? basename.slice(dot + 1) : "";
|
|
4930
4958
|
if (ext) {
|
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
setTelemetrySource,
|
|
28
28
|
showFirstRunNotice,
|
|
29
29
|
trackEvent
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-2SO5ZS4S.js";
|
|
31
31
|
import {
|
|
32
32
|
CliError,
|
|
33
33
|
EXIT_SYSTEM_ERROR,
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
saveConfig,
|
|
45
45
|
saveConfigPatch,
|
|
46
46
|
usageError
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-6RVUSM4Y.js";
|
|
48
48
|
import {
|
|
49
49
|
apiKeys,
|
|
50
50
|
createClient,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
projects,
|
|
53
53
|
queries,
|
|
54
54
|
renderReportHtml
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-AFSVCKFI.js";
|
|
56
56
|
import {
|
|
57
57
|
BacklinkSources,
|
|
58
58
|
CcReleaseSyncStatuses,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
providerQuotaPolicySchema,
|
|
78
78
|
resolveProviderInput,
|
|
79
79
|
winnabilityClassSchema
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-WMBOHP7E.js";
|
|
81
81
|
|
|
82
82
|
// src/cli.ts
|
|
83
83
|
import { pathToFileURL } from "url";
|
|
@@ -3514,7 +3514,7 @@ async function gbpLodging(project, opts) {
|
|
|
3514
3514
|
}
|
|
3515
3515
|
console.log(`${response.total} lodging profile(s):`);
|
|
3516
3516
|
for (const l of response.lodging) {
|
|
3517
|
-
const note = l.populatedGroupCount === 0 ? "
|
|
3517
|
+
const note = l.populatedGroupCount === 0 ? ' (none readable via the Lodging API; verify the "Hotel details" panel, often set there but not exposed by the API)' : "";
|
|
3518
3518
|
console.log(` ${l.locationName} ${l.populatedGroupCount} attribute group(s)${note}`);
|
|
3519
3519
|
}
|
|
3520
3520
|
}
|
|
@@ -3566,6 +3566,8 @@ async function gbpSummary(project, opts) {
|
|
|
3566
3566
|
Keywords: ${s.keywords.total} tracked, ${s.keywords.thresholdedPct}% privacy-thresholded`);
|
|
3567
3567
|
console.log(`Place actions: ${s.placeActions.total} CTA(s) \u2014 reservation:${s.placeActions.hasReservationCta ? "yes" : "no"} booking:${s.placeActions.hasBookingCta ? "yes" : "no"} direct-merchant:${s.placeActions.hasDirectMerchantCta ? "yes" : "no"}`);
|
|
3568
3568
|
console.log(`Lodging: ${s.lodging.lodgingLocationCount} profile(s), ${s.lodging.populatedLodgingCount} populated, ${s.lodging.emptyLodgingCount} empty`);
|
|
3569
|
+
const pc = s.profileCompleteness;
|
|
3570
|
+
console.log(`Profile completeness (${pc.locationCount} location(s)): ${pc.withSecondaryCategories} w/ secondary categories (${pc.secondaryCategoryTotal} total), ${pc.withDescription} w/ description, ${pc.withServiceArea} w/ service area, ${pc.withHours} w/ hours, ${pc.withPrimaryPhone} w/ phone` + (pc.permanentlyClosed || pc.temporarilyClosed ? `; closed: ${pc.permanentlyClosed} permanently, ${pc.temporarilyClosed} temporarily` : ""));
|
|
3569
3571
|
}
|
|
3570
3572
|
|
|
3571
3573
|
// src/cli-commands/gbp.ts
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2SO5ZS4S.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-6RVUSM4Y.js";
|
|
7
|
+
import "./chunk-AFSVCKFI.js";
|
|
8
|
+
import "./chunk-WMBOHP7E.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-6RVUSM4Y.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-WMBOHP7E.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.91.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",
|
|
@@ -63,26 +63,26 @@
|
|
|
63
63
|
"tsup": "^8.5.1",
|
|
64
64
|
"tsx": "^4.19.0",
|
|
65
65
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
66
|
-
"@ainyc/canonry-
|
|
66
|
+
"@ainyc/canonry-api-client": "0.0.0",
|
|
67
67
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
68
|
-
"@ainyc/canonry-db": "0.0.0",
|
|
69
|
-
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
70
68
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
71
|
-
"@ainyc/canonry-
|
|
69
|
+
"@ainyc/canonry-db": "0.0.0",
|
|
70
|
+
"@ainyc/canonry-config": "0.0.0",
|
|
72
71
|
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
72
|
+
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
73
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
73
74
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
74
75
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
75
|
-
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
76
|
-
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
77
76
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
77
|
+
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
78
78
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
79
|
-
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
80
79
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
81
80
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
82
81
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
83
|
-
"@ainyc/canonry-provider-local": "0.0.0",
|
|
84
82
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
85
|
-
"@ainyc/canonry-provider-
|
|
83
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
84
|
+
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
85
|
+
"@ainyc/canonry-integration-wordpress": "0.0.0"
|
|
86
86
|
},
|
|
87
87
|
"scripts": {
|
|
88
88
|
"build": "tsx scripts/copy-agent-assets.ts && tsup && tsx build-web.ts",
|