@canonry/canonry 4.117.2 → 4.123.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 +9 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +3 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +161 -2
- package/assets/assets/AuditHistoryPanel-BVQMDIhQ.js +1 -0
- package/assets/assets/{BacklinksPage-CschVQxC.js → BacklinksPage-nrHT9VMG.js} +1 -1
- package/assets/assets/ChartPrimitives-_8qLsQzI.js +1 -0
- package/assets/assets/HistoryPage-DqsKKdyy.js +1 -0
- package/assets/assets/ProjectPage-GyKoQUv_.js +7 -0
- package/assets/assets/{RunRow-ggNGlwdN.js → RunRow-BRZZcSjQ.js} +1 -1
- package/assets/assets/RunsPage-24aI8DKF.js +1 -0
- package/assets/assets/{SettingsPage-Cy9SlBJX.js → SettingsPage-BaNeTG1i.js} +1 -1
- package/assets/assets/{TrafficPage-1S2PzSHB.js → TrafficPage-ByvcLSjv.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CqXCoaod.js → TrafficSourceDetailPage-B-siTXVg.js} +1 -1
- package/assets/assets/{arrow-left-kCK5M8Bp.js → arrow-left-CBh1cZNH.js} +1 -1
- package/assets/assets/{extract-error-message-C82LAW6q.js → extract-error-message-DJZCPudR.js} +1 -1
- package/assets/assets/index-jymsMBpZ.css +1 -0
- package/assets/assets/index-mzLganDP.js +210 -0
- package/assets/assets/{trash-2-8Pe4v1T0.js → trash-2-Bh-Yvkgc.js} +1 -1
- package/assets/assets/vendor-recharts-DSxTZhaH.js +36 -0
- package/assets/index.html +3 -3
- package/dist/{chunk-BMPIV35C.js → chunk-4AZ7VES6.js} +5726 -697
- package/dist/{chunk-4PMMEFME.js → chunk-HJIIJI7K.js} +816 -18
- package/dist/{chunk-F2SRZJMY.js → chunk-JYBNBK25.js} +627 -22
- package/dist/{chunk-EZME2J2G.js → chunk-MMFSI3OG.js} +3315 -2191
- package/dist/cli.js +861 -109
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-GNOE4J35.js → intelligence-service-K35EN43I.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +12 -12
- package/assets/assets/ChartPrimitives-D4782Ifx.js +0 -1
- package/assets/assets/ProjectPage-BSGTpBUJ.js +0 -7
- package/assets/assets/RunsPage-Bfp0K6Ry.js +0 -1
- package/assets/assets/index-DrDNyUhv.css +0 -1
- package/assets/assets/index-i-t64RXw.js +0 -210
- package/assets/assets/vendor-recharts-C9EZkgbP.js +0 -36
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
loadConfig,
|
|
11
11
|
loadConfigRaw,
|
|
12
12
|
saveConfigPatch
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HJIIJI7K.js";
|
|
14
14
|
import {
|
|
15
15
|
CC_CACHE_DIR,
|
|
16
16
|
DUCKDB_SPEC,
|
|
@@ -115,12 +115,14 @@ import {
|
|
|
115
115
|
siteAuditPages,
|
|
116
116
|
siteAuditSnapshots,
|
|
117
117
|
usageCounters
|
|
118
|
-
} from "./chunk-
|
|
118
|
+
} from "./chunk-4AZ7VES6.js";
|
|
119
119
|
import {
|
|
120
120
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
121
121
|
AGENT_PROVIDER_IDS,
|
|
122
122
|
AI_ENGINE_DOMAINS,
|
|
123
123
|
AI_ENGINE_SELF_DOMAINS,
|
|
124
|
+
AdsAdGroupBillingEventTypes,
|
|
125
|
+
AdsCampaignBiddingTypes,
|
|
124
126
|
AgentProviderIds,
|
|
125
127
|
BacklinkSources,
|
|
126
128
|
CcReleaseSyncStatuses,
|
|
@@ -142,6 +144,10 @@ import {
|
|
|
142
144
|
TrafficSourceTypes,
|
|
143
145
|
VERTEX_AI_SEARCH_PROXY_DOMAIN,
|
|
144
146
|
actionConfidenceLabel,
|
|
147
|
+
adsAccountDtoSchema,
|
|
148
|
+
adsConversionEventSettingListResponseSchema,
|
|
149
|
+
adsConversionPixelListResponseSchema,
|
|
150
|
+
adsGeoSearchResponseSchema,
|
|
145
151
|
agentBusy,
|
|
146
152
|
agentMemoryDeleteRequestSchema,
|
|
147
153
|
agentMemoryUpsertRequestSchema,
|
|
@@ -166,6 +172,7 @@ import {
|
|
|
166
172
|
isGhostTelemetryEvent,
|
|
167
173
|
isRetryableHttpError,
|
|
168
174
|
normalizeIdTokens,
|
|
175
|
+
normalizeServedModel,
|
|
169
176
|
normalizeUrlPath,
|
|
170
177
|
notFound,
|
|
171
178
|
parseOriginList,
|
|
@@ -178,7 +185,7 @@ import {
|
|
|
178
185
|
validationError,
|
|
179
186
|
winnabilityClassLabel,
|
|
180
187
|
withRetry
|
|
181
|
-
} from "./chunk-
|
|
188
|
+
} from "./chunk-MMFSI3OG.js";
|
|
182
189
|
|
|
183
190
|
// src/telemetry.ts
|
|
184
191
|
import crypto from "crypto";
|
|
@@ -594,6 +601,7 @@ async function executeTrackedQuery(input) {
|
|
|
594
601
|
provider: "gemini",
|
|
595
602
|
rawResponse,
|
|
596
603
|
model,
|
|
604
|
+
servedModel: extractServedModel(rawResponse),
|
|
597
605
|
groundingSources: parsed.groundingSources,
|
|
598
606
|
searchQueries: parsed.searchQueries
|
|
599
607
|
};
|
|
@@ -619,6 +627,9 @@ function normalizeResult(raw) {
|
|
|
619
627
|
function hasParsedResponseContent(rawResponse) {
|
|
620
628
|
return Array.isArray(rawResponse.candidates) && rawResponse.candidates.length > 0;
|
|
621
629
|
}
|
|
630
|
+
function extractServedModel(rawResponse) {
|
|
631
|
+
return normalizeServedModel(rawResponse.modelVersion);
|
|
632
|
+
}
|
|
622
633
|
function reparseStoredResult(rawResponse) {
|
|
623
634
|
const groundingSources = extractGroundingMetadataFromRaw(rawResponse);
|
|
624
635
|
const searchQueries = extractSearchQueriesFromRaw(rawResponse);
|
|
@@ -767,7 +778,11 @@ function responseToRecord(response) {
|
|
|
767
778
|
}));
|
|
768
779
|
return {
|
|
769
780
|
candidates: candidates ?? [],
|
|
770
|
-
usageMetadata: response.usageMetadata ?? null
|
|
781
|
+
usageMetadata: response.usageMetadata ?? null,
|
|
782
|
+
// Gemini reports the identity it actually served here — the only place it
|
|
783
|
+
// appears. Preserved so `servedModel` can be read back off a stored snapshot.
|
|
784
|
+
modelVersion: response.modelVersion ?? null,
|
|
785
|
+
responseId: response.responseId ?? null
|
|
771
786
|
};
|
|
772
787
|
} catch {
|
|
773
788
|
return { error: "failed to serialize response" };
|
|
@@ -884,6 +899,7 @@ var geminiAdapter = {
|
|
|
884
899
|
provider: "gemini",
|
|
885
900
|
rawResponse: raw.rawResponse,
|
|
886
901
|
model: raw.model,
|
|
902
|
+
servedModel: raw.servedModel,
|
|
887
903
|
groundingSources: raw.groundingSources,
|
|
888
904
|
searchQueries: raw.searchQueries
|
|
889
905
|
};
|
|
@@ -1006,6 +1022,7 @@ async function executeTrackedQuery2(input) {
|
|
|
1006
1022
|
provider: "openai",
|
|
1007
1023
|
rawResponse,
|
|
1008
1024
|
model,
|
|
1025
|
+
servedModel: extractServedModel2(rawResponse),
|
|
1009
1026
|
groundingSources: parsed.groundingSources,
|
|
1010
1027
|
searchQueries: parsed.searchQueries
|
|
1011
1028
|
};
|
|
@@ -1031,6 +1048,9 @@ function normalizeResult2(raw) {
|
|
|
1031
1048
|
function hasParsedResponseContent2(rawResponse) {
|
|
1032
1049
|
return Array.isArray(rawResponse.output) && rawResponse.output.length > 0;
|
|
1033
1050
|
}
|
|
1051
|
+
function extractServedModel2(rawResponse) {
|
|
1052
|
+
return normalizeServedModel(rawResponse.model);
|
|
1053
|
+
}
|
|
1034
1054
|
function reparseStoredResult2(rawResponse) {
|
|
1035
1055
|
const groundingSources = extractGroundingSourcesFromRaw(rawResponse);
|
|
1036
1056
|
const searchQueries = extractSearchQueriesFromRaw2(rawResponse);
|
|
@@ -1228,6 +1248,7 @@ var openaiAdapter = {
|
|
|
1228
1248
|
provider: "openai",
|
|
1229
1249
|
rawResponse: raw.rawResponse,
|
|
1230
1250
|
model: raw.model,
|
|
1251
|
+
servedModel: raw.servedModel,
|
|
1231
1252
|
groundingSources: raw.groundingSources,
|
|
1232
1253
|
searchQueries: raw.searchQueries
|
|
1233
1254
|
};
|
|
@@ -1365,6 +1386,7 @@ async function executeTrackedQuery3(input) {
|
|
|
1365
1386
|
provider: "claude",
|
|
1366
1387
|
rawResponse,
|
|
1367
1388
|
model,
|
|
1389
|
+
servedModel: extractServedModel3(rawResponse),
|
|
1368
1390
|
groundingSources: parsed.groundingSources,
|
|
1369
1391
|
searchQueries: parsed.searchQueries
|
|
1370
1392
|
};
|
|
@@ -1390,6 +1412,9 @@ function normalizeResult3(raw) {
|
|
|
1390
1412
|
function hasParsedResponseContent3(rawResponse) {
|
|
1391
1413
|
return Array.isArray(rawResponse.content) && rawResponse.content.length > 0;
|
|
1392
1414
|
}
|
|
1415
|
+
function extractServedModel3(rawResponse) {
|
|
1416
|
+
return normalizeServedModel(rawResponse.model);
|
|
1417
|
+
}
|
|
1393
1418
|
function reparseStoredResult3(rawResponse) {
|
|
1394
1419
|
const groundingSources = extractGroundingSourcesFromRaw2(rawResponse);
|
|
1395
1420
|
const searchQueries = extractSearchQueriesFromRaw3(rawResponse);
|
|
@@ -1589,6 +1614,7 @@ var claudeAdapter = {
|
|
|
1589
1614
|
provider: "claude",
|
|
1590
1615
|
rawResponse: raw.rawResponse,
|
|
1591
1616
|
model: raw.model,
|
|
1617
|
+
servedModel: raw.servedModel,
|
|
1592
1618
|
groundingSources: raw.groundingSources,
|
|
1593
1619
|
searchQueries: raw.searchQueries
|
|
1594
1620
|
};
|
|
@@ -1704,10 +1730,12 @@ async function executeTrackedQuery4(input) {
|
|
|
1704
1730
|
]
|
|
1705
1731
|
})
|
|
1706
1732
|
);
|
|
1733
|
+
const rawResponse = responseToRecord4(response);
|
|
1707
1734
|
return {
|
|
1708
1735
|
provider: "local",
|
|
1709
|
-
rawResponse
|
|
1736
|
+
rawResponse,
|
|
1710
1737
|
model,
|
|
1738
|
+
servedModel: extractServedModel4(rawResponse),
|
|
1711
1739
|
groundingSources: [],
|
|
1712
1740
|
searchQueries: []
|
|
1713
1741
|
};
|
|
@@ -1771,6 +1799,9 @@ function extractDomainMentions(text) {
|
|
|
1771
1799
|
}
|
|
1772
1800
|
return [...domains];
|
|
1773
1801
|
}
|
|
1802
|
+
function extractServedModel4(rawResponse) {
|
|
1803
|
+
return normalizeServedModel(rawResponse.model);
|
|
1804
|
+
}
|
|
1774
1805
|
function responseToRecord4(response) {
|
|
1775
1806
|
try {
|
|
1776
1807
|
return JSON.parse(JSON.stringify(response));
|
|
@@ -1830,6 +1861,7 @@ var localAdapter = {
|
|
|
1830
1861
|
provider: "local",
|
|
1831
1862
|
rawResponse: raw.rawResponse,
|
|
1832
1863
|
model: raw.model,
|
|
1864
|
+
servedModel: raw.servedModel,
|
|
1833
1865
|
groundingSources: raw.groundingSources,
|
|
1834
1866
|
searchQueries: raw.searchQueries
|
|
1835
1867
|
};
|
|
@@ -2505,6 +2537,7 @@ async function executeTrackedQuery5(input) {
|
|
|
2505
2537
|
provider: "perplexity",
|
|
2506
2538
|
rawResponse,
|
|
2507
2539
|
model,
|
|
2540
|
+
servedModel: extractServedModel5(rawResponse),
|
|
2508
2541
|
groundingSources: parsed.groundingSources,
|
|
2509
2542
|
searchQueries: parsed.searchQueries
|
|
2510
2543
|
};
|
|
@@ -2535,6 +2568,9 @@ function hasParsedResponseContent4(rawResponse) {
|
|
|
2535
2568
|
if (!nestedResponse) return false;
|
|
2536
2569
|
return Array.isArray(nestedResponse.choices) && nestedResponse.choices.length > 0 || Array.isArray(nestedResponse.search_results) && nestedResponse.search_results.length > 0 || Array.isArray(nestedResponse.citations) && nestedResponse.citations.length > 0;
|
|
2537
2570
|
}
|
|
2571
|
+
function extractServedModel5(rawResponse) {
|
|
2572
|
+
return normalizeServedModel(rawResponse.model);
|
|
2573
|
+
}
|
|
2538
2574
|
function reparseStoredResult4(rawResponse) {
|
|
2539
2575
|
const groundingSources = extractGroundingSources(rawResponse);
|
|
2540
2576
|
return {
|
|
@@ -2725,6 +2761,7 @@ var perplexityAdapter = {
|
|
|
2725
2761
|
provider: "perplexity",
|
|
2726
2762
|
rawResponse: raw.rawResponse,
|
|
2727
2763
|
model: raw.model,
|
|
2764
|
+
servedModel: raw.servedModel,
|
|
2728
2765
|
groundingSources: raw.groundingSources,
|
|
2729
2766
|
searchQueries: raw.searchQueries
|
|
2730
2767
|
};
|
|
@@ -3303,7 +3340,10 @@ var JobRunner = class {
|
|
|
3303
3340
|
}
|
|
3304
3341
|
}
|
|
3305
3342
|
const projectProviders = providerOverride ?? project.providers;
|
|
3306
|
-
activeProviders = this.registry.getForProject(projectProviders)
|
|
3343
|
+
activeProviders = this.registry.getForProject(projectProviders).map((entry) => {
|
|
3344
|
+
const model = project.providerModels[entry.adapter.name];
|
|
3345
|
+
return model === void 0 ? entry : { ...entry, config: { ...entry.config, model } };
|
|
3346
|
+
});
|
|
3307
3347
|
if (activeProviders.length === 0) {
|
|
3308
3348
|
throw new Error("No providers configured. Add at least one provider API key.");
|
|
3309
3349
|
}
|
|
@@ -3406,6 +3446,7 @@ var JobRunner = class {
|
|
|
3406
3446
|
queryText: q.query,
|
|
3407
3447
|
provider: providerName,
|
|
3408
3448
|
model: raw.model,
|
|
3449
|
+
servedModel: raw.servedModel ?? null,
|
|
3409
3450
|
citationState,
|
|
3410
3451
|
answerMentioned,
|
|
3411
3452
|
answerText: normalized.answerText,
|
|
@@ -3416,6 +3457,7 @@ var JobRunner = class {
|
|
|
3416
3457
|
screenshotPath: screenshotRelPath,
|
|
3417
3458
|
rawResponse: JSON.stringify({
|
|
3418
3459
|
model: raw.model,
|
|
3460
|
+
servedModel: raw.servedModel ?? null,
|
|
3419
3461
|
groundingSources: normalized.groundingSources,
|
|
3420
3462
|
searchQueries: normalized.searchQueries,
|
|
3421
3463
|
apiResponse: raw.rawResponse
|
|
@@ -3430,6 +3472,7 @@ var JobRunner = class {
|
|
|
3430
3472
|
queryText: q.query,
|
|
3431
3473
|
provider: providerName,
|
|
3432
3474
|
model: raw.model,
|
|
3475
|
+
servedModel: raw.servedModel ?? null,
|
|
3433
3476
|
citationState,
|
|
3434
3477
|
answerMentioned,
|
|
3435
3478
|
answerText: normalized.answerText,
|
|
@@ -3439,6 +3482,7 @@ var JobRunner = class {
|
|
|
3439
3482
|
location: runLocation?.label ?? null,
|
|
3440
3483
|
rawResponse: JSON.stringify({
|
|
3441
3484
|
model: raw.model,
|
|
3485
|
+
servedModel: raw.servedModel ?? null,
|
|
3442
3486
|
groundingSources: normalized.groundingSources,
|
|
3443
3487
|
searchQueries: normalized.searchQueries,
|
|
3444
3488
|
apiResponse: raw.rawResponse
|
|
@@ -4241,6 +4285,15 @@ var OPENAI_ADS_REQUEST_TIMEOUT_MS = 3e4;
|
|
|
4241
4285
|
var OPENAI_ADS_MAX_PAGES = 100;
|
|
4242
4286
|
|
|
4243
4287
|
// ../integration-openai-ads/src/types.ts
|
|
4288
|
+
var OpenAiAdsWriteStatuses = {
|
|
4289
|
+
active: "active",
|
|
4290
|
+
paused: "paused"
|
|
4291
|
+
};
|
|
4292
|
+
var OpenAiAdsBiddingTypes = AdsCampaignBiddingTypes;
|
|
4293
|
+
var OpenAiAdsBillingEventTypes = AdsAdGroupBillingEventTypes;
|
|
4294
|
+
var OpenAiAdsCreativeTypes = {
|
|
4295
|
+
chatCard: "chat_card"
|
|
4296
|
+
};
|
|
4244
4297
|
var OpenAiAdsApiError = class extends Error {
|
|
4245
4298
|
status;
|
|
4246
4299
|
code;
|
|
@@ -4264,6 +4317,18 @@ function parseErrorEnvelope(body) {
|
|
|
4264
4317
|
}
|
|
4265
4318
|
|
|
4266
4319
|
// ../integration-openai-ads/src/ads-client.ts
|
|
4320
|
+
var MIN_ENTITY_NAME_LENGTH = 3;
|
|
4321
|
+
var MAX_ENTITY_NAME_LENGTH = 1e3;
|
|
4322
|
+
var MIN_CAMPAIGN_TIMESTAMP = 946684800;
|
|
4323
|
+
var MAX_CAMPAIGN_TIMESTAMP = 4102444800;
|
|
4324
|
+
var MIN_LIFETIME_BUDGET_MICROS = 1e6;
|
|
4325
|
+
var MIN_BID_MICROS = 1;
|
|
4326
|
+
var MAX_BID_MICROS = 1e8;
|
|
4327
|
+
var MIN_AD_TITLE_LENGTH = 3;
|
|
4328
|
+
var MAX_AD_TITLE_LENGTH = 50;
|
|
4329
|
+
var MAX_AD_BODY_LENGTH = 100;
|
|
4330
|
+
var MIN_GEO_SEARCH_LIMIT = 1;
|
|
4331
|
+
var MAX_GEO_SEARCH_LIMIT = 500;
|
|
4267
4332
|
function validateApiKey(apiKey) {
|
|
4268
4333
|
if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
|
|
4269
4334
|
throw new OpenAiAdsApiError("API key is required and must be a non-empty string", 400);
|
|
@@ -4274,6 +4339,208 @@ function validateId(value, label) {
|
|
|
4274
4339
|
throw new OpenAiAdsApiError(`${label} is required and must be a non-empty string`, 400);
|
|
4275
4340
|
}
|
|
4276
4341
|
}
|
|
4342
|
+
function validateGeoSearchLimit(limit) {
|
|
4343
|
+
if (limit === void 0) return;
|
|
4344
|
+
if (!Number.isInteger(limit) || limit < MIN_GEO_SEARCH_LIMIT || limit > MAX_GEO_SEARCH_LIMIT) {
|
|
4345
|
+
throw new OpenAiAdsApiError(
|
|
4346
|
+
`Geo search limit must be an integer between ${MIN_GEO_SEARCH_LIMIT} and ${MAX_GEO_SEARCH_LIMIT}`,
|
|
4347
|
+
400
|
|
4348
|
+
);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
function isRecord(value) {
|
|
4352
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4353
|
+
}
|
|
4354
|
+
function validateRequestObject(value, label) {
|
|
4355
|
+
if (!isRecord(value)) {
|
|
4356
|
+
throw new OpenAiAdsApiError(`${label} must be a JSON object`, 400);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
function validateNonEmptyRequest(value, label) {
|
|
4360
|
+
validateRequestObject(value, label);
|
|
4361
|
+
if (Object.keys(value).length === 0) {
|
|
4362
|
+
throw new OpenAiAdsApiError(`${label} must include at least one field`, 400);
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
function snapshotJsonRequest(value, label) {
|
|
4366
|
+
try {
|
|
4367
|
+
const serialized = JSON.stringify(value);
|
|
4368
|
+
return JSON.parse(serialized);
|
|
4369
|
+
} catch {
|
|
4370
|
+
throw new OpenAiAdsApiError(`${label} must be JSON-serializable`, 400);
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
function validateEntityName(value, label) {
|
|
4374
|
+
if (typeof value !== "string" || value.trim().length < MIN_ENTITY_NAME_LENGTH || value.length > MAX_ENTITY_NAME_LENGTH) {
|
|
4375
|
+
throw new OpenAiAdsApiError(
|
|
4376
|
+
`${label} must be ${MIN_ENTITY_NAME_LENGTH}-${MAX_ENTITY_NAME_LENGTH} characters and include a non-space character`,
|
|
4377
|
+
400
|
|
4378
|
+
);
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
function validatePausedCreateStatus(value, label) {
|
|
4382
|
+
if (value !== OpenAiAdsWriteStatuses.paused) {
|
|
4383
|
+
throw new OpenAiAdsApiError(`${label} status must be paused`, 400);
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
function validatePublicUpdateRequest(value, label) {
|
|
4387
|
+
validateNonEmptyRequest(value, label);
|
|
4388
|
+
if (Object.hasOwn(value, "status")) {
|
|
4389
|
+
throw new OpenAiAdsApiError(`${label} cannot include status; use an explicit lifecycle action`, 400);
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
function validateCampaignTimestamp(value, label) {
|
|
4393
|
+
if (value === void 0 || value === null) return;
|
|
4394
|
+
if (!Number.isInteger(value) || value < MIN_CAMPAIGN_TIMESTAMP || value > MAX_CAMPAIGN_TIMESTAMP) {
|
|
4395
|
+
throw new OpenAiAdsApiError(
|
|
4396
|
+
`${label} must be a Unix timestamp between ${MIN_CAMPAIGN_TIMESTAMP} and ${MAX_CAMPAIGN_TIMESTAMP}`,
|
|
4397
|
+
400
|
|
4398
|
+
);
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
function validateCampaignBudget(value) {
|
|
4402
|
+
validateRequestObject(value, "Campaign budget");
|
|
4403
|
+
const limit = value.lifetime_spend_limit_micros;
|
|
4404
|
+
if (!Number.isInteger(limit) || limit < MIN_LIFETIME_BUDGET_MICROS) {
|
|
4405
|
+
throw new OpenAiAdsApiError(
|
|
4406
|
+
`Campaign budget lifetime_spend_limit_micros must be an integer of at least ${MIN_LIFETIME_BUDGET_MICROS}`,
|
|
4407
|
+
400
|
|
4408
|
+
);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
function validateCampaignTargeting(value) {
|
|
4412
|
+
if (value === void 0) return;
|
|
4413
|
+
if (value === null) {
|
|
4414
|
+
throw new OpenAiAdsApiError("Campaign targeting cannot be null; omit it to preserve existing targeting", 400);
|
|
4415
|
+
}
|
|
4416
|
+
validateRequestObject(value, "Campaign targeting");
|
|
4417
|
+
validateRequestObject(value.locations, "Campaign targeting locations");
|
|
4418
|
+
const include = value.locations.include;
|
|
4419
|
+
if (!Array.isArray(include) || include.length === 0) {
|
|
4420
|
+
throw new OpenAiAdsApiError("Campaign targeting locations include must be a non-empty array", 400);
|
|
4421
|
+
}
|
|
4422
|
+
for (const target of include) {
|
|
4423
|
+
validateRequestObject(target, "Campaign location target");
|
|
4424
|
+
validateId(target.id, "Campaign location id");
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
function validateCampaignBidding(request) {
|
|
4428
|
+
const biddingType = request.bidding_type;
|
|
4429
|
+
if (biddingType !== void 0 && biddingType !== OpenAiAdsBiddingTypes.impressions && biddingType !== OpenAiAdsBiddingTypes.clicks) {
|
|
4430
|
+
throw new OpenAiAdsApiError("Campaign bidding_type must be impressions or clicks", 400);
|
|
4431
|
+
}
|
|
4432
|
+
const conversionIds = request.conversion_event_setting_ids;
|
|
4433
|
+
if (conversionIds !== void 0) {
|
|
4434
|
+
if (!Array.isArray(conversionIds)) {
|
|
4435
|
+
throw new OpenAiAdsApiError("Campaign conversion_event_setting_ids must be an array of unique IDs", 400);
|
|
4436
|
+
}
|
|
4437
|
+
for (const conversionId of conversionIds) {
|
|
4438
|
+
validateId(conversionId, "Campaign conversion event setting id");
|
|
4439
|
+
}
|
|
4440
|
+
if (new Set(conversionIds).size !== conversionIds.length) {
|
|
4441
|
+
throw new OpenAiAdsApiError("Campaign conversion_event_setting_ids must contain unique IDs", 400);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
if (biddingType === OpenAiAdsBiddingTypes.clicks && (!Array.isArray(conversionIds) || conversionIds.length === 0)) {
|
|
4445
|
+
throw new OpenAiAdsApiError(
|
|
4446
|
+
"Click campaigns require at least one conversion_event_setting_id",
|
|
4447
|
+
400
|
|
4448
|
+
);
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
function validateBiddingConfig(value) {
|
|
4452
|
+
validateRequestObject(value, "Ad group bidding_config");
|
|
4453
|
+
if (value.billing_event_type !== OpenAiAdsBillingEventTypes.impression && value.billing_event_type !== OpenAiAdsBillingEventTypes.click) {
|
|
4454
|
+
throw new OpenAiAdsApiError("Ad group billing_event_type must be impression or click", 400);
|
|
4455
|
+
}
|
|
4456
|
+
const maxBid = value.max_bid_micros;
|
|
4457
|
+
if (!Number.isInteger(maxBid) || maxBid < MIN_BID_MICROS || maxBid > MAX_BID_MICROS) {
|
|
4458
|
+
throw new OpenAiAdsApiError(
|
|
4459
|
+
`Ad group max_bid_micros must be an integer between ${MIN_BID_MICROS} and ${MAX_BID_MICROS}`,
|
|
4460
|
+
400
|
|
4461
|
+
);
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
function validateContextHints(value) {
|
|
4465
|
+
if (value === void 0) return;
|
|
4466
|
+
if (!Array.isArray(value) || value.some((hint) => typeof hint !== "string" || hint.trim().length === 0)) {
|
|
4467
|
+
throw new OpenAiAdsApiError("Ad group context_hints must be an array of non-empty strings", 400);
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
function validateHttpUrl(value, label) {
|
|
4471
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4472
|
+
throw new OpenAiAdsApiError(`${label} is required and must be a non-empty URL`, 400);
|
|
4473
|
+
}
|
|
4474
|
+
try {
|
|
4475
|
+
const parsed = new URL(value);
|
|
4476
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4477
|
+
throw new Error("unsupported protocol");
|
|
4478
|
+
}
|
|
4479
|
+
} catch {
|
|
4480
|
+
throw new OpenAiAdsApiError(`${label} must be an absolute HTTP or HTTPS URL`, 400);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
function validateChatCardCreative(value) {
|
|
4484
|
+
validateRequestObject(value, "Ad creative");
|
|
4485
|
+
if (value.type !== OpenAiAdsCreativeTypes.chatCard) {
|
|
4486
|
+
throw new OpenAiAdsApiError("Ad creative type must be chat_card", 400);
|
|
4487
|
+
}
|
|
4488
|
+
if (typeof value.title !== "string" || value.title.trim().length < MIN_AD_TITLE_LENGTH || value.title.length > MAX_AD_TITLE_LENGTH) {
|
|
4489
|
+
throw new OpenAiAdsApiError(
|
|
4490
|
+
`Ad creative title must be ${MIN_AD_TITLE_LENGTH}-${MAX_AD_TITLE_LENGTH} characters`,
|
|
4491
|
+
400
|
|
4492
|
+
);
|
|
4493
|
+
}
|
|
4494
|
+
if (typeof value.body !== "string" || value.body.trim().length === 0 || value.body.length > MAX_AD_BODY_LENGTH) {
|
|
4495
|
+
throw new OpenAiAdsApiError(`Ad creative body must be 1-${MAX_AD_BODY_LENGTH} characters`, 400);
|
|
4496
|
+
}
|
|
4497
|
+
validateId(value.file_id, "Ad creative file id");
|
|
4498
|
+
validateHttpUrl(value.target_url, "Ad creative target URL");
|
|
4499
|
+
}
|
|
4500
|
+
function validateCreateCampaignRequest(request) {
|
|
4501
|
+
validateRequestObject(request, "Campaign create request");
|
|
4502
|
+
validateEntityName(request.name, "Campaign name");
|
|
4503
|
+
validatePausedCreateStatus(request.status, "Campaign create request");
|
|
4504
|
+
validateCampaignBudget(request.budget);
|
|
4505
|
+
validateCampaignTimestamp(request.start_time, "Campaign start_time");
|
|
4506
|
+
validateCampaignTimestamp(request.end_time, "Campaign end_time");
|
|
4507
|
+
validateCampaignBidding(request);
|
|
4508
|
+
validateCampaignTargeting(request.targeting);
|
|
4509
|
+
}
|
|
4510
|
+
function validateUpdateCampaignRequest(request) {
|
|
4511
|
+
validatePublicUpdateRequest(request, "Campaign update request");
|
|
4512
|
+
if (request.name !== void 0) validateEntityName(request.name, "Campaign name");
|
|
4513
|
+
if (request.budget !== void 0) validateCampaignBudget(request.budget);
|
|
4514
|
+
validateCampaignTimestamp(request.start_time, "Campaign start_time");
|
|
4515
|
+
validateCampaignTimestamp(request.end_time, "Campaign end_time");
|
|
4516
|
+
validateCampaignTargeting(request.targeting);
|
|
4517
|
+
}
|
|
4518
|
+
function validateCreateAdGroupRequest(request) {
|
|
4519
|
+
validateRequestObject(request, "Ad group create request");
|
|
4520
|
+
validateId(request.campaign_id, "Campaign id");
|
|
4521
|
+
validateEntityName(request.name, "Ad group name");
|
|
4522
|
+
validatePausedCreateStatus(request.status, "Ad group create request");
|
|
4523
|
+
validateContextHints(request.context_hints);
|
|
4524
|
+
validateBiddingConfig(request.bidding_config);
|
|
4525
|
+
}
|
|
4526
|
+
function validateUpdateAdGroupRequest(request) {
|
|
4527
|
+
validatePublicUpdateRequest(request, "Ad group update request");
|
|
4528
|
+
if (request.name !== void 0) validateEntityName(request.name, "Ad group name");
|
|
4529
|
+
validateContextHints(request.context_hints);
|
|
4530
|
+
if (request.bidding_config !== void 0) validateBiddingConfig(request.bidding_config);
|
|
4531
|
+
}
|
|
4532
|
+
function validateCreateAdRequest(request) {
|
|
4533
|
+
validateRequestObject(request, "Ad create request");
|
|
4534
|
+
validateId(request.ad_group_id, "Ad group id");
|
|
4535
|
+
validateEntityName(request.name, "Ad name");
|
|
4536
|
+
validatePausedCreateStatus(request.status, "Ad create request");
|
|
4537
|
+
validateChatCardCreative(request.creative);
|
|
4538
|
+
}
|
|
4539
|
+
function validateUpdateAdRequest(request) {
|
|
4540
|
+
validatePublicUpdateRequest(request, "Ad update request");
|
|
4541
|
+
if (request.name !== void 0) validateEntityName(request.name, "Ad name");
|
|
4542
|
+
if (request.creative !== void 0) validateChatCardCreative(request.creative);
|
|
4543
|
+
}
|
|
4277
4544
|
function adsClientLog(level, action, ctx) {
|
|
4278
4545
|
const entry = {
|
|
4279
4546
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4290,14 +4557,15 @@ function buildUrl(path10, queryPairs) {
|
|
|
4290
4557
|
const qs = queryPairs.join("&");
|
|
4291
4558
|
return qs ? `${OPENAI_ADS_API_BASE}/${path10}?${qs}` : `${OPENAI_ADS_API_BASE}/${path10}`;
|
|
4292
4559
|
}
|
|
4293
|
-
async function adsFetch(apiKey, path10, queryPairs = []) {
|
|
4560
|
+
async function adsFetch(apiKey, path10, queryPairs = [], method = "GET", body) {
|
|
4294
4561
|
const url = buildUrl(path10, queryPairs);
|
|
4295
4562
|
const res = await fetch(url, {
|
|
4296
|
-
method
|
|
4563
|
+
method,
|
|
4297
4564
|
headers: {
|
|
4298
4565
|
Authorization: `Bearer ${apiKey}`,
|
|
4299
4566
|
"Content-Type": "application/json"
|
|
4300
4567
|
},
|
|
4568
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
4301
4569
|
signal: AbortSignal.timeout(OPENAI_ADS_REQUEST_TIMEOUT_MS)
|
|
4302
4570
|
});
|
|
4303
4571
|
if (res.status === 401 || res.status === 403) {
|
|
@@ -4311,10 +4579,10 @@ async function adsFetch(apiKey, path10, queryPairs = []) {
|
|
|
4311
4579
|
throw new OpenAiAdsApiError("OpenAI Ads API rate limit exceeded", 429, code);
|
|
4312
4580
|
}
|
|
4313
4581
|
if (!res.ok) {
|
|
4314
|
-
const
|
|
4315
|
-
const { message, code } = parseErrorEnvelope(
|
|
4582
|
+
const body2 = await res.text();
|
|
4583
|
+
const { message, code } = parseErrorEnvelope(body2);
|
|
4316
4584
|
adsClientLog("error", "http.error", { path: path10, httpStatus: res.status, code });
|
|
4317
|
-
const detail = message ?? (
|
|
4585
|
+
const detail = message ?? (body2.length <= 500 ? body2 : `${body2.slice(0, 500)}... [truncated]`);
|
|
4318
4586
|
throw new OpenAiAdsApiError(`OpenAI Ads API error (${res.status}): ${detail}`, res.status, code);
|
|
4319
4587
|
}
|
|
4320
4588
|
const text = await res.text();
|
|
@@ -4337,7 +4605,11 @@ async function fetchAllPages(apiKey, path10, queryPairs) {
|
|
|
4337
4605
|
after = response.last_id;
|
|
4338
4606
|
}
|
|
4339
4607
|
adsClientLog("error", "pagination.cap-reached", { path: path10, pages: OPENAI_ADS_MAX_PAGES, items: items.length });
|
|
4340
|
-
|
|
4608
|
+
throw new OpenAiAdsApiError(
|
|
4609
|
+
`OpenAI Ads API collection remained incomplete after the ${OPENAI_ADS_MAX_PAGES}-page safety cap`,
|
|
4610
|
+
502,
|
|
4611
|
+
"pagination_limit_exceeded"
|
|
4612
|
+
);
|
|
4341
4613
|
}
|
|
4342
4614
|
function insightsPairs(opts) {
|
|
4343
4615
|
return (opts?.fields ?? []).map((field) => `fields[]=${encodeURIComponent(field)}`);
|
|
@@ -4346,20 +4618,126 @@ async function getAdAccount(apiKey) {
|
|
|
4346
4618
|
validateApiKey(apiKey);
|
|
4347
4619
|
return adsFetch(apiKey, "ad_account");
|
|
4348
4620
|
}
|
|
4621
|
+
async function searchGeoLocations(apiKey, query, limit) {
|
|
4622
|
+
validateApiKey(apiKey);
|
|
4623
|
+
validateId(query, "Geo search query");
|
|
4624
|
+
validateGeoSearchLimit(limit);
|
|
4625
|
+
const queryPairs = [`q=${encodeURIComponent(query)}`];
|
|
4626
|
+
if (limit !== void 0) queryPairs.push(`limit=${limit}`);
|
|
4627
|
+
return adsFetch(apiKey, "geo_lookup/search", queryPairs);
|
|
4628
|
+
}
|
|
4629
|
+
async function listConversionPixels(apiKey) {
|
|
4630
|
+
validateApiKey(apiKey);
|
|
4631
|
+
return fetchAllPages(apiKey, "conversions/pixels", []);
|
|
4632
|
+
}
|
|
4633
|
+
async function listConversionEventSettings(apiKey) {
|
|
4634
|
+
validateApiKey(apiKey);
|
|
4635
|
+
return fetchAllPages(apiKey, "conversions/event_settings", []);
|
|
4636
|
+
}
|
|
4349
4637
|
async function listCampaigns(apiKey) {
|
|
4350
4638
|
validateApiKey(apiKey);
|
|
4351
4639
|
return fetchAllPages(apiKey, "campaigns", []);
|
|
4352
4640
|
}
|
|
4641
|
+
async function getCampaign(apiKey, campaignId) {
|
|
4642
|
+
validateApiKey(apiKey);
|
|
4643
|
+
validateId(campaignId, "Campaign id");
|
|
4644
|
+
return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}`);
|
|
4645
|
+
}
|
|
4646
|
+
async function createCampaign(apiKey, request) {
|
|
4647
|
+
validateApiKey(apiKey);
|
|
4648
|
+
const outbound = snapshotJsonRequest(request, "Campaign create request");
|
|
4649
|
+
validateCreateCampaignRequest(outbound);
|
|
4650
|
+
return adsFetch(apiKey, "campaigns", [], "POST", outbound);
|
|
4651
|
+
}
|
|
4652
|
+
async function updateCampaign(apiKey, campaignId, request) {
|
|
4653
|
+
validateApiKey(apiKey);
|
|
4654
|
+
validateId(campaignId, "Campaign id");
|
|
4655
|
+
const outbound = snapshotJsonRequest(request, "Campaign update request");
|
|
4656
|
+
validateUpdateCampaignRequest(outbound);
|
|
4657
|
+
return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}`, [], "POST", outbound);
|
|
4658
|
+
}
|
|
4659
|
+
async function activateCampaign(apiKey, campaignId) {
|
|
4660
|
+
validateApiKey(apiKey);
|
|
4661
|
+
validateId(campaignId, "Campaign id");
|
|
4662
|
+
return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}/activate`, [], "POST");
|
|
4663
|
+
}
|
|
4664
|
+
async function pauseCampaign(apiKey, campaignId) {
|
|
4665
|
+
validateApiKey(apiKey);
|
|
4666
|
+
validateId(campaignId, "Campaign id");
|
|
4667
|
+
return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}/pause`, [], "POST");
|
|
4668
|
+
}
|
|
4353
4669
|
async function listAdGroups(apiKey, campaignId) {
|
|
4354
4670
|
validateApiKey(apiKey);
|
|
4355
4671
|
validateId(campaignId, "Campaign id");
|
|
4356
4672
|
return fetchAllPages(apiKey, "ad_groups", [`campaign_id=${encodeURIComponent(campaignId)}`]);
|
|
4357
4673
|
}
|
|
4674
|
+
async function getAdGroup(apiKey, adGroupId) {
|
|
4675
|
+
validateApiKey(apiKey);
|
|
4676
|
+
validateId(adGroupId, "Ad group id");
|
|
4677
|
+
return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}`);
|
|
4678
|
+
}
|
|
4679
|
+
async function createAdGroup(apiKey, request) {
|
|
4680
|
+
validateApiKey(apiKey);
|
|
4681
|
+
const outbound = snapshotJsonRequest(request, "Ad group create request");
|
|
4682
|
+
validateCreateAdGroupRequest(outbound);
|
|
4683
|
+
return adsFetch(apiKey, "ad_groups", [], "POST", outbound);
|
|
4684
|
+
}
|
|
4685
|
+
async function updateAdGroup(apiKey, adGroupId, request) {
|
|
4686
|
+
validateApiKey(apiKey);
|
|
4687
|
+
validateId(adGroupId, "Ad group id");
|
|
4688
|
+
const outbound = snapshotJsonRequest(request, "Ad group update request");
|
|
4689
|
+
validateUpdateAdGroupRequest(outbound);
|
|
4690
|
+
return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}`, [], "POST", outbound);
|
|
4691
|
+
}
|
|
4692
|
+
async function activateAdGroup(apiKey, adGroupId) {
|
|
4693
|
+
validateApiKey(apiKey);
|
|
4694
|
+
validateId(adGroupId, "Ad group id");
|
|
4695
|
+
return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}/activate`, [], "POST");
|
|
4696
|
+
}
|
|
4697
|
+
async function pauseAdGroup(apiKey, adGroupId) {
|
|
4698
|
+
validateApiKey(apiKey);
|
|
4699
|
+
validateId(adGroupId, "Ad group id");
|
|
4700
|
+
return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}/pause`, [], "POST");
|
|
4701
|
+
}
|
|
4358
4702
|
async function listAds(apiKey, adGroupId) {
|
|
4359
4703
|
validateApiKey(apiKey);
|
|
4360
4704
|
validateId(adGroupId, "Ad group id");
|
|
4361
4705
|
return fetchAllPages(apiKey, "ads", [`ad_group_id=${encodeURIComponent(adGroupId)}`]);
|
|
4362
4706
|
}
|
|
4707
|
+
async function getAd(apiKey, adId) {
|
|
4708
|
+
validateApiKey(apiKey);
|
|
4709
|
+
validateId(adId, "Ad id");
|
|
4710
|
+
return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}`);
|
|
4711
|
+
}
|
|
4712
|
+
async function createAd(apiKey, request) {
|
|
4713
|
+
validateApiKey(apiKey);
|
|
4714
|
+
const outbound = snapshotJsonRequest(request, "Ad create request");
|
|
4715
|
+
validateCreateAdRequest(outbound);
|
|
4716
|
+
return adsFetch(apiKey, "ads", [], "POST", outbound);
|
|
4717
|
+
}
|
|
4718
|
+
async function updateAd(apiKey, adId, request) {
|
|
4719
|
+
validateApiKey(apiKey);
|
|
4720
|
+
validateId(adId, "Ad id");
|
|
4721
|
+
const outbound = snapshotJsonRequest(request, "Ad update request");
|
|
4722
|
+
validateUpdateAdRequest(outbound);
|
|
4723
|
+
return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}`, [], "POST", outbound);
|
|
4724
|
+
}
|
|
4725
|
+
async function activateAd(apiKey, adId) {
|
|
4726
|
+
validateApiKey(apiKey);
|
|
4727
|
+
validateId(adId, "Ad id");
|
|
4728
|
+
return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}/activate`, [], "POST");
|
|
4729
|
+
}
|
|
4730
|
+
async function pauseAd(apiKey, adId) {
|
|
4731
|
+
validateApiKey(apiKey);
|
|
4732
|
+
validateId(adId, "Ad id");
|
|
4733
|
+
return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}/pause`, [], "POST");
|
|
4734
|
+
}
|
|
4735
|
+
async function uploadImageFromUrl(apiKey, imageUrl) {
|
|
4736
|
+
validateApiKey(apiKey);
|
|
4737
|
+
validateHttpUrl(imageUrl, "Image URL");
|
|
4738
|
+
const request = { image_url: imageUrl };
|
|
4739
|
+
return adsFetch(apiKey, "upload", [], "POST", request);
|
|
4740
|
+
}
|
|
4363
4741
|
async function getCampaignInsights(apiKey, campaignId, opts) {
|
|
4364
4742
|
validateApiKey(apiKey);
|
|
4365
4743
|
validateId(campaignId, "Campaign id");
|
|
@@ -4491,10 +4869,14 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
4491
4869
|
id: campaign.id,
|
|
4492
4870
|
projectId,
|
|
4493
4871
|
name: campaign.name,
|
|
4872
|
+
description: campaign.description ?? null,
|
|
4494
4873
|
status: campaign.status,
|
|
4874
|
+
startTime: campaign.start_time ?? null,
|
|
4875
|
+
endTime: campaign.end_time ?? null,
|
|
4495
4876
|
biddingType: campaign.bidding_type,
|
|
4496
4877
|
dailySpendLimitMicros: campaign.budget?.daily_spend_limit_micros ?? null,
|
|
4497
4878
|
lifetimeSpendLimitMicros: campaign.budget?.lifetime_spend_limit_micros ?? null,
|
|
4879
|
+
conversionEventSettingIds: campaign.conversion_event_setting_ids ?? [],
|
|
4498
4880
|
targeting: campaign.targeting,
|
|
4499
4881
|
upstreamCreatedAt: campaign.created_at,
|
|
4500
4882
|
upstreamUpdatedAt: campaign.updated_at,
|
|
@@ -4507,6 +4889,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
4507
4889
|
projectId,
|
|
4508
4890
|
campaignId: campaign.id,
|
|
4509
4891
|
name: group.name,
|
|
4892
|
+
description: group.description ?? null,
|
|
4510
4893
|
status: group.status,
|
|
4511
4894
|
billingEventType: group.bidding_config?.billing_event_type ?? null,
|
|
4512
4895
|
maxBidMicros: group.bidding_config?.max_bid_micros ?? null,
|
|
@@ -4556,6 +4939,9 @@ async function executeAdsSync(db, runId, projectId, opts) {
|
|
|
4556
4939
|
currencyCode: account.currency_code,
|
|
4557
4940
|
timezone: account.timezone,
|
|
4558
4941
|
status: account.status,
|
|
4942
|
+
reviewStatus: account.review?.status ?? null,
|
|
4943
|
+
integrityReviewStatus: account.account_integrity_review?.review?.status ?? null,
|
|
4944
|
+
integrityDecision: account.account_integrity_review?.details?.decision ?? null,
|
|
4559
4945
|
conversionTrackingConfigured,
|
|
4560
4946
|
lastSyncedAt: insertNow,
|
|
4561
4947
|
updatedAt: insertNow
|
|
@@ -6515,7 +6901,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
6515
6901
|
return result;
|
|
6516
6902
|
}
|
|
6517
6903
|
async function backfillInsightsCommand(project, opts) {
|
|
6518
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
6904
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-K35EN43I.js");
|
|
6519
6905
|
const config = loadConfig();
|
|
6520
6906
|
const db = createClient(config.database);
|
|
6521
6907
|
migrate(db);
|
|
@@ -8602,9 +8988,28 @@ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
8602
8988
|
CanonryMcpToolNames.canonry_memory_forget,
|
|
8603
8989
|
CanonryMcpToolNames.canonry_run_trigger,
|
|
8604
8990
|
CanonryMcpToolNames.canonry_ads_status,
|
|
8991
|
+
CanonryMcpToolNames.canonry_ads_account,
|
|
8992
|
+
CanonryMcpToolNames.canonry_ads_geo_search,
|
|
8993
|
+
CanonryMcpToolNames.canonry_ads_conversion_pixels,
|
|
8994
|
+
CanonryMcpToolNames.canonry_ads_conversion_event_settings,
|
|
8605
8995
|
CanonryMcpToolNames.canonry_ads_campaigns,
|
|
8606
8996
|
CanonryMcpToolNames.canonry_ads_insights,
|
|
8607
8997
|
CanonryMcpToolNames.canonry_ads_summary,
|
|
8998
|
+
CanonryMcpToolNames.canonry_ads_operations_unresolved,
|
|
8999
|
+
CanonryMcpToolNames.canonry_ads_operation_get,
|
|
9000
|
+
CanonryMcpToolNames.canonry_ads_operation_reconcile,
|
|
9001
|
+
CanonryMcpToolNames.canonry_ads_operation_resume_activation,
|
|
9002
|
+
CanonryMcpToolNames.canonry_ads_image_upload,
|
|
9003
|
+
CanonryMcpToolNames.canonry_ads_campaign_create,
|
|
9004
|
+
CanonryMcpToolNames.canonry_ads_campaign_update,
|
|
9005
|
+
CanonryMcpToolNames.canonry_ads_campaign_pause,
|
|
9006
|
+
CanonryMcpToolNames.canonry_ads_campaign_activate_tree,
|
|
9007
|
+
CanonryMcpToolNames.canonry_ads_ad_group_create,
|
|
9008
|
+
CanonryMcpToolNames.canonry_ads_ad_group_update,
|
|
9009
|
+
CanonryMcpToolNames.canonry_ads_ad_group_pause,
|
|
9010
|
+
CanonryMcpToolNames.canonry_ads_ad_create,
|
|
9011
|
+
CanonryMcpToolNames.canonry_ads_ad_update,
|
|
9012
|
+
CanonryMcpToolNames.canonry_ads_ad_pause,
|
|
8608
9013
|
CanonryMcpToolNames.canonry_ads_sync
|
|
8609
9014
|
]);
|
|
8610
9015
|
function toolResult(details) {
|
|
@@ -8666,6 +9071,7 @@ function compactCampaigns(response, limit) {
|
|
|
8666
9071
|
adGroups: campaign.adGroups.slice(0, 8).map((group) => ({
|
|
8667
9072
|
id: group.id,
|
|
8668
9073
|
name: group.name,
|
|
9074
|
+
description: group.description,
|
|
8669
9075
|
status: group.status,
|
|
8670
9076
|
billingEventType: group.billingEventType,
|
|
8671
9077
|
maxBidMicros: group.maxBidMicros,
|
|
@@ -8677,14 +9083,25 @@ function compactCampaigns(response, limit) {
|
|
|
8677
9083
|
name: ad.name,
|
|
8678
9084
|
status: ad.status,
|
|
8679
9085
|
reviewStatus: ad.reviewStatus,
|
|
9086
|
+
upstreamUpdatedAt: ad.upstreamUpdatedAt,
|
|
9087
|
+
syncedAt: ad.syncedAt,
|
|
8680
9088
|
creative: ad.creative ? {
|
|
8681
9089
|
type: ad.creative.type,
|
|
8682
9090
|
title: ad.creative.title,
|
|
8683
9091
|
body: ad.creative.body,
|
|
8684
|
-
targetUrl: ad.creative.targetUrl
|
|
9092
|
+
targetUrl: ad.creative.targetUrl,
|
|
9093
|
+
fileId: ad.creative.fileId
|
|
8685
9094
|
} : ad.creative
|
|
8686
|
-
}))
|
|
8687
|
-
|
|
9095
|
+
})),
|
|
9096
|
+
upstreamUpdatedAt: group.upstreamUpdatedAt,
|
|
9097
|
+
syncedAt: group.syncedAt
|
|
9098
|
+
})),
|
|
9099
|
+
description: campaign.description,
|
|
9100
|
+
startTime: campaign.startTime,
|
|
9101
|
+
endTime: campaign.endTime,
|
|
9102
|
+
locationIds: campaign.locationIds ?? [],
|
|
9103
|
+
upstreamUpdatedAt: campaign.upstreamUpdatedAt,
|
|
9104
|
+
syncedAt: campaign.syncedAt
|
|
8688
9105
|
}))
|
|
8689
9106
|
};
|
|
8690
9107
|
}
|
|
@@ -8845,11 +9262,11 @@ function recordLlmUsageEvent(args) {
|
|
|
8845
9262
|
var AERO_MEMORY_SEPARATOR = "\n\n---\n\n";
|
|
8846
9263
|
var AERO_MEMORY_BLOCK_PREFIX = `${AERO_MEMORY_SEPARATOR}<memory>
|
|
8847
9264
|
`;
|
|
8848
|
-
function
|
|
9265
|
+
function isRecord2(value) {
|
|
8849
9266
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
8850
9267
|
}
|
|
8851
9268
|
function isTextSystemBlock(value) {
|
|
8852
|
-
return
|
|
9269
|
+
return isRecord2(value) && value.type === "text" && typeof value.text === "string";
|
|
8853
9270
|
}
|
|
8854
9271
|
function splitAeroHydratedSystemPrompt(systemPrompt) {
|
|
8855
9272
|
const idx = systemPrompt.lastIndexOf(AERO_MEMORY_BLOCK_PREFIX);
|
|
@@ -8861,7 +9278,7 @@ function splitAeroHydratedSystemPrompt(systemPrompt) {
|
|
|
8861
9278
|
}
|
|
8862
9279
|
function splitAeroAnthropicSystemCachePayload(payload, model) {
|
|
8863
9280
|
if (model.api !== "anthropic-messages") return void 0;
|
|
8864
|
-
if (!
|
|
9281
|
+
if (!isRecord2(payload) || !Array.isArray(payload.system)) return void 0;
|
|
8865
9282
|
let changed = false;
|
|
8866
9283
|
const nextSystem = [];
|
|
8867
9284
|
for (const block of payload.system) {
|
|
@@ -11361,16 +11778,199 @@ async function createServer(opts) {
|
|
|
11361
11778
|
return removed;
|
|
11362
11779
|
}
|
|
11363
11780
|
};
|
|
11781
|
+
const normalizeAdsAccount = (account) => adsAccountDtoSchema.parse({
|
|
11782
|
+
id: account.id,
|
|
11783
|
+
name: account.name,
|
|
11784
|
+
status: account.status,
|
|
11785
|
+
currencyCode: account.currency_code ?? null,
|
|
11786
|
+
timezone: account.timezone ?? null,
|
|
11787
|
+
url: account.url ?? null,
|
|
11788
|
+
reviewStatus: account.review?.status ?? null,
|
|
11789
|
+
integrityReviewStatus: account.account_integrity_review?.review?.status ?? null,
|
|
11790
|
+
integrityDecision: account.account_integrity_review?.details?.decision ?? null
|
|
11791
|
+
});
|
|
11364
11792
|
const verifyAdsAccount = async (apiKey) => {
|
|
11365
|
-
const account = await getAdAccount(apiKey);
|
|
11793
|
+
const account = normalizeAdsAccount(await getAdAccount(apiKey));
|
|
11366
11794
|
return {
|
|
11367
11795
|
id: account.id,
|
|
11368
11796
|
name: account.name,
|
|
11369
11797
|
status: account.status,
|
|
11370
|
-
currencyCode: account.
|
|
11371
|
-
timezone: account.timezone
|
|
11798
|
+
currencyCode: account.currencyCode,
|
|
11799
|
+
timezone: account.timezone,
|
|
11800
|
+
reviewStatus: account.reviewStatus,
|
|
11801
|
+
integrityReviewStatus: account.integrityReviewStatus,
|
|
11802
|
+
integrityDecision: account.integrityDecision
|
|
11372
11803
|
};
|
|
11373
11804
|
};
|
|
11805
|
+
const adsReader = {
|
|
11806
|
+
getAccount: async (apiKey) => normalizeAdsAccount(await getAdAccount(apiKey)),
|
|
11807
|
+
searchGeo: async (apiKey, input) => {
|
|
11808
|
+
const response = await searchGeoLocations(apiKey, input.q, input.limit);
|
|
11809
|
+
return adsGeoSearchResponseSchema.parse({
|
|
11810
|
+
count: response.count,
|
|
11811
|
+
query: response.query,
|
|
11812
|
+
results: response.results.map((location) => ({
|
|
11813
|
+
id: location.id,
|
|
11814
|
+
type: location.type,
|
|
11815
|
+
canonicalName: location.canonical_name,
|
|
11816
|
+
countryCode: location.country_code,
|
|
11817
|
+
name: location.name,
|
|
11818
|
+
regionCode: location.region_code
|
|
11819
|
+
}))
|
|
11820
|
+
});
|
|
11821
|
+
},
|
|
11822
|
+
listConversionPixels: async (apiKey) => {
|
|
11823
|
+
const pixels = await listConversionPixels(apiKey);
|
|
11824
|
+
return adsConversionPixelListResponseSchema.parse({
|
|
11825
|
+
pixels: pixels.map((pixel) => ({
|
|
11826
|
+
id: pixel.id,
|
|
11827
|
+
clientType: pixel.client_type,
|
|
11828
|
+
name: pixel.name,
|
|
11829
|
+
pixelId: pixel.pixel_id
|
|
11830
|
+
}))
|
|
11831
|
+
});
|
|
11832
|
+
},
|
|
11833
|
+
listConversionEventSettings: async (apiKey) => {
|
|
11834
|
+
const eventSettings = await listConversionEventSettings(apiKey);
|
|
11835
|
+
return adsConversionEventSettingListResponseSchema.parse({
|
|
11836
|
+
eventSettings: eventSettings.map((eventSetting) => ({
|
|
11837
|
+
id: eventSetting.id,
|
|
11838
|
+
name: eventSetting.name,
|
|
11839
|
+
eventType: eventSetting.event_type,
|
|
11840
|
+
customEventName: eventSetting.custom_event_name,
|
|
11841
|
+
attributionWindowDays: eventSetting.attribution_window_days,
|
|
11842
|
+
adAccountId: eventSetting.ad_account_id,
|
|
11843
|
+
sourceIds: eventSetting.source_ids,
|
|
11844
|
+
sources: eventSetting.sources,
|
|
11845
|
+
archived: eventSetting.archived,
|
|
11846
|
+
version: eventSetting.version
|
|
11847
|
+
}))
|
|
11848
|
+
});
|
|
11849
|
+
}
|
|
11850
|
+
};
|
|
11851
|
+
const adsEntityResult = (entity) => ({
|
|
11852
|
+
id: entity.id,
|
|
11853
|
+
name: entity.name,
|
|
11854
|
+
status: entity.status,
|
|
11855
|
+
updatedAt: entity.updated_at,
|
|
11856
|
+
reviewStatus: entity.review_status ?? null,
|
|
11857
|
+
creative: typeof entity.creative?.title === "string" && typeof entity.creative.body === "string" && typeof entity.creative.target_url === "string" && typeof entity.creative.file_id === "string" ? {
|
|
11858
|
+
title: entity.creative.title,
|
|
11859
|
+
body: entity.creative.body,
|
|
11860
|
+
targetUrl: entity.creative.target_url,
|
|
11861
|
+
fileId: entity.creative.file_id
|
|
11862
|
+
} : null
|
|
11863
|
+
});
|
|
11864
|
+
const adsCampaignEntityResult = (entity) => ({
|
|
11865
|
+
...adsEntityResult(entity),
|
|
11866
|
+
description: entity.description,
|
|
11867
|
+
startTime: entity.start_time,
|
|
11868
|
+
endTime: entity.end_time,
|
|
11869
|
+
lifetimeSpendLimitMicros: entity.budget?.lifetime_spend_limit_micros ?? null,
|
|
11870
|
+
locationIds: entity.targeting?.locations?.include?.map((location) => location.id) ?? [],
|
|
11871
|
+
biddingType: entity.bidding_type,
|
|
11872
|
+
conversionEventSettingIds: entity.conversion_event_setting_ids
|
|
11873
|
+
});
|
|
11874
|
+
const adsAdGroupEntityResult = (entity, campaignId) => ({
|
|
11875
|
+
...adsEntityResult(entity),
|
|
11876
|
+
description: entity.description,
|
|
11877
|
+
campaignId: campaignId ?? null,
|
|
11878
|
+
contextHints: entity.context_hints,
|
|
11879
|
+
maxBidMicros: entity.bidding_config?.max_bid_micros ?? null,
|
|
11880
|
+
billingEventType: entity.bidding_config?.billing_event_type ?? null
|
|
11881
|
+
});
|
|
11882
|
+
const adsAdEntityResult = (entity, adGroupId) => ({
|
|
11883
|
+
...adsEntityResult(entity),
|
|
11884
|
+
adGroupId: adGroupId ?? null
|
|
11885
|
+
});
|
|
11886
|
+
const adsOperator = {
|
|
11887
|
+
uploadImage: async (apiKey, imageUrl) => {
|
|
11888
|
+
const result = await uploadImageFromUrl(apiKey, imageUrl);
|
|
11889
|
+
return { fileId: result.file_id };
|
|
11890
|
+
},
|
|
11891
|
+
getCampaign: async (apiKey, id) => adsCampaignEntityResult(await getCampaign(apiKey, id)),
|
|
11892
|
+
listCampaigns: async (apiKey) => (await listCampaigns(apiKey)).map(adsCampaignEntityResult),
|
|
11893
|
+
createCampaign: async (apiKey, input) => adsCampaignEntityResult(await createCampaign(apiKey, {
|
|
11894
|
+
name: input.name,
|
|
11895
|
+
description: input.description,
|
|
11896
|
+
start_time: input.startTime,
|
|
11897
|
+
end_time: input.endTime,
|
|
11898
|
+
status: OpenAiAdsWriteStatuses.paused,
|
|
11899
|
+
budget: { lifetime_spend_limit_micros: input.lifetimeSpendLimitMicros },
|
|
11900
|
+
bidding_type: input.biddingType,
|
|
11901
|
+
conversion_event_setting_ids: input.conversionEventSettingIds,
|
|
11902
|
+
targeting: { locations: { include: input.locationIds.map((id) => ({ id })) } }
|
|
11903
|
+
})),
|
|
11904
|
+
updateCampaign: async (apiKey, id, input) => adsCampaignEntityResult(await updateCampaign(apiKey, id, {
|
|
11905
|
+
name: input.name,
|
|
11906
|
+
description: input.description,
|
|
11907
|
+
start_time: input.startTime,
|
|
11908
|
+
end_time: input.endTime,
|
|
11909
|
+
budget: input.lifetimeSpendLimitMicros === void 0 ? void 0 : { lifetime_spend_limit_micros: input.lifetimeSpendLimitMicros },
|
|
11910
|
+
targeting: input.locationIds === void 0 ? void 0 : { locations: { include: input.locationIds.map((locationId) => ({ id: locationId })) } }
|
|
11911
|
+
})),
|
|
11912
|
+
activateCampaign: async (apiKey, id) => adsCampaignEntityResult(await activateCampaign(apiKey, id)),
|
|
11913
|
+
pauseCampaign: async (apiKey, id) => adsCampaignEntityResult(await pauseCampaign(apiKey, id)),
|
|
11914
|
+
getAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await getAdGroup(apiKey, id)),
|
|
11915
|
+
listAdGroups: async (apiKey, campaignId) => (await listAdGroups(apiKey, campaignId)).map((entity) => adsAdGroupEntityResult(entity, campaignId)),
|
|
11916
|
+
createAdGroup: async (apiKey, input) => adsAdGroupEntityResult(await createAdGroup(apiKey, {
|
|
11917
|
+
campaign_id: input.campaignId,
|
|
11918
|
+
name: input.name,
|
|
11919
|
+
description: input.description,
|
|
11920
|
+
context_hints: input.contextHints,
|
|
11921
|
+
status: OpenAiAdsWriteStatuses.paused,
|
|
11922
|
+
bidding_config: {
|
|
11923
|
+
billing_event_type: input.billingEventType,
|
|
11924
|
+
max_bid_micros: input.maxBidMicros
|
|
11925
|
+
}
|
|
11926
|
+
})),
|
|
11927
|
+
updateAdGroup: async (apiKey, id, input) => {
|
|
11928
|
+
let biddingConfig;
|
|
11929
|
+
if (input.maxBidMicros !== void 0) {
|
|
11930
|
+
if (input.billingEventType === void 0) {
|
|
11931
|
+
throw new Error("Ad group max-bid updates require the current billing event type");
|
|
11932
|
+
}
|
|
11933
|
+
biddingConfig = {
|
|
11934
|
+
billing_event_type: input.billingEventType,
|
|
11935
|
+
max_bid_micros: input.maxBidMicros
|
|
11936
|
+
};
|
|
11937
|
+
}
|
|
11938
|
+
return adsAdGroupEntityResult(await updateAdGroup(apiKey, id, {
|
|
11939
|
+
name: input.name,
|
|
11940
|
+
description: input.description,
|
|
11941
|
+
context_hints: input.contextHints,
|
|
11942
|
+
bidding_config: biddingConfig
|
|
11943
|
+
}));
|
|
11944
|
+
},
|
|
11945
|
+
activateAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await activateAdGroup(apiKey, id)),
|
|
11946
|
+
pauseAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await pauseAdGroup(apiKey, id)),
|
|
11947
|
+
getAd: async (apiKey, id) => adsAdEntityResult(await getAd(apiKey, id)),
|
|
11948
|
+
listAds: async (apiKey, adGroupId) => (await listAds(apiKey, adGroupId)).map((entity) => adsAdEntityResult(entity, adGroupId)),
|
|
11949
|
+
createAd: async (apiKey, input) => adsAdEntityResult(await createAd(apiKey, {
|
|
11950
|
+
ad_group_id: input.adGroupId,
|
|
11951
|
+
name: input.name,
|
|
11952
|
+
status: OpenAiAdsWriteStatuses.paused,
|
|
11953
|
+
creative: {
|
|
11954
|
+
type: OpenAiAdsCreativeTypes.chatCard,
|
|
11955
|
+
title: input.creative.title,
|
|
11956
|
+
body: input.creative.body,
|
|
11957
|
+
target_url: input.creative.targetUrl,
|
|
11958
|
+
file_id: input.creative.fileId
|
|
11959
|
+
}
|
|
11960
|
+
})),
|
|
11961
|
+
updateAd: async (apiKey, id, input) => adsAdEntityResult(await updateAd(apiKey, id, {
|
|
11962
|
+
name: input.name,
|
|
11963
|
+
creative: input.creative ? {
|
|
11964
|
+
type: OpenAiAdsCreativeTypes.chatCard,
|
|
11965
|
+
title: input.creative.title,
|
|
11966
|
+
body: input.creative.body,
|
|
11967
|
+
target_url: input.creative.targetUrl,
|
|
11968
|
+
file_id: input.creative.fileId
|
|
11969
|
+
} : void 0
|
|
11970
|
+
})),
|
|
11971
|
+
activateAd: async (apiKey, id) => adsAdEntityResult(await activateAd(apiKey, id)),
|
|
11972
|
+
pauseAd: async (apiKey, id) => adsAdEntityResult(await pauseAd(apiKey, id))
|
|
11973
|
+
};
|
|
11374
11974
|
const scheduler = new Scheduler(opts.db, {
|
|
11375
11975
|
onRunCreated: (runId, projectId, providers2, location) => {
|
|
11376
11976
|
jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
|
|
@@ -11963,6 +12563,8 @@ async function createServer(opts) {
|
|
|
11963
12563
|
},
|
|
11964
12564
|
adsCredentialStore,
|
|
11965
12565
|
verifyAdsAccount,
|
|
12566
|
+
adsReader,
|
|
12567
|
+
adsOperator,
|
|
11966
12568
|
onAdsSyncRequested: (runId, projectId) => {
|
|
11967
12569
|
runAdsSync(runId, projectId);
|
|
11968
12570
|
},
|
|
@@ -12106,6 +12708,9 @@ async function createServer(opts) {
|
|
|
12106
12708
|
name: a.name,
|
|
12107
12709
|
displayName: a.displayName,
|
|
12108
12710
|
mode: a.mode,
|
|
12711
|
+
modelConfigurable: a.mode === "api",
|
|
12712
|
+
defaultModel: a.modelRegistry.defaultModel,
|
|
12713
|
+
knownModels: a.modelRegistry.knownModels,
|
|
12109
12714
|
modelValidationPattern: a.modelRegistry.validationPattern,
|
|
12110
12715
|
modelValidationHint: a.modelRegistry.validationHint
|
|
12111
12716
|
})),
|