@ainyc/canonry 4.91.1 → 4.93.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.
Files changed (26) hide show
  1. package/assets/agent-workspace/skills/aero/references/regression-playbook.md +1 -0
  2. package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +7 -0
  3. package/assets/assets/{BacklinksPage-DHC1po5Q.js → BacklinksPage-nkgAbbjk.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-Cu2xEVle.js → ChartPrimitives-kdfylr1f.js} +1 -1
  5. package/assets/assets/ProjectPage-BQYmq7FP.js +6 -0
  6. package/assets/assets/{RunRow-BPqbBlXl.js → RunRow-P-n5OkiY.js} +1 -1
  7. package/assets/assets/{RunsPage-BJx3_Nel.js → RunsPage-DfBb4WCS.js} +1 -1
  8. package/assets/assets/{SettingsPage-CXTIHdsv.js → SettingsPage-CRGuFgeK.js} +1 -1
  9. package/assets/assets/{TrafficPage-iibCIBqX.js → TrafficPage-CMqP0AGZ.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-WU30E_59.js → TrafficSourceDetailPage-XRGzkKWk.js} +1 -1
  11. package/assets/assets/{arrow-left-Ds_f14R8.js → arrow-left-BCR55g4n.js} +1 -1
  12. package/assets/assets/{extract-error-message-D250utEX.js → extract-error-message-BG1AlucT.js} +1 -1
  13. package/assets/assets/{index-qiysJOcG.js → index-BPjIZIDO.js} +73 -73
  14. package/assets/assets/{index-fLUYE9Z5.css → index-DiykVUPr.css} +1 -1
  15. package/assets/assets/{trash-2-CcVWAeec.js → trash-2-Cme1gNjU.js} +1 -1
  16. package/assets/index.html +2 -2
  17. package/dist/{chunk-KFJPJBW3.js → chunk-ADTLQHJY.js} +69 -5
  18. package/dist/{chunk-WMBOHP7E.js → chunk-AUQNKEHV.js} +20 -0
  19. package/dist/{chunk-4KQYRCVC.js → chunk-KBDQ5OE4.js} +181 -56
  20. package/dist/{chunk-6RVUSM4Y.js → chunk-PK46TKGM.js} +33 -1
  21. package/dist/cli.js +36 -4
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-QB6HQRWU.js → intelligence-service-4ATCJITP.js} +2 -2
  24. package/dist/mcp.js +2 -2
  25. package/package.json +10 -10
  26. package/assets/assets/ProjectPage-DXYcaMVO.js +0 -6
@@ -1 +1 @@
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};
1
+ import{c}from"./index-BPjIZIDO.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,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-qiysJOcG.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-BPjIZIDO.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">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-DK7fbRNb.js">
20
- <link rel="stylesheet" crossorigin href="./assets/index-fLUYE9Z5.css">
20
+ <link rel="stylesheet" crossorigin href="./assets/index-DiykVUPr.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-6RVUSM4Y.js";
12
+ } from "./chunk-PK46TKGM.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -32,12 +32,14 @@ import {
32
32
  backlinkSummaries,
33
33
  bingCoverageSnapshots,
34
34
  bingUrlInspections,
35
+ buildLocationProfileFields,
35
36
  ccReleasePaths,
36
37
  ccReleaseSyncs,
37
38
  classifyAiUserFetch,
38
39
  classifyCrawler,
39
40
  competitors,
40
41
  computeCompetitorOverlap,
42
+ countAttributes,
41
43
  countPopulatedGroups,
42
44
  crawlerEventsHourly,
43
45
  createClient,
