@canonry/canonry 4.133.3 → 4.134.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/README.md +0 -13
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +18 -0
- package/assets/assets/{AuditHistoryPanel-DTHQGvkW.js → AuditHistoryPanel-DBUWbfNk.js} +1 -1
- package/assets/assets/{BacklinksPage-DuPI3wt9.js → BacklinksPage-BFm0VdUO.js} +1 -1
- package/assets/assets/{ChartPrimitives-T8mxOObc.js → ChartPrimitives-oV_nduTH.js} +1 -1
- package/assets/assets/{HistoryPage-T1W40xKG.js → HistoryPage-IF2FMem3.js} +1 -1
- package/assets/assets/{ProjectPage-DYwM8-Rp.js → ProjectPage-ClwxX9Ga.js} +2 -2
- package/assets/assets/{RunRow-nHr4wW9B.js → RunRow-De5zdch5.js} +1 -1
- package/assets/assets/{RunsPage-Cbusy_84.js → RunsPage-xtRF78r8.js} +1 -1
- package/assets/assets/{SettingsPage-BVcbl4gn.js → SettingsPage-B_GAxaWu.js} +1 -1
- package/assets/assets/{TrafficPage-CZ4NTWI6.js → TrafficPage-E8I5ineJ.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-xW-KvZY1.js → TrafficSourceDetailPage-DGk2MqSp.js} +1 -1
- package/assets/assets/{arrow-left-flskyFPK.js → arrow-left-D-4QMxi9.js} +1 -1
- package/assets/assets/{extract-error-message-BHfCTm0H.js → extract-error-message-DuqIFU8Z.js} +1 -1
- package/assets/assets/{index-CQzGDGMB.js → index-De1MKbc7.js} +88 -88
- package/assets/assets/{trash-2-LGfqtSzZ.js → trash-2-BVh__r6G.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-HENMGNLT.js → chunk-LIXWDIDR.js} +496 -3
- package/dist/{chunk-YRRQFOAH.js → chunk-PC7AYXND.js} +233 -2
- package/dist/{chunk-6ON6GUAL.js → chunk-TKXWTDV6.js} +68 -6
- package/dist/{chunk-A6BYMSZL.js → chunk-ZUQPHGEN.js} +45 -2
- package/dist/cli.js +68 -4
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-OSLFG5S2.js → intelligence-service-4XOFUN5E.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -7
|
@@ -47,8 +47,8 @@ function authInvalid() {
|
|
|
47
47
|
function forbidden(message = "Forbidden", details) {
|
|
48
48
|
return new AppError("FORBIDDEN", message, 403, details);
|
|
49
49
|
}
|
|
50
|
-
function quotaExceeded(metric) {
|
|
51
|
-
return new AppError("QUOTA_EXCEEDED", `Quota exceeded for ${metric}`, 429);
|
|
50
|
+
function quotaExceeded(metric, details) {
|
|
51
|
+
return new AppError("QUOTA_EXCEEDED", `Quota exceeded for ${metric}`, 429, details);
|
|
52
52
|
}
|
|
53
53
|
function providerError(message, details) {
|
|
54
54
|
return new AppError("PROVIDER_ERROR", message, 502, details);
|
|
@@ -960,6 +960,111 @@ function formatIsoDate(iso) {
|
|
|
960
960
|
return iso;
|
|
961
961
|
}
|
|
962
962
|
}
|
|
963
|
+
function zonedClockFormat(timeZone) {
|
|
964
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
965
|
+
timeZone,
|
|
966
|
+
year: "numeric",
|
|
967
|
+
month: "2-digit",
|
|
968
|
+
day: "2-digit",
|
|
969
|
+
hour: "2-digit",
|
|
970
|
+
minute: "2-digit",
|
|
971
|
+
second: "2-digit",
|
|
972
|
+
hourCycle: "h23"
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
function readZonedParts(format, date) {
|
|
976
|
+
const values = {};
|
|
977
|
+
for (const part of format.formatToParts(date)) values[part.type] = part.value;
|
|
978
|
+
return values;
|
|
979
|
+
}
|
|
980
|
+
function readZonedClock(format, date) {
|
|
981
|
+
const parts = readZonedParts(format, date);
|
|
982
|
+
const value = (raw) => {
|
|
983
|
+
if (raw === void 0) return null;
|
|
984
|
+
const parsed = Number(raw);
|
|
985
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
986
|
+
};
|
|
987
|
+
const year = value(parts.year);
|
|
988
|
+
const month = value(parts.month);
|
|
989
|
+
const day = value(parts.day);
|
|
990
|
+
const hour = value(parts.hour);
|
|
991
|
+
const minute = value(parts.minute);
|
|
992
|
+
const second = value(parts.second);
|
|
993
|
+
if (year === null || month === null || day === null) return null;
|
|
994
|
+
if (hour === null || minute === null || second === null) return null;
|
|
995
|
+
return { year, month, day, hour, minute, second };
|
|
996
|
+
}
|
|
997
|
+
function formatIsoDateInTimeZone(iso, timeZone) {
|
|
998
|
+
if (!iso) return formatIsoDate(iso);
|
|
999
|
+
const d = new Date(iso);
|
|
1000
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
1001
|
+
try {
|
|
1002
|
+
const parts = readZonedParts(zonedClockFormat(timeZone), d);
|
|
1003
|
+
const yyyy = parts.year;
|
|
1004
|
+
const mm = parts.month;
|
|
1005
|
+
const dd = parts.day;
|
|
1006
|
+
if (!yyyy || !mm || !dd) return formatIsoDate(iso);
|
|
1007
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
1008
|
+
} catch {
|
|
1009
|
+
return formatIsoDate(iso);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
function shiftIsoCalendarDate(isoDate, days) {
|
|
1013
|
+
if (!Number.isFinite(days)) return isoDate;
|
|
1014
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
|
|
1015
|
+
if (!match?.[1] || !match[2] || !match[3]) return isoDate;
|
|
1016
|
+
const shifted = new Date(Date.UTC(
|
|
1017
|
+
Number(match[1]),
|
|
1018
|
+
Number(match[2]) - 1,
|
|
1019
|
+
Number(match[3]) + Math.trunc(days)
|
|
1020
|
+
));
|
|
1021
|
+
if (Number.isNaN(shifted.getTime())) return isoDate;
|
|
1022
|
+
const yyyy = shifted.getUTCFullYear();
|
|
1023
|
+
const mm = String(shifted.getUTCMonth() + 1).padStart(2, "0");
|
|
1024
|
+
const dd = String(shifted.getUTCDate()).padStart(2, "0");
|
|
1025
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
1026
|
+
}
|
|
1027
|
+
function isoDateDaysBeforeInTimeZone(iso, days, timeZone) {
|
|
1028
|
+
return shiftIsoCalendarDate(formatIsoDateInTimeZone(iso, timeZone), -days);
|
|
1029
|
+
}
|
|
1030
|
+
var HOURS_PER_DAY = 24;
|
|
1031
|
+
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1032
|
+
function localHourExists(format, year, month, day, hour) {
|
|
1033
|
+
const asIfUtcMs = Date.UTC(year, month - 1, day, hour);
|
|
1034
|
+
for (const probeMs of [asIfUtcMs - ONE_DAY_MS, asIfUtcMs + ONE_DAY_MS]) {
|
|
1035
|
+
const probeClock = readZonedClock(format, new Date(probeMs));
|
|
1036
|
+
if (!probeClock) continue;
|
|
1037
|
+
const offsetMs = Date.UTC(
|
|
1038
|
+
probeClock.year,
|
|
1039
|
+
probeClock.month - 1,
|
|
1040
|
+
probeClock.day,
|
|
1041
|
+
probeClock.hour,
|
|
1042
|
+
probeClock.minute,
|
|
1043
|
+
probeClock.second
|
|
1044
|
+
) - probeMs;
|
|
1045
|
+
const candidate = readZonedClock(format, new Date(asIfUtcMs - offsetMs));
|
|
1046
|
+
if (!candidate) continue;
|
|
1047
|
+
if (candidate.year === year && candidate.month === month && candidate.day === day && candidate.hour === hour && candidate.minute === 0 && candidate.second === 0) return true;
|
|
1048
|
+
}
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
function startOfDayHourInTimeZone(isoDate, timeZone) {
|
|
1052
|
+
const hourBoundary = (hour) => `${isoDate}T${String(hour).padStart(2, "0")}`;
|
|
1053
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
|
|
1054
|
+
if (!match?.[1] || !match[2] || !match[3]) return hourBoundary(0);
|
|
1055
|
+
const year = Number(match[1]);
|
|
1056
|
+
const month = Number(match[2]);
|
|
1057
|
+
const day = Number(match[3]);
|
|
1058
|
+
try {
|
|
1059
|
+
const format = zonedClockFormat(timeZone);
|
|
1060
|
+
for (let hour = 0; hour < HOURS_PER_DAY; hour += 1) {
|
|
1061
|
+
if (localHourExists(format, year, month, day, hour)) return hourBoundary(hour);
|
|
1062
|
+
}
|
|
1063
|
+
return hourBoundary(0);
|
|
1064
|
+
} catch {
|
|
1065
|
+
return hourBoundary(0);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
963
1068
|
function formatDateRange(start, end) {
|
|
964
1069
|
if (!start && !end) return "";
|
|
965
1070
|
if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
|
|
@@ -6518,6 +6623,8 @@ var researchRunQuerySchema = z37.object({
|
|
|
6518
6623
|
groundingSources: z37.array(groundingSourceSchema),
|
|
6519
6624
|
citedDomains: z37.array(z37.string()),
|
|
6520
6625
|
searchQueries: z37.array(z37.string()),
|
|
6626
|
+
namedCompetitors: z37.array(z37.string()).default([]),
|
|
6627
|
+
citedCompetitorDomains: z37.array(z37.string()).default([]),
|
|
6521
6628
|
answerMentioned: z37.boolean().nullable(),
|
|
6522
6629
|
citationState: citationStateSchema.nullable(),
|
|
6523
6630
|
error: z37.string().nullable(),
|
|
@@ -7333,6 +7440,122 @@ var adsOperationReconcileResponseSchema = z38.object({
|
|
|
7333
7440
|
operation: adsOperationDtoSchema,
|
|
7334
7441
|
resolved: z38.boolean()
|
|
7335
7442
|
});
|
|
7443
|
+
var adsLiveDeliveryQuerySchema = z38.object({
|
|
7444
|
+
campaignId: z38.string().min(1).max(200).optional(),
|
|
7445
|
+
lookbackDays: z38.coerce.number().int().min(1).max(30).default(7)
|
|
7446
|
+
});
|
|
7447
|
+
var adsLiveEntityTypeSchema = z38.enum(["campaign", "ad_group", "ad"]);
|
|
7448
|
+
var AdsLiveEntityTypes = adsLiveEntityTypeSchema.enum;
|
|
7449
|
+
var adsLivePresenceSchema = z38.enum(["both", "live-only", "stored-only"]);
|
|
7450
|
+
var AdsLivePresences = adsLivePresenceSchema.enum;
|
|
7451
|
+
var adsLiveDeliveryBasisSchema = z38.literal("live-provider-read");
|
|
7452
|
+
var AdsLiveDeliveryBases = { liveProviderRead: "live-provider-read" };
|
|
7453
|
+
var adsLiveMetricRowSchema = z38.object({
|
|
7454
|
+
date: z38.string().nullable(),
|
|
7455
|
+
startTime: z38.number().nullable(),
|
|
7456
|
+
endTime: z38.number().nullable(),
|
|
7457
|
+
impressions: z38.number().nullable(),
|
|
7458
|
+
clicks: z38.number().nullable(),
|
|
7459
|
+
spend: z38.number().nullable(),
|
|
7460
|
+
conversions: z38.number().nullable(),
|
|
7461
|
+
ctr: z38.number().nullable(),
|
|
7462
|
+
cpc: z38.number().nullable(),
|
|
7463
|
+
cpm: z38.number().nullable()
|
|
7464
|
+
});
|
|
7465
|
+
var adsLiveMetricValuesSchema = z38.object({
|
|
7466
|
+
impressions: z38.number().int(),
|
|
7467
|
+
clicks: z38.number().int(),
|
|
7468
|
+
spendMicros: z38.number().int(),
|
|
7469
|
+
conversions: z38.number().int()
|
|
7470
|
+
});
|
|
7471
|
+
var adsLiveMetricDeltaSchema = z38.object({
|
|
7472
|
+
date: z38.string(),
|
|
7473
|
+
live: adsLiveMetricValuesSchema.nullable(),
|
|
7474
|
+
stored: adsLiveMetricValuesSchema.nullable(),
|
|
7475
|
+
drifted: z38.boolean()
|
|
7476
|
+
});
|
|
7477
|
+
var adsLiveFieldDeltaSchema = z38.object({
|
|
7478
|
+
field: z38.string(),
|
|
7479
|
+
live: z38.string().nullable(),
|
|
7480
|
+
stored: z38.string().nullable()
|
|
7481
|
+
});
|
|
7482
|
+
var adsLiveEntityStateSchema = z38.object({
|
|
7483
|
+
name: z38.string().nullable(),
|
|
7484
|
+
status: z38.string(),
|
|
7485
|
+
reviewStatus: z38.string().nullable(),
|
|
7486
|
+
mode: z38.string().nullable(),
|
|
7487
|
+
updatedAt: z38.number().nullable()
|
|
7488
|
+
});
|
|
7489
|
+
var adsStoredEntityStateSchema = z38.object({
|
|
7490
|
+
name: z38.string(),
|
|
7491
|
+
status: z38.string(),
|
|
7492
|
+
reviewStatus: z38.string().nullable(),
|
|
7493
|
+
upstreamUpdatedAt: z38.number().nullable(),
|
|
7494
|
+
syncedAt: z38.string()
|
|
7495
|
+
});
|
|
7496
|
+
var adsLiveEntityComparisonSchema = z38.object({
|
|
7497
|
+
entityType: adsLiveEntityTypeSchema,
|
|
7498
|
+
id: z38.string(),
|
|
7499
|
+
parentId: z38.string().nullable(),
|
|
7500
|
+
presence: adsLivePresenceSchema,
|
|
7501
|
+
live: adsLiveEntityStateSchema.nullable(),
|
|
7502
|
+
stored: adsStoredEntityStateSchema.nullable(),
|
|
7503
|
+
fieldDeltas: z38.array(adsLiveFieldDeltaSchema),
|
|
7504
|
+
/** Provider rows, unaggregated. Null for ads (no per-ad insights surface). */
|
|
7505
|
+
liveMetrics: z38.array(adsLiveMetricRowSchema).nullable(),
|
|
7506
|
+
metricDeltas: z38.array(adsLiveMetricDeltaSchema).nullable(),
|
|
7507
|
+
drifted: z38.boolean()
|
|
7508
|
+
});
|
|
7509
|
+
var adsLiveReadFailureSchema = z38.object({
|
|
7510
|
+
surface: z38.string(),
|
|
7511
|
+
entityId: z38.string().nullable(),
|
|
7512
|
+
upstreamStatus: z38.number().int().nullable()
|
|
7513
|
+
});
|
|
7514
|
+
var adsLiveDeliveryDtoSchema = z38.object({
|
|
7515
|
+
basis: adsLiveDeliveryBasisSchema,
|
|
7516
|
+
/** Instant the live read was issued; the metrics window is measured back from it. */
|
|
7517
|
+
fetchedAt: z38.string(),
|
|
7518
|
+
adAccountId: z38.string(),
|
|
7519
|
+
storedSnapshotSyncedAt: z38.string().nullable(),
|
|
7520
|
+
metricsWindow: z38.object({ lookbackDays: z38.number().int().positive() }),
|
|
7521
|
+
/**
|
|
7522
|
+
* Two different units live here, and confusing them understates the cost of
|
|
7523
|
+
* this endpoint by two orders of magnitude.
|
|
7524
|
+
*
|
|
7525
|
+
* A READER CALL is one logical list or insight read. Every list/insight
|
|
7526
|
+
* reader call is an auto-paginating walk, so one reader call can issue up to
|
|
7527
|
+
* `maxPagesPerReaderCall` upstream HTTP requests before the client gives up.
|
|
7528
|
+
* `maxUpstreamHttpRequests` is the honest worst-case ceiling in HTTP
|
|
7529
|
+
* requests (`maxReaderCalls * maxPagesPerReaderCall`, about 4000 at the
|
|
7530
|
+
* shipped defaults, not 40).
|
|
7531
|
+
*
|
|
7532
|
+
* `readerCalls` is an observed count of reader calls. There is deliberately
|
|
7533
|
+
* no observed HTTP-request count: the pagination happens inside the provider
|
|
7534
|
+
* client, below the injected reader seam, so the route cannot see it.
|
|
7535
|
+
*/
|
|
7536
|
+
bounds: z38.object({
|
|
7537
|
+
maxCampaigns: z38.number().int().positive(),
|
|
7538
|
+
maxAdGroupsPerCampaign: z38.number().int().positive(),
|
|
7539
|
+
maxAdsPerAdGroup: z38.number().int().positive(),
|
|
7540
|
+
/** Budget in logical reader calls, NOT in upstream HTTP requests. */
|
|
7541
|
+
maxReaderCalls: z38.number().int().positive(),
|
|
7542
|
+
/** Reader calls this read actually issued. Observed, not a bound. */
|
|
7543
|
+
readerCalls: z38.number().int().nonnegative(),
|
|
7544
|
+
/** Pages one paginated reader call may fetch before the client gives up. */
|
|
7545
|
+
maxPagesPerReaderCall: z38.number().int().positive(),
|
|
7546
|
+
/** Worst-case upstream HTTP requests. A documented bound, not an observation. */
|
|
7547
|
+
maxUpstreamHttpRequests: z38.number().int().positive(),
|
|
7548
|
+
truncated: z38.boolean()
|
|
7549
|
+
}),
|
|
7550
|
+
entities: z38.array(adsLiveEntityComparisonSchema),
|
|
7551
|
+
drift: z38.object({
|
|
7552
|
+
entitiesCompared: z38.number().int().nonnegative(),
|
|
7553
|
+
driftedEntities: z38.number().int().nonnegative(),
|
|
7554
|
+
statusDrifted: z38.number().int().nonnegative(),
|
|
7555
|
+
metricsDrifted: z38.number().int().nonnegative()
|
|
7556
|
+
}),
|
|
7557
|
+
errors: z38.array(adsLiveReadFailureSchema)
|
|
7558
|
+
});
|
|
7336
7559
|
function adsCtr(clicks, impressions) {
|
|
7337
7560
|
return impressions > 0 ? clicks / impressions : null;
|
|
7338
7561
|
}
|
|
@@ -7583,6 +7806,9 @@ export {
|
|
|
7583
7806
|
formatNumber,
|
|
7584
7807
|
formatDate,
|
|
7585
7808
|
formatIsoDate,
|
|
7809
|
+
formatIsoDateInTimeZone,
|
|
7810
|
+
isoDateDaysBeforeInTimeZone,
|
|
7811
|
+
startOfDayHourInTimeZone,
|
|
7586
7812
|
formatDateRange,
|
|
7587
7813
|
parseInclusiveEndMs,
|
|
7588
7814
|
deltaPercent,
|
|
@@ -7902,6 +8128,11 @@ export {
|
|
|
7902
8128
|
adsUnresolvedOperationListResponseSchema,
|
|
7903
8129
|
adsOperationReconcileRequestSchema,
|
|
7904
8130
|
adsOperationReconcileResponseSchema,
|
|
8131
|
+
adsLiveDeliveryQuerySchema,
|
|
8132
|
+
AdsLiveEntityTypes,
|
|
8133
|
+
AdsLivePresences,
|
|
8134
|
+
AdsLiveDeliveryBases,
|
|
8135
|
+
adsLiveDeliveryDtoSchema,
|
|
7905
8136
|
adsCtr,
|
|
7906
8137
|
adsCpcMicros,
|
|
7907
8138
|
dollarsToMicros,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZUQPHGEN.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
classifyAiUserFetch,
|
|
41
41
|
classifyCrawler,
|
|
42
42
|
competitors,
|
|
43
|
+
computeCitedCompetitorDomains,
|
|
43
44
|
computeCompetitorOverlap,
|
|
44
45
|
countAttributes,
|
|
45
46
|
countPopulatedGroups,
|
|
@@ -118,7 +119,7 @@ import {
|
|
|
118
119
|
siteAuditPages,
|
|
119
120
|
siteAuditSnapshots,
|
|
120
121
|
usageCounters
|
|
121
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-LIXWDIDR.js";
|
|
122
123
|
import {
|
|
123
124
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
124
125
|
AGENT_PROVIDER_IDS,
|
|
@@ -188,10 +189,11 @@ import {
|
|
|
188
189
|
serializeRunError,
|
|
189
190
|
skillsClientSchema,
|
|
190
191
|
splitList,
|
|
192
|
+
startOfDayHourInTimeZone,
|
|
191
193
|
validationError,
|
|
192
194
|
winnabilityClassLabel,
|
|
193
195
|
withRetry
|
|
194
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-PC7AYXND.js";
|
|
195
197
|
|
|
196
198
|
// src/telemetry.ts
|
|
197
199
|
import crypto from "crypto";
|
|
@@ -4877,14 +4879,22 @@ function accountHour(date, timezone) {
|
|
|
4877
4879
|
};
|
|
4878
4880
|
return `${value("year")}-${value("month")}-${value("day")}T${value("hour")}`;
|
|
4879
4881
|
}
|
|
4880
|
-
function trailingAdsInsightHourRange(now, timezone) {
|
|
4882
|
+
function trailingAdsInsightHourRange(now, timezone, lookbackMs = INSIGHTS_LOOKBACK_MS) {
|
|
4881
4883
|
return {
|
|
4882
4884
|
type: "hour_range",
|
|
4883
|
-
since: accountHour(new Date(now.getTime() -
|
|
4885
|
+
since: accountHour(new Date(now.getTime() - lookbackMs), timezone),
|
|
4884
4886
|
until: accountHour(now, timezone),
|
|
4885
4887
|
timezone
|
|
4886
4888
|
};
|
|
4887
4889
|
}
|
|
4890
|
+
function liveAdsInsightHourRange(request) {
|
|
4891
|
+
return {
|
|
4892
|
+
type: "hour_range",
|
|
4893
|
+
since: startOfDayHourInTimeZone(request.startDate, request.timezone),
|
|
4894
|
+
until: accountHour(new Date(request.fetchedAtMs), request.timezone),
|
|
4895
|
+
timezone: request.timezone
|
|
4896
|
+
};
|
|
4897
|
+
}
|
|
4888
4898
|
function toInsightUpserts(level, entityId, rows) {
|
|
4889
4899
|
const upserts = [];
|
|
4890
4900
|
for (const row of rows) {
|
|
@@ -7000,7 +7010,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
7000
7010
|
return result;
|
|
7001
7011
|
}
|
|
7002
7012
|
async function backfillInsightsCommand(project, opts) {
|
|
7003
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
7013
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-4XOFUN5E.js");
|
|
7004
7014
|
const config = loadConfig();
|
|
7005
7015
|
const db = createClient(config.database);
|
|
7006
7016
|
migrate(db);
|
|
@@ -9114,6 +9124,7 @@ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
9114
9124
|
CanonryMcpToolNames.canonry_ads_insights,
|
|
9115
9125
|
CanonryMcpToolNames.canonry_ads_summary,
|
|
9116
9126
|
CanonryMcpToolNames.canonry_ads_delivery_diagnostics,
|
|
9127
|
+
CanonryMcpToolNames.canonry_ads_live_delivery,
|
|
9117
9128
|
CanonryMcpToolNames.canonry_ads_operations_unresolved,
|
|
9118
9129
|
CanonryMcpToolNames.canonry_ads_operation_get,
|
|
9119
9130
|
CanonryMcpToolNames.canonry_ads_operation_reconcile,
|
|
@@ -11592,6 +11603,14 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
11592
11603
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
11593
11604
|
});
|
|
11594
11605
|
const normalized = provider.adapter.normalizeResult(raw);
|
|
11606
|
+
const namedCompetitors = extractRecommendedCompetitors(
|
|
11607
|
+
normalized.answerText,
|
|
11608
|
+
domains,
|
|
11609
|
+
normalized.citedDomains,
|
|
11610
|
+
competitorDomains,
|
|
11611
|
+
brands
|
|
11612
|
+
);
|
|
11613
|
+
const citedCompetitorDomains = computeCitedCompetitorDomains(normalized.citedDomains, competitorDomains);
|
|
11595
11614
|
const completed = db.update(researchRunQueries).set({
|
|
11596
11615
|
status: ResearchQueryStatuses.completed,
|
|
11597
11616
|
servedModel: raw.servedModel ?? null,
|
|
@@ -11599,6 +11618,8 @@ async function executeResearchRun(db, registry, runId, projectId) {
|
|
|
11599
11618
|
groundingSources: normalized.groundingSources,
|
|
11600
11619
|
citedDomains: normalized.citedDomains,
|
|
11601
11620
|
searchQueries: normalized.searchQueries,
|
|
11621
|
+
namedCompetitors,
|
|
11622
|
+
citedCompetitorDomains,
|
|
11602
11623
|
answerMentioned: determineAnswerMentioned(normalized.answerText, brands, domains),
|
|
11603
11624
|
citationState: determineCitationState(normalized, domains),
|
|
11604
11625
|
rawResponse: raw.rawResponse,
|
|
@@ -12204,6 +12225,42 @@ async function createServer(opts) {
|
|
|
12204
12225
|
activateAd: async (apiKey, id) => adsAdEntityResult(await activateAd(apiKey, id)),
|
|
12205
12226
|
pauseAd: async (apiKey, id) => adsAdEntityResult(await pauseAd(apiKey, id))
|
|
12206
12227
|
};
|
|
12228
|
+
const adsLiveEntity = (entity, extra) => ({
|
|
12229
|
+
id: entity.id,
|
|
12230
|
+
name: entity.name,
|
|
12231
|
+
status: entity.status,
|
|
12232
|
+
reviewStatus: extra?.reviewStatus ?? null,
|
|
12233
|
+
mode: extra?.mode ?? null,
|
|
12234
|
+
updatedAt: entity.updated_at
|
|
12235
|
+
});
|
|
12236
|
+
const adsLiveMetricRow = (row) => ({
|
|
12237
|
+
date: row.readable_time ?? null,
|
|
12238
|
+
startTime: row.start_time ?? null,
|
|
12239
|
+
endTime: row.end_time ?? null,
|
|
12240
|
+
impressions: row.impressions ?? null,
|
|
12241
|
+
clicks: row.clicks ?? null,
|
|
12242
|
+
// Provider units: the insights API returns decimal currency for spend.
|
|
12243
|
+
spend: row.spend ?? null,
|
|
12244
|
+
conversions: row.conversions ?? null,
|
|
12245
|
+
ctr: row.ctr ?? null,
|
|
12246
|
+
cpc: row.cpc ?? null,
|
|
12247
|
+
cpm: row.cpm ?? null
|
|
12248
|
+
});
|
|
12249
|
+
const adsLiveDeliveryReader = {
|
|
12250
|
+
listCampaigns: async (apiKey) => (await listCampaigns(apiKey)).map((campaign) => adsLiveEntity(campaign, { mode: campaign.mode })),
|
|
12251
|
+
listAdGroups: async (apiKey, campaignId) => (await listAdGroups(apiKey, campaignId)).map((adGroup) => adsLiveEntity(adGroup)),
|
|
12252
|
+
listAds: async (apiKey, adGroupId) => (await listAds(apiKey, adGroupId)).map(
|
|
12253
|
+
(ad) => adsLiveEntity(ad, { reviewStatus: ad.review_status ?? ad.review?.status ?? null })
|
|
12254
|
+
),
|
|
12255
|
+
campaignInsights: async (apiKey, campaignId, request) => (await getCampaignInsights(apiKey, campaignId, {
|
|
12256
|
+
fields: CAMPAIGN_INSIGHT_FIELDS,
|
|
12257
|
+
timeRanges: [liveAdsInsightHourRange(request)]
|
|
12258
|
+
})).map(adsLiveMetricRow),
|
|
12259
|
+
adGroupInsights: async (apiKey, adGroupId, request) => (await getAdGroupInsights(apiKey, adGroupId, {
|
|
12260
|
+
fields: AD_GROUP_INSIGHT_FIELDS,
|
|
12261
|
+
timeRanges: [liveAdsInsightHourRange(request)]
|
|
12262
|
+
})).map(adsLiveMetricRow)
|
|
12263
|
+
};
|
|
12207
12264
|
const scheduler = new Scheduler(opts.db, {
|
|
12208
12265
|
onRunCreated: (runId, projectId, providers2, location) => {
|
|
12209
12266
|
jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
|
|
@@ -12803,6 +12860,11 @@ async function createServer(opts) {
|
|
|
12803
12860
|
adsCredentialStore,
|
|
12804
12861
|
verifyAdsAccount,
|
|
12805
12862
|
adsReader,
|
|
12863
|
+
adsLiveDeliveryReader,
|
|
12864
|
+
// Reporting only: lets the route state the true upstream HTTP ceiling
|
|
12865
|
+
// (reader-call budget x pages per reader call) instead of implying that a
|
|
12866
|
+
// reader call is one request.
|
|
12867
|
+
adsLiveDeliveryMaxPagesPerReaderCall: OPENAI_ADS_MAX_PAGES,
|
|
12806
12868
|
adsOperator,
|
|
12807
12869
|
onAdsSyncRequested: (runId, projectId) => {
|
|
12808
12870
|
runAdsSync(runId, projectId);
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
trafficConnectWordpressRequestSchema,
|
|
43
43
|
trafficEventKindSchema,
|
|
44
44
|
trafficSeriesGranularitySchema
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-PC7AYXND.js";
|
|
46
46
|
|
|
47
47
|
// src/config.ts
|
|
48
48
|
import fs from "fs";
|
|
@@ -2739,6 +2739,18 @@ var getApiV1ProjectsByNameAdsDeliveryDiagnostics = (options) => {
|
|
|
2739
2739
|
...options
|
|
2740
2740
|
});
|
|
2741
2741
|
};
|
|
2742
|
+
var getApiV1ProjectsByNameAdsLiveDelivery = (options) => {
|
|
2743
|
+
return (options.client ?? client).get({
|
|
2744
|
+
security: [
|
|
2745
|
+
{
|
|
2746
|
+
scheme: "bearer",
|
|
2747
|
+
type: "http"
|
|
2748
|
+
}
|
|
2749
|
+
],
|
|
2750
|
+
url: "/api/v1/projects/{name}/ads/live-delivery",
|
|
2751
|
+
...options
|
|
2752
|
+
});
|
|
2753
|
+
};
|
|
2742
2754
|
var postApiV1ProjectsByNameBingConnect = (options) => {
|
|
2743
2755
|
return (options.client ?? client).post({
|
|
2744
2756
|
security: [
|
|
@@ -4871,6 +4883,18 @@ var ApiClient = class {
|
|
|
4871
4883
|
() => getApiV1ProjectsByNameAdsDeliveryDiagnostics({ client: this.heyClient, path: { name: project } })
|
|
4872
4884
|
);
|
|
4873
4885
|
}
|
|
4886
|
+
async getAdsLiveDelivery(project, opts) {
|
|
4887
|
+
return this.invoke(
|
|
4888
|
+
() => getApiV1ProjectsByNameAdsLiveDelivery({
|
|
4889
|
+
client: this.heyClient,
|
|
4890
|
+
path: { name: project },
|
|
4891
|
+
query: {
|
|
4892
|
+
...opts?.campaignId === void 0 ? {} : { campaignId: opts.campaignId },
|
|
4893
|
+
...opts?.lookbackDays === void 0 ? {} : { lookbackDays: opts.lookbackDays }
|
|
4894
|
+
}
|
|
4895
|
+
})
|
|
4896
|
+
);
|
|
4897
|
+
}
|
|
4874
4898
|
async getAdsOperation(project, operationKey) {
|
|
4875
4899
|
return this.invoke(
|
|
4876
4900
|
() => getApiV1ProjectsByNameAdsOperationsByOperationKey({
|
|
@@ -6169,6 +6193,11 @@ var adsInsightsInputSchema = z2.object({
|
|
|
6169
6193
|
var adsGeoSearchInputSchema = adsGeoSearchQuerySchema.extend({
|
|
6170
6194
|
project: projectNameSchema
|
|
6171
6195
|
});
|
|
6196
|
+
var adsLiveDeliveryInputSchema = z2.object({
|
|
6197
|
+
project: projectNameSchema,
|
|
6198
|
+
campaignId: z2.string().min(1).max(200).optional(),
|
|
6199
|
+
lookbackDays: z2.number().int().min(1).max(30).optional()
|
|
6200
|
+
});
|
|
6172
6201
|
var adsOperationInputSchema = z2.object({
|
|
6173
6202
|
project: projectNameSchema,
|
|
6174
6203
|
operationKey: z2.string().min(8).max(128)
|
|
@@ -7710,7 +7739,7 @@ var canonryMcpTools = [
|
|
|
7710
7739
|
defineTool({
|
|
7711
7740
|
name: "canonry_research_run_get",
|
|
7712
7741
|
title: "Get research query run",
|
|
7713
|
-
description: "Get
|
|
7742
|
+
description: "Get saved per-query answers, source links, cited domains, answer-text named competitors, cited competitor domains, and independent mention/citation results for one research run. It is read-only and does not promote or track any query.",
|
|
7714
7743
|
access: "read",
|
|
7715
7744
|
tier: "discovery",
|
|
7716
7745
|
inputSchema: researchRunIdInputSchema,
|
|
@@ -7940,6 +7969,20 @@ var canonryMcpTools = [
|
|
|
7940
7969
|
openApiOperations: ["GET /api/v1/projects/{name}/ads/delivery-diagnostics"],
|
|
7941
7970
|
handler: (client2, input) => client2.getAdsDeliveryDiagnostics(input.project)
|
|
7942
7971
|
}),
|
|
7972
|
+
defineTool({
|
|
7973
|
+
name: "canonry_ads_live_delivery",
|
|
7974
|
+
title: "Live ads read with stored-snapshot delta",
|
|
7975
|
+
description: "Call the OpenAI Ads API right now and return the provider's current status and metrics per campaign / ad group / ad, unaggregated, alongside the stored snapshot values and an explicit per-entity delta. Use this when stored data is suspected of being stale or contradicted by the advertiser UI. Read-only: it mutates nothing and does not wait for a sync. The walk is bounded and one project may issue at most one live read per minute, so prefer canonry_ads_delivery_diagnostics for routine checks.",
|
|
7976
|
+
access: "read",
|
|
7977
|
+
tier: "ads",
|
|
7978
|
+
inputSchema: adsLiveDeliveryInputSchema,
|
|
7979
|
+
annotations: readAnnotations(),
|
|
7980
|
+
openApiOperations: ["GET /api/v1/projects/{name}/ads/live-delivery"],
|
|
7981
|
+
handler: (client2, input) => client2.getAdsLiveDelivery(input.project, {
|
|
7982
|
+
campaignId: input.campaignId,
|
|
7983
|
+
lookbackDays: input.lookbackDays
|
|
7984
|
+
})
|
|
7985
|
+
}),
|
|
7943
7986
|
defineTool({
|
|
7944
7987
|
name: "canonry_ads_operations_unresolved",
|
|
7945
7988
|
title: "List unresolved ads mutation receipts",
|
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-TKXWTDV6.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-ZUQPHGEN.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,10 +54,11 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-LIXWDIDR.js";
|
|
58
58
|
import {
|
|
59
59
|
AdsDeliverySnapshotStatuses,
|
|
60
60
|
AdsHistoricalCampaignRollupStatuses,
|
|
61
|
+
AdsLiveEntityTypes,
|
|
61
62
|
AdsOperationKinds,
|
|
62
63
|
AdsOperationStates,
|
|
63
64
|
AdsOperationStepStates,
|
|
@@ -99,7 +100,7 @@ import {
|
|
|
99
100
|
providerQuotaPolicySchema,
|
|
100
101
|
resolveProviderInput,
|
|
101
102
|
winnabilityClassSchema
|
|
102
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-PC7AYXND.js";
|
|
103
104
|
|
|
104
105
|
// src/cli.ts
|
|
105
106
|
import { pathToFileURL } from "url";
|
|
@@ -2692,6 +2693,12 @@ function printDetail(project, detail, format) {
|
|
|
2692
2693
|
console.log(` ${query.query.slice(0, 58).padEnd(58)} ${query.status.padEnd(10)} ${cited.padEnd(5)} ${mentioned}`);
|
|
2693
2694
|
if (query.error) console.log(` Error: ${query.error}`);
|
|
2694
2695
|
if (query.answerText) console.log(` Answer: ${query.answerText}`);
|
|
2696
|
+
if (query.namedCompetitors.length > 0) {
|
|
2697
|
+
console.log(` Named competitors: ${query.namedCompetitors.join(", ")}`);
|
|
2698
|
+
}
|
|
2699
|
+
if (query.citedCompetitorDomains.length > 0) {
|
|
2700
|
+
console.log(` Cited competitor domains: ${query.citedCompetitorDomains.join(", ")}`);
|
|
2701
|
+
}
|
|
2695
2702
|
if (query.groundingSources.length > 0) {
|
|
2696
2703
|
console.log(" Sources:");
|
|
2697
2704
|
for (const source of query.groundingSources) {
|
|
@@ -3414,6 +3421,42 @@ async function adsDeliveryDiagnostics(project, opts) {
|
|
|
3414
3421
|
}
|
|
3415
3422
|
}
|
|
3416
3423
|
}
|
|
3424
|
+
async function adsLiveDelivery(project, opts) {
|
|
3425
|
+
const result = await getClient6().getAdsLiveDelivery(project, {
|
|
3426
|
+
campaignId: opts?.campaignId,
|
|
3427
|
+
lookbackDays: opts?.lookbackDays
|
|
3428
|
+
});
|
|
3429
|
+
if (isMachineFormat(opts?.format)) {
|
|
3430
|
+
console.log(JSON.stringify(result, null, 2));
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
console.log(`Read at: ${result.fetchedAt} (live provider read)`);
|
|
3434
|
+
console.log(`Ad account: ${result.adAccountId}`);
|
|
3435
|
+
console.log(`Snapshot: last synced ${result.storedSnapshotSyncedAt ?? "never"}`);
|
|
3436
|
+
console.log(`Metrics: last ${result.metricsWindow.lookbackDays}d, as the provider reported them`);
|
|
3437
|
+
console.log(`Provider: ${result.bounds.readerCalls}/${result.bounds.maxReaderCalls} reader calls, up to ${result.bounds.maxUpstreamHttpRequests} upstream HTTP requests${result.bounds.truncated ? " (TRUNCATED, walk hit a cap)" : ""}`);
|
|
3438
|
+
console.log(`Drift: ${result.drift.driftedEntities}/${result.drift.entitiesCompared} entities (${result.drift.statusDrifted} status, ${result.drift.metricsDrifted} metrics)`);
|
|
3439
|
+
for (const failure of result.errors) {
|
|
3440
|
+
console.log(`Read failed: ${failure.surface}${failure.entityId ? ` ${failure.entityId}` : ""}${failure.upstreamStatus === null ? "" : ` [HTTP ${failure.upstreamStatus}]`}`);
|
|
3441
|
+
}
|
|
3442
|
+
for (const entity of result.entities) {
|
|
3443
|
+
const indent = entity.entityType === AdsLiveEntityTypes.campaign ? "" : entity.entityType === AdsLiveEntityTypes.ad_group ? " " : " ";
|
|
3444
|
+
const label = entity.live?.name ?? entity.stored?.name ?? entity.id;
|
|
3445
|
+
const liveStatus = entity.live === null ? "absent upstream" : entity.live.status;
|
|
3446
|
+
const storedStatus = entity.stored === null ? "absent locally" : entity.stored.status;
|
|
3447
|
+
const marker = entity.drifted ? "DRIFT" : "match";
|
|
3448
|
+
console.log(`${indent}${label} [${entity.entityType}]: live ${liveStatus} / stored ${storedStatus} (${marker})`);
|
|
3449
|
+
for (const delta of entity.fieldDeltas) {
|
|
3450
|
+
console.log(`${indent} ${delta.field}: live ${delta.live ?? "none"} / stored ${delta.stored ?? "none"}`);
|
|
3451
|
+
}
|
|
3452
|
+
for (const delta of entity.metricDeltas ?? []) {
|
|
3453
|
+
if (!delta.drifted) continue;
|
|
3454
|
+
const live = delta.live === null ? "no provider row" : `${delta.live.impressions} impr / ${delta.live.clicks} clicks / ${formatMicros(delta.live.spendMicros)}`;
|
|
3455
|
+
const stored = delta.stored === null ? "no stored row" : `${delta.stored.impressions} impr / ${delta.stored.clicks} clicks / ${formatMicros(delta.stored.spendMicros)}`;
|
|
3456
|
+
console.log(`${indent} ${delta.date}: live ${live} / stored ${stored}`);
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3417
3460
|
|
|
3418
3461
|
// src/cli-commands/ads.ts
|
|
3419
3462
|
var ADS_CLI_COMMANDS = [
|
|
@@ -3770,6 +3813,27 @@ Usage: ${usage}`, {
|
|
|
3770
3813
|
await adsSummary(project, { format: input.format });
|
|
3771
3814
|
}
|
|
3772
3815
|
},
|
|
3816
|
+
{
|
|
3817
|
+
path: ["ads", "live-delivery"],
|
|
3818
|
+
usage: "canonry ads live-delivery <project> [--campaign <id>] [--lookback-days <n>] [--format json]",
|
|
3819
|
+
options: {
|
|
3820
|
+
campaign: stringOption(),
|
|
3821
|
+
"lookback-days": stringOption()
|
|
3822
|
+
},
|
|
3823
|
+
run: async (input) => {
|
|
3824
|
+
const usage = "canonry ads live-delivery <project> [--campaign <id>] [--lookback-days <n>] [--format json]";
|
|
3825
|
+
const project = requireProject(input, "ads.live-delivery", usage);
|
|
3826
|
+
await adsLiveDelivery(project, {
|
|
3827
|
+
campaignId: getString(input.values, "campaign"),
|
|
3828
|
+
lookbackDays: parseIntegerOption(input, "lookback-days", {
|
|
3829
|
+
command: "ads.live-delivery",
|
|
3830
|
+
message: "--lookback-days must be an integer from 1 to 30",
|
|
3831
|
+
usage
|
|
3832
|
+
}),
|
|
3833
|
+
format: input.format
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
},
|
|
3773
3837
|
{
|
|
3774
3838
|
path: ["ads", "delivery-diagnostics"],
|
|
3775
3839
|
usage: "canonry ads delivery-diagnostics <project> [--format json]",
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TKXWTDV6.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-ZUQPHGEN.js";
|
|
7
|
+
import "./chunk-LIXWDIDR.js";
|
|
8
|
+
import "./chunk-PC7AYXND.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-ZUQPHGEN.js";
|
|
7
7
|
import {
|
|
8
8
|
isReadOnlyKey
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PC7AYXND.js";
|
|
10
10
|
|
|
11
11
|
// src/mcp/cli.ts
|
|
12
12
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|