@@ -52,8 +54,10 @@ import {
52
54
  extractRecommendedCompetitors,
53
55
  fetchDailyMetrics,
54
56
  fetchSearchAnalytics,
57
+ formatStorefrontAddress,
55
58
  gaAiReferrals,
56
59
  gaTrafficSnapshots,
60
+ gbpAttributesSnapshots,
57
61
  gbpDailyMetrics,
58
62
  gbpKeywordImpressions,
59
63
  gbpKeywordMonthly,
@@ -61,6 +65,7 @@ import {
61
65
  gbpLodgingSnapshots,
62
66
  gbpPlaceActions,
63
67
  gbpPlaceDetails,
68
+ getAttributes,
64
69
  getCrawlIssues,
65
70
  getLinkCounts,
66
71
  getLodging,
@@ -71,6 +76,7 @@ import {
71
76
  gscCoverageSnapshots,
72
77
  gscSearchData,
73
78
  gscUrlInspections,
79
+ hashAttributes,
74
80
  hashLodging,
75
81
  hashPlaceDetails,
76
82
  insights,
@@ -79,6 +85,7 @@ import {
79
85
  isDuckdbInstalled,
80
86
  isValidReleaseId,
81
87
  listCachedReleases,
88
+ listLocations,
82
89
  listMonthlyKeywords,
83
90
  listPlaceActionLinks,
84
91
  loadDuckdb,
@@ -104,7 +111,7 @@ import {
104
111
  siteAuditPages,
105
112
  siteAuditSnapshots,
106
113
  usageCounters
107
- } from "./chunk-4KQYRCVC.js";
114
+ } from "./chunk-KBDQ5OE4.js";
108
115
  import {
109
116
  AGENT_MEMORY_VALUE_MAX_BYTES,
110
117
  AGENT_PROVIDER_IDS,
@@ -164,7 +171,7 @@ import {
164
171
  validationError,
165
172
  winnabilityClassLabel,
166
173
  withRetry
167
- } from "./chunk-WMBOHP7E.js";
174
+ } from "./chunk-AUQNKEHV.js";
168
175
 
169
176
  // src/telemetry.ts
170
177
  import crypto from "crypto";
@@ -3927,6 +3934,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
3927
3934
  if (locationRows.length === 0) {
3928
3935
  throw new Error("No selected GBP locations to sync. Discover and select locations first.");
3929
3936
  }
3937
+ locationRows = await refreshSelectedLocationProfiles(db, projectId, accessToken, locationRows);
3930
3938
  const daysOfMetrics = opts.daysOfMetrics ?? DEFAULT_DAYS_OF_METRICS;
3931
3939
  const monthsOfKeywords = opts.monthsOfKeywords ?? DEFAULT_MONTHS_OF_KEYWORDS;
3932
3940
  const metricsStart = daysAgo2(daysOfMetrics);
@@ -3943,7 +3951,7 @@ async function executeGbpSync(db, runId, projectId, opts) {
3943
3951
  const batch = locationRows.slice(i, i + LOCATION_CONCURRENCY);
3944
3952
  await Promise.all(batch.map(async (loc) => {
3945
3953
  try {
3946
- const [metricRows, keywordRows, placeActionRows, lodging, monthlyKeywordResults] = await Promise.all([
3954
+ const [metricRows, keywordRows, placeActionRows, lodging, attributes, monthlyKeywordResults] = await Promise.all([
3947
3955
  fetchDailyMetrics(accessToken, loc.locationName, {
3948
3956
  metrics: GBP_DAILY_METRICS,
3949
3957
  startDate: metricsStart,
@@ -3956,6 +3964,9 @@ async function executeGbpSync(db, runId, projectId, opts) {
3956
3964
  listPlaceActionLinks(accessToken, loc.locationName),
3957
3965
  // null when the location is not a lodging-category property.
3958
3966
  getLodging(accessToken, loc.locationName),
3967
+ // Owner-set attributes (any business category) — [] when the
3968
+ // location has none or there is no attributes resource (404).
3969
+ getAttributes(accessToken, loc.locationName),
3959
3970
  // Per-month keyword impressions for the accumulating monthly series.
3960
3971
  // One call per complete month (the endpoint aggregates a range into
3961
3972
  // a single figure, so month resolution requires month-sized calls).
@@ -3967,6 +3978,9 @@ async function executeGbpSync(db, runId, projectId, opts) {
3967
3978
  const lodgingHash = lodging ? hashLodging(lodging) : null;
3968
3979
  const latestLodging = lodging ? db.select().from(gbpLodgingSnapshots).where(and3(eq3(gbpLodgingSnapshots.projectId, projectId), eq3(gbpLodgingSnapshots.locationName, loc.locationName))).orderBy(desc(gbpLodgingSnapshots.syncedAt)).limit(1).get() : void 0;
3969
3980
  const lodgingChanged = lodging !== null && latestLodging?.contentHash !== lodgingHash;
3981
+ const attributesHash = hashAttributes(attributes);
3982
+ const latestAttributes = db.select().from(gbpAttributesSnapshots).where(and3(eq3(gbpAttributesSnapshots.projectId, projectId), eq3(gbpAttributesSnapshots.locationName, loc.locationName))).orderBy(desc(gbpAttributesSnapshots.syncedAt)).limit(1).get();
3983
+ const attributesChanged = latestAttributes?.contentHash !== attributesHash;
3970
3984
  let placeToWrite = null;
3971
3985
  let placeToTouch = null;
3972
3986
  if (placesTier !== "off" && placesApiKey && lodging !== null && loc.placeId) {
@@ -4070,6 +4084,20 @@ async function executeGbpSync(db, runId, projectId, opts) {
4070
4084
  } else if (lodging !== null && latestLodging) {
4071
4085
  tx.update(gbpLodgingSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq3(gbpLodgingSnapshots.id, latestLodging.id)).run();
4072
4086
  }
4087
+ if (attributesChanged) {
4088
+ tx.insert(gbpAttributesSnapshots).values({
4089
+ id: crypto5.randomUUID(),
4090
+ projectId,
4091
+ locationName: loc.locationName,
4092
+ contentHash: attributesHash,
4093
+ attributes,
4094
+ attributeCount: countAttributes(attributes),
4095
+ syncedAt: insertNow,
4096
+ syncRunId: runId
4097
+ }).run();
4098
+ } else if (latestAttributes) {
4099
+ tx.update(gbpAttributesSnapshots).set({ syncedAt: insertNow, syncRunId: runId }).where(eq3(gbpAttributesSnapshots.id, latestAttributes.id)).run();
4100
+ }
4073
4101
  if (placeToWrite) {
4074
4102
  tx.insert(gbpPlaceDetails).values({
4075
4103
  id: crypto5.randomUUID(),
@@ -4121,6 +4149,42 @@ async function executeGbpSync(db, runId, projectId, opts) {
4121
4149
  function monthKey(m) {
4122
4150
  return `${m.year}-${String(m.month).padStart(2, "0")}`;
4123
4151
  }
4152
+ async function refreshSelectedLocationProfiles(db, projectId, accessToken, locationRows) {
4153
+ const byAccount = /* @__PURE__ */ new Map();
4154
+ for (const row of locationRows) {
4155
+ const accountRows = byAccount.get(row.accountName) ?? [];
4156
+ accountRows.push(row);
4157
+ byAccount.set(row.accountName, accountRows);
4158
+ }
4159
+ const refreshedRows = /* @__PURE__ */ new Map();
4160
+ for (const [accountName, accountRows] of byAccount.entries()) {
4161
+ const remoteLocations = await listLocations(accessToken, accountName);
4162
+ const remoteByName = new Map(remoteLocations.map((loc) => [loc.name, loc]));
4163
+ const missing = accountRows.filter((row) => !remoteByName.has(row.locationName));
4164
+ if (missing.length > 0) {
4165
+ throw new Error(`Selected GBP location(s) no longer appear under ${accountName}: ${missing.map((row) => row.locationName).join(", ")}. Run "canonry gbp locations discover" to refresh the selection.`);
4166
+ }
4167
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4168
+ for (const row of accountRows) {
4169
+ const remote = remoteByName.get(row.locationName);
4170
+ const profile = buildLocationProfileFields(remote);
4171
+ const update = {
4172
+ accountName,
4173
+ displayName: remote.title ?? row.displayName,
4174
+ primaryCategoryDisplayName: remote.categories?.primaryCategory?.displayName ?? null,
4175
+ storefrontAddress: formatStorefrontAddress(remote),
4176
+ websiteUri: remote.websiteUri ?? null,
4177
+ placeId: remote.metadata?.placeId ?? null,
4178
+ mapsUri: remote.metadata?.mapsUri ?? null,
4179
+ ...profile,
4180
+ updatedAt
4181
+ };
4182
+ db.update(gbpLocations).set(update).where(and3(eq3(gbpLocations.projectId, projectId), eq3(gbpLocations.id, row.id))).run();
4183
+ refreshedRows.set(row.id, { ...row, ...update });
4184
+ }
4185
+ }
4186
+ return locationRows.map((row) => refreshedRows.get(row.id) ?? row);
4187
+ }
4124
4188
 
4125
4189
  // src/ads-sync.ts
4126
4190
  import crypto6 from "crypto";
@@ -6323,7 +6387,7 @@ function readStoredGroundingSources(rawResponse) {
6323
6387
  return result;
6324
6388
  }
6325
6389
  async function backfillInsightsCommand(project, opts) {
6326
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-QB6HQRWU.js");
6390
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4ATCJITP.js");
6327
6391
  const config = loadConfig();
6328
6392
  const db = createClient(config.database);
6329
6393
  migrate(db);
@@ -1005,6 +1005,23 @@ var gbpLodgingListResponseSchema = z8.object({
1005
1005
  lodging: z8.array(gbpLodgingDtoSchema),
1006
1006
  total: z8.number().int().nonnegative()
1007
1007
  });
1008
+ var gbpAttributeDtoSchema = z8.object({
1009
+ name: z8.string(),
1010
+ valueType: z8.string(),
1011
+ values: z8.array(z8.union([z8.boolean(), z8.string()])),
1012
+ uris: z8.array(z8.string())
1013
+ });
1014
+ var gbpAttributesDtoSchema = z8.object({
1015
+ locationName: z8.string(),
1016
+ /** Count of owner-set attributes (getAttributes returns only set ones). */
1017
+ attributeCount: z8.number().int().nonnegative(),
1018
+ syncedAt: z8.string(),
1019
+ attributes: z8.array(gbpAttributeDtoSchema)
1020
+ });
1021
+ var gbpAttributesListResponseSchema = z8.object({
1022
+ attributes: z8.array(gbpAttributesDtoSchema),
1023
+ total: z8.number().int().nonnegative()
1024
+ });
1008
1025
  var gbpPlaceDetailsDtoSchema = z8.object({
1009
1026
  locationName: z8.string(),
1010
1027
  placeId: z8.string(),
@@ -3249,6 +3266,7 @@ var projectOverviewInsightSchema = z24.object({
3249
3266
  "gbp-lodging-gap",
3250
3267
  "gbp-listing-discrepancy",
3251
3268
  "gbp-cta-gap",
3269
+ "gbp-description-missing",
3252
3270
  "gbp-metric-drop",
3253
3271
  "gbp-keyword-drop"
3254
3272
  ]),
@@ -3407,6 +3425,7 @@ var projectSearchInsightHitSchema = z24.object({
3407
3425
  "gbp-lodging-gap",
3408
3426
  "gbp-listing-discrepancy",
3409
3427
  "gbp-cta-gap",
3428
+ "gbp-description-missing",
3410
3429
  "gbp-metric-drop",
3411
3430
  "gbp-keyword-drop"
3412
3431
  ]),
@@ -5342,6 +5361,7 @@ export {
5342
5361
  gbpKeywordImpressionListResponseSchema,
5343
5362
  gbpPlaceActionListResponseSchema,
5344
5363
  gbpLodgingListResponseSchema,
5364
+ gbpAttributesListResponseSchema,
5345
5365
  gbpPlaceDetailsListResponseSchema,
5346
5366
  gbpSummaryDtoSchema,
5347
5367
  formatGbpMetricLabel,