@kaminari-ad/mcp 0.9.1 → 0.11.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/CHANGELOG.md +99 -0
- package/dist/bin.js +3 -3
- package/dist/{chunk-R56UDCOA.js → chunk-XCAKB2QW.js} +169 -55
- package/dist/chunk-XCAKB2QW.js.map +1 -0
- package/dist/{chunk-JEGBPX3R.js → chunk-XSI6SZTX.js} +3 -3
- package/dist/{chunk-JEGBPX3R.js.map → chunk-XSI6SZTX.js.map} +1 -1
- package/dist/{http-bootstrap-JS2O6I22.js → http-bootstrap-XJNTLL2C.js} +4 -4
- package/dist/{http-bootstrap-JS2O6I22.js.map → http-bootstrap-XJNTLL2C.js.map} +1 -1
- package/dist/{stdio-bootstrap-YQFKQP2G.js → stdio-bootstrap-QK4ENRPP.js} +4 -4
- package/dist/{stdio-bootstrap-YQFKQP2G.js.map → stdio-bootstrap-QK4ENRPP.js.map} +1 -1
- package/package.json +20 -14
- package/dist/chunk-R56UDCOA.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { err, ok } from './chunk-
|
|
2
|
+
import { err, ok } from './chunk-XSI6SZTX.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -240,17 +240,22 @@ var ProxyTargetRequest = z.object({
|
|
|
240
240
|
city: z.string().default(""),
|
|
241
241
|
isp: z.string().default("")
|
|
242
242
|
}).partial().passthrough();
|
|
243
|
+
var RepeatModeType = z.enum(["isolated", "shared"]);
|
|
243
244
|
z.object({
|
|
244
245
|
url: z.union([z.string(), z.null()]).optional(),
|
|
245
246
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
246
247
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
248
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
247
249
|
country_code: z.string().min(2).max(2),
|
|
248
250
|
emulator_id: z.string().min(1).max(100),
|
|
249
251
|
proxy: ProxyTargetRequest.optional(),
|
|
250
252
|
labels: z.record(z.string()).optional(),
|
|
251
253
|
campaign_id: z.union([z.string(), z.null()]).optional(),
|
|
252
254
|
run_id: z.union([z.string(), z.null()]).optional(),
|
|
253
|
-
ad_discovery: z.boolean().optional().default(false)
|
|
255
|
+
ad_discovery: z.boolean().optional().default(false),
|
|
256
|
+
repeat_count: z.number().int().gte(1).optional().default(1),
|
|
257
|
+
repeat_mode: RepeatModeType.optional(),
|
|
258
|
+
retry_max_attempts: z.number().int().gte(0).optional().default(0)
|
|
254
259
|
}).passthrough();
|
|
255
260
|
var ScanStatus = z.enum([
|
|
256
261
|
"pending",
|
|
@@ -290,7 +295,8 @@ var VideoMetaResponse = z.object({
|
|
|
290
295
|
vast_version: z.string().default(""),
|
|
291
296
|
ad_system: z.string().default(""),
|
|
292
297
|
is_vpaid: z.boolean().default(false),
|
|
293
|
-
wrapper_depth: z.number().int().default(0)
|
|
298
|
+
wrapper_depth: z.number().int().default(0),
|
|
299
|
+
click_through: z.string().default("")
|
|
294
300
|
}).partial().passthrough();
|
|
295
301
|
var ProxyTargetResponse = z.object({
|
|
296
302
|
proxy_type: z.string(),
|
|
@@ -357,8 +363,12 @@ var ScanResponse = z.object({
|
|
|
357
363
|
public_report_url: z.string().optional().default(""),
|
|
358
364
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
359
365
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
360
|
-
|
|
366
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
367
|
+
creative_kind: z.enum(["banner", "video"]).optional().default("banner"),
|
|
361
368
|
creative_screenshot_url: z.string().optional().default(""),
|
|
369
|
+
creative_video_url: z.string().optional().default(""),
|
|
370
|
+
vast_xml_url: z.string().optional().default(""),
|
|
371
|
+
creative_html_url: z.string().optional().default(""),
|
|
362
372
|
creative_width: z.number().int().optional().default(0),
|
|
363
373
|
creative_height: z.number().int().optional().default(0),
|
|
364
374
|
video: z.union([VideoMetaResponse, z.null()]).optional(),
|
|
@@ -374,7 +384,13 @@ var ScanResponse = z.object({
|
|
|
374
384
|
campaign_group_name: z.union([z.string(), z.null()]).optional(),
|
|
375
385
|
created_at: z.string().datetime({ offset: true }),
|
|
376
386
|
completed_at: z.union([z.string(), z.null()]),
|
|
377
|
-
landings: z.array(LandingResponse).optional()
|
|
387
|
+
landings: z.array(LandingResponse).optional(),
|
|
388
|
+
repeat_index: z.number().int().optional().default(0),
|
|
389
|
+
repeat_total: z.number().int().optional().default(1),
|
|
390
|
+
repeat_session_id: z.union([z.string(), z.null()]).optional(),
|
|
391
|
+
repeat_scan_ids: z.array(z.string().uuid()).optional(),
|
|
392
|
+
retry_attempt: z.number().int().optional().default(0),
|
|
393
|
+
retry_max_attempts: z.number().int().optional().default(0)
|
|
378
394
|
}).passthrough();
|
|
379
395
|
z.union([z.string(), z.null()]).optional();
|
|
380
396
|
var ScanBriefResponse = z.object({
|
|
@@ -401,7 +417,12 @@ var ScanBriefResponse = z.object({
|
|
|
401
417
|
ad_kind: z.union([z.string(), z.null()]).optional(),
|
|
402
418
|
network: z.string().optional().default(""),
|
|
403
419
|
emulator_display_name: z.string().optional().default(""),
|
|
404
|
-
emulator_category: z.string().optional().default("")
|
|
420
|
+
emulator_category: z.string().optional().default(""),
|
|
421
|
+
repeat_index: z.number().int().optional().default(0),
|
|
422
|
+
repeat_total: z.number().int().optional().default(1),
|
|
423
|
+
repeat_session_id: z.union([z.string(), z.null()]).optional(),
|
|
424
|
+
retry_attempt: z.number().int().optional().default(0),
|
|
425
|
+
retry_max_attempts: z.number().int().optional().default(0)
|
|
405
426
|
}).passthrough();
|
|
406
427
|
z.object({
|
|
407
428
|
items: z.array(ScanBriefResponse),
|
|
@@ -414,10 +435,14 @@ z.object({
|
|
|
414
435
|
url: z.union([z.string(), z.null()]).optional(),
|
|
415
436
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
416
437
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
438
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
417
439
|
country_codes: z.array(z.string()).min(1),
|
|
418
440
|
emulator_id: z.string().min(1).max(100),
|
|
419
441
|
proxy: ProxyTargetRequest.optional(),
|
|
420
|
-
labels: z.record(z.string()).optional()
|
|
442
|
+
labels: z.record(z.string()).optional(),
|
|
443
|
+
repeat_count: z.number().int().gte(1).optional().default(1),
|
|
444
|
+
repeat_mode: RepeatModeType.optional(),
|
|
445
|
+
retry_max_attempts: z.number().int().gte(0).optional().default(0)
|
|
421
446
|
}).passthrough();
|
|
422
447
|
z.object({
|
|
423
448
|
scope_type: z.enum(["last_n", "hours"]),
|
|
@@ -461,12 +486,14 @@ var GroupActionResponse = z.object({
|
|
|
461
486
|
run_ids: z.array(z.string().uuid()).optional(),
|
|
462
487
|
failures: z.array(BulkCampaignFailure).optional()
|
|
463
488
|
}).passthrough();
|
|
489
|
+
var RepeatMode = z.enum(["isolated", "shared"]);
|
|
464
490
|
z.object({
|
|
465
491
|
name: z.string().min(1).max(200),
|
|
466
492
|
campaign_type: z.string().optional().default("url"),
|
|
467
493
|
url: z.union([z.string(), z.null()]).optional(),
|
|
468
494
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
469
495
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
496
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
470
497
|
country_codes: z.array(z.string()).min(1),
|
|
471
498
|
group_id: z.union([z.string(), z.null()]).optional(),
|
|
472
499
|
emulator_categories: z.array(z.string()).optional(),
|
|
@@ -476,6 +503,9 @@ z.object({
|
|
|
476
503
|
proxy_region: z.string().optional().default(""),
|
|
477
504
|
proxy_city: z.string().optional().default(""),
|
|
478
505
|
proxy_isp: z.string().optional().default(""),
|
|
506
|
+
repeat_count: z.number().int().gte(1).optional().default(1),
|
|
507
|
+
repeat_mode: RepeatMode.optional(),
|
|
508
|
+
retry_max_attempts: z.number().int().gte(0).optional().default(0),
|
|
479
509
|
labels: z.record(z.string()).optional(),
|
|
480
510
|
policy_set_id: z.union([z.string(), z.null()]).optional(),
|
|
481
511
|
schedule_type: z.union([z.string(), z.null()]).optional(),
|
|
@@ -496,6 +526,7 @@ var CampaignResponse = z.object({
|
|
|
496
526
|
url: z.string(),
|
|
497
527
|
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
498
528
|
vast_tag: z.union([z.string(), z.null()]).optional(),
|
|
529
|
+
referrer: z.union([z.string(), z.null()]).optional(),
|
|
499
530
|
country_codes: z.array(z.string()),
|
|
500
531
|
group_id: z.string().uuid(),
|
|
501
532
|
emulator_selection: EmulatorSelectionResponse,
|
|
@@ -503,6 +534,9 @@ var CampaignResponse = z.object({
|
|
|
503
534
|
proxy_region: z.string().optional().default(""),
|
|
504
535
|
proxy_city: z.string().optional().default(""),
|
|
505
536
|
proxy_isp: z.string().optional().default(""),
|
|
537
|
+
repeat_count: z.number().int().optional().default(1),
|
|
538
|
+
repeat_mode: RepeatMode.optional(),
|
|
539
|
+
retry_max_attempts: z.number().int().optional().default(0),
|
|
506
540
|
schedule_type: z.union([z.string(), z.null()]).optional(),
|
|
507
541
|
schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]).optional(),
|
|
508
542
|
schedule_interval_seconds: z.union([z.number(), z.null()]).optional(),
|
|
@@ -533,6 +567,7 @@ z.object({
|
|
|
533
567
|
url: z.union([z.string(), z.null()]),
|
|
534
568
|
ad_tag: z.union([z.string(), z.null()]),
|
|
535
569
|
vast_tag: z.union([z.string(), z.null()]),
|
|
570
|
+
referrer: z.union([z.string(), z.null()]),
|
|
536
571
|
country_codes: z.union([z.array(z.string()), z.null()]),
|
|
537
572
|
group_id: z.union([z.string(), z.null()]),
|
|
538
573
|
emulator_categories: z.union([z.array(z.string()), z.null()]),
|
|
@@ -542,6 +577,9 @@ z.object({
|
|
|
542
577
|
proxy_region: z.union([z.string(), z.null()]),
|
|
543
578
|
proxy_city: z.union([z.string(), z.null()]),
|
|
544
579
|
proxy_isp: z.union([z.string(), z.null()]),
|
|
580
|
+
repeat_count: z.union([z.number(), z.null()]),
|
|
581
|
+
repeat_mode: z.union([RepeatMode, z.null()]),
|
|
582
|
+
retry_max_attempts: z.union([z.number(), z.null()]),
|
|
545
583
|
labels: z.union([z.record(z.string()), z.null()]),
|
|
546
584
|
policy_set_id: z.union([z.string(), z.null()]),
|
|
547
585
|
schedule_type: z.union([z.string(), z.null()]),
|
|
@@ -649,7 +687,8 @@ z.object({
|
|
|
649
687
|
tag_slug: z.string().max(100).optional().default(""),
|
|
650
688
|
rule_type: z.string().max(50),
|
|
651
689
|
config: z.object({}).partial().passthrough(),
|
|
652
|
-
target: z.string().max(30).optional().default("page")
|
|
690
|
+
target: z.string().max(30).optional().default("page"),
|
|
691
|
+
tag_visibility: z.union([z.record(TagVisibility), z.null()]).optional()
|
|
653
692
|
}).passthrough();
|
|
654
693
|
var CustomRuleResponse = z.object({
|
|
655
694
|
id: z.string().uuid(),
|
|
@@ -661,7 +700,8 @@ var CustomRuleResponse = z.object({
|
|
|
661
700
|
target: z.string(),
|
|
662
701
|
is_active: z.boolean(),
|
|
663
702
|
created_at: z.string().datetime({ offset: true }),
|
|
664
|
-
scope: z.string().optional().default("personal")
|
|
703
|
+
scope: z.string().optional().default("personal"),
|
|
704
|
+
tag_visibility: z.record(z.string()).optional()
|
|
665
705
|
}).passthrough();
|
|
666
706
|
z.object({
|
|
667
707
|
items: z.array(CustomRuleResponse),
|
|
@@ -675,7 +715,8 @@ z.object({
|
|
|
675
715
|
tag_slug: z.union([z.string(), z.null()]),
|
|
676
716
|
config: z.union([z.object({}).partial().passthrough(), z.null()]),
|
|
677
717
|
target: z.union([z.string(), z.null()]),
|
|
678
|
-
is_active: z.union([z.boolean(), z.null()])
|
|
718
|
+
is_active: z.union([z.boolean(), z.null()]),
|
|
719
|
+
tag_visibility: z.union([z.record(TagVisibility), z.null()])
|
|
679
720
|
}).partial().passthrough();
|
|
680
721
|
z.object({
|
|
681
722
|
scan_id: z.string().uuid(),
|
|
@@ -906,7 +947,8 @@ var BalanceTransactionType = z.enum([
|
|
|
906
947
|
"admin_adjustment",
|
|
907
948
|
"refund",
|
|
908
949
|
"invoice_settlement",
|
|
909
|
-
"crypto_top_up"
|
|
950
|
+
"crypto_top_up",
|
|
951
|
+
"card_top_up"
|
|
910
952
|
]);
|
|
911
953
|
z.union([z.array(BalanceTransactionType), z.null()]).optional();
|
|
912
954
|
var BalanceTransactionResponse = z.object({
|
|
@@ -1153,6 +1195,7 @@ z.object({
|
|
|
1153
1195
|
description: z.string().max(500).optional().default(""),
|
|
1154
1196
|
nodes: z.array(TaxonomyNodeRequest).optional().default([])
|
|
1155
1197
|
});
|
|
1198
|
+
z.enum(["any", "all"]);
|
|
1156
1199
|
var schemas = {
|
|
1157
1200
|
OrgResponse,
|
|
1158
1201
|
LabelDefinitionResponse,
|
|
@@ -1166,6 +1209,7 @@ var schemas = {
|
|
|
1166
1209
|
EmulatorResponse,
|
|
1167
1210
|
CampaignGroupResponse,
|
|
1168
1211
|
GroupActionResponse,
|
|
1212
|
+
RepeatMode,
|
|
1169
1213
|
CampaignResponse,
|
|
1170
1214
|
CampaignPickerItem,
|
|
1171
1215
|
RunResponse,
|
|
@@ -1301,6 +1345,7 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1301
1345
|
url: true,
|
|
1302
1346
|
ad_tag: true,
|
|
1303
1347
|
vast_tag: true,
|
|
1348
|
+
referrer: true,
|
|
1304
1349
|
country_codes: true,
|
|
1305
1350
|
group_id: true,
|
|
1306
1351
|
emulator_selection: true,
|
|
@@ -1308,6 +1353,9 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1308
1353
|
proxy_region: true,
|
|
1309
1354
|
proxy_city: true,
|
|
1310
1355
|
proxy_isp: true,
|
|
1356
|
+
repeat_count: true,
|
|
1357
|
+
repeat_mode: true,
|
|
1358
|
+
retry_max_attempts: true,
|
|
1311
1359
|
labels: true,
|
|
1312
1360
|
policy_set_id: true,
|
|
1313
1361
|
schedule_enabled: true,
|
|
@@ -1318,7 +1366,7 @@ var CampaignSchema = schemas.CampaignResponse.pick({
|
|
|
1318
1366
|
is_archived: true,
|
|
1319
1367
|
created_at: true,
|
|
1320
1368
|
last_run_at: true
|
|
1321
|
-
}).strip();
|
|
1369
|
+
}).extend({ repeat_mode: schemas.RepeatMode.default("isolated") }).strip();
|
|
1322
1370
|
var parseCampaign = (raw) => parseWithSchema(CampaignSchema, raw, "campaign");
|
|
1323
1371
|
var parseCampaignPage = (raw) => parsePagedWithItemSchema(CampaignSchema, raw, "campaigns");
|
|
1324
1372
|
|
|
@@ -1731,6 +1779,7 @@ var ScanSchema = schemas.ScanResponse.pick({
|
|
|
1731
1779
|
public_report_url: true,
|
|
1732
1780
|
ad_tag: true,
|
|
1733
1781
|
vast_tag: true,
|
|
1782
|
+
referrer: true,
|
|
1734
1783
|
creative_kind: true,
|
|
1735
1784
|
video: true,
|
|
1736
1785
|
creative_screenshot_url: true,
|
|
@@ -1746,8 +1795,14 @@ var ScanSchema = schemas.ScanResponse.pick({
|
|
|
1746
1795
|
ad_kind: true,
|
|
1747
1796
|
network: true,
|
|
1748
1797
|
created_at: true,
|
|
1749
|
-
completed_at: true
|
|
1750
|
-
|
|
1798
|
+
completed_at: true,
|
|
1799
|
+
repeat_index: true,
|
|
1800
|
+
repeat_total: true,
|
|
1801
|
+
repeat_session_id: true,
|
|
1802
|
+
repeat_scan_ids: true,
|
|
1803
|
+
retry_attempt: true,
|
|
1804
|
+
retry_max_attempts: true
|
|
1805
|
+
}).extend({ creative_kind: z.string().default("banner") }).strip();
|
|
1751
1806
|
var ScanArraySchema = z.array(ScanSchema);
|
|
1752
1807
|
var parseScan = (raw) => parseWithSchema(ScanSchema, raw, "scan");
|
|
1753
1808
|
var parseScanArray = (raw) => parseWithSchema(ScanArraySchema, raw, "scans");
|
|
@@ -1773,7 +1828,12 @@ var ScanBriefSchema = schemas.ScanBriefResponse.pick({
|
|
|
1773
1828
|
slot_index: true,
|
|
1774
1829
|
ad_kind: true,
|
|
1775
1830
|
network: true,
|
|
1776
|
-
created_at: true
|
|
1831
|
+
created_at: true,
|
|
1832
|
+
repeat_index: true,
|
|
1833
|
+
repeat_total: true,
|
|
1834
|
+
repeat_session_id: true,
|
|
1835
|
+
retry_attempt: true,
|
|
1836
|
+
retry_max_attempts: true
|
|
1777
1837
|
}).strip();
|
|
1778
1838
|
var parseScanPage = (raw) => parsePagedWithItemSchema(ScanBriefSchema, raw, "scans");
|
|
1779
1839
|
var TagDefinitionSchema = schemas.TagDefinitionWithStatsResponse.pick({
|
|
@@ -1865,10 +1925,7 @@ function createHttpApiGateway(config) {
|
|
|
1865
1925
|
if (dispatcher !== void 0) {
|
|
1866
1926
|
baseInit["dispatcher"] = dispatcher;
|
|
1867
1927
|
}
|
|
1868
|
-
return fetch(
|
|
1869
|
-
input.url,
|
|
1870
|
-
baseInit
|
|
1871
|
-
);
|
|
1928
|
+
return fetch(input.url, baseInit);
|
|
1872
1929
|
};
|
|
1873
1930
|
const client = createClient({
|
|
1874
1931
|
baseUrl,
|
|
@@ -1933,10 +1990,7 @@ function createHttpApiGateway(config) {
|
|
|
1933
1990
|
}
|
|
1934
1991
|
};
|
|
1935
1992
|
if (dispatcher !== void 0) init["dispatcher"] = dispatcher;
|
|
1936
|
-
response = await fetch(
|
|
1937
|
-
url,
|
|
1938
|
-
init
|
|
1939
|
-
);
|
|
1993
|
+
response = await fetch(url, init);
|
|
1940
1994
|
} catch (cause) {
|
|
1941
1995
|
logger.warn({ api_path: path, elapsed_ms: Date.now() - startedAtMs }, "api.network_error");
|
|
1942
1996
|
const inner = cause instanceof Error && cause.cause instanceof Error ? cause.cause : cause instanceof Error ? cause : null;
|
|
@@ -3245,13 +3299,14 @@ var TransactionTypeEnum = z.enum([
|
|
|
3245
3299
|
"admin_adjustment",
|
|
3246
3300
|
"refund",
|
|
3247
3301
|
"invoice_settlement",
|
|
3248
|
-
"crypto_top_up"
|
|
3302
|
+
"crypto_top_up",
|
|
3303
|
+
"card_top_up"
|
|
3249
3304
|
]);
|
|
3250
3305
|
var ListBalanceHistoryInputShape = {
|
|
3251
3306
|
date_from: z.string().date().optional().describe("ISO date, inclusive."),
|
|
3252
3307
|
date_to: z.string().date().optional().describe("ISO date, inclusive."),
|
|
3253
|
-
type: z.array(TransactionTypeEnum).max(
|
|
3254
|
-
"Filter by transaction kind. Pass several values to OR them (e.g. ['top_up_manual','crypto_top_up'] for credits-only)."
|
|
3308
|
+
type: z.array(TransactionTypeEnum).max(TransactionTypeEnum.options.length).optional().describe(
|
|
3309
|
+
"Filter by transaction kind. Pass several values to OR them (e.g. ['top_up_manual','crypto_top_up','card_top_up'] for credits-only)."
|
|
3255
3310
|
),
|
|
3256
3311
|
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3257
3312
|
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
@@ -3558,6 +3613,28 @@ var cancelCampaignTool = {
|
|
|
3558
3613
|
return ok(result.value);
|
|
3559
3614
|
}
|
|
3560
3615
|
};
|
|
3616
|
+
var repeatRetryFields = {
|
|
3617
|
+
repeat_count: z.number().int().min(1).max(20).optional().describe(
|
|
3618
|
+
"How many times to repeat every scan this call already produces (1-20). It is a multiplier on top of the tool's own fan-out \u2014 see that tool's description for the exact formula \u2014 and each resulting repeat is a full scan with its own report and its own billing. 1-20 is the API's default ceiling; an operator can lower it during an incident, and then a value accepted here still comes back as a 422 from the API. Default: 1."
|
|
3619
|
+
),
|
|
3620
|
+
repeat_mode: z.enum(["isolated", "shared"]).optional().describe(
|
|
3621
|
+
"How the repeats of one combination relate to each other. 'isolated' gives every repeat a fresh browser and a new IP, so the repeats are statistically independent. 'shared' runs all repeats of one combination in a single browser behind one IP, carrying cookies and localStorage from one repeat to the next \u2014 use it to reproduce a cloaker or a frequency cap that only misbehaves on the second or third visit. 'shared' is rejected with 422 on an ad-discovery target: when creating, that is `ad_discovery: true` on a scan or `campaign_type: \"ad_discovery\"` on a campaign; when updating a campaign, it is a campaign whose existing type is already ad_discovery, since campaign_type itself cannot be changed. Default: isolated."
|
|
3622
|
+
),
|
|
3623
|
+
retry_max_attempts: z.number().int().min(0).max(5).optional().describe(
|
|
3624
|
+
"Extra crawl attempts when a scan fails for a technical reason \u2014 dead proxy, navigation timeout, browser crash (0-5). Permanent failures are never retried. The same scan is reused and only a completed scan is billed, so a retry never double-charges. 0-5 is the API's default ceiling; an operator can lower it, and then a value accepted here still comes back as a 422 from the API. Default: 0."
|
|
3625
|
+
)
|
|
3626
|
+
};
|
|
3627
|
+
function pickRepeatRetryBody(input) {
|
|
3628
|
+
const body = {};
|
|
3629
|
+
if (input.repeat_count !== void 0) body.repeat_count = input.repeat_count;
|
|
3630
|
+
if (input.repeat_mode !== void 0) body.repeat_mode = input.repeat_mode;
|
|
3631
|
+
if (input.retry_max_attempts !== void 0) {
|
|
3632
|
+
body.retry_max_attempts = input.retry_max_attempts;
|
|
3633
|
+
}
|
|
3634
|
+
return body;
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
// src/application/tools/campaigns/_campaign-config-fields.ts
|
|
3561
3638
|
var campaignConfigFields = {
|
|
3562
3639
|
emulator_categories: z.array(z.string()).optional().describe(
|
|
3563
3640
|
"Device categories to rotate through, e.g. ['android_phone']. Valid: android_phone, android_tablet, iphone, ipad, windows_desktop, macos_desktop. On create, omitting this defaults to ['android_phone']; pass [] together with emulator_specific_ids to target ONLY specific devices."
|
|
@@ -3572,6 +3649,7 @@ var campaignConfigFields = {
|
|
|
3572
3649
|
proxy_region: z.string().optional().describe("Proxy region/state targeting (free-text; only honoured for a single country)."),
|
|
3573
3650
|
proxy_city: z.string().optional().describe("Proxy city targeting."),
|
|
3574
3651
|
proxy_isp: z.string().optional().describe("Proxy ISP targeting."),
|
|
3652
|
+
...repeatRetryFields,
|
|
3575
3653
|
schedule_type: z.enum(["weekly", "interval"]).optional().describe(
|
|
3576
3654
|
"Scheduling mode: 'weekly' (run on a weekday/hour grid via schedule_weekly) or 'interval' (run every schedule_interval_seconds). Omit for a manual (run-on-demand) campaign."
|
|
3577
3655
|
),
|
|
@@ -3584,7 +3662,7 @@ var campaignConfigFields = {
|
|
|
3584
3662
|
schedule_timezone: z.string().optional().describe("IANA timezone for the weekly grid, e.g. 'Europe/Berlin'. Default: UTC.")
|
|
3585
3663
|
};
|
|
3586
3664
|
function pickCampaignConfigBody(input) {
|
|
3587
|
-
const body = {};
|
|
3665
|
+
const body = { ...pickRepeatRetryBody(input) };
|
|
3588
3666
|
if (input.emulator_categories !== void 0) body.emulator_categories = input.emulator_categories;
|
|
3589
3667
|
if (input.emulator_specific_ids !== void 0) {
|
|
3590
3668
|
body.emulator_specific_ids = input.emulator_specific_ids;
|
|
@@ -3602,6 +3680,10 @@ function pickCampaignConfigBody(input) {
|
|
|
3602
3680
|
if (input.schedule_timezone !== void 0) body.schedule_timezone = input.schedule_timezone;
|
|
3603
3681
|
return body;
|
|
3604
3682
|
}
|
|
3683
|
+
var campaignReferrerUrl = z.string().url().regex(/^https?:\/\//i, "Must be an http(s) URL.").max(2048);
|
|
3684
|
+
var CAMPAIGN_REFERRER_DESCRIPTION = "Optional http(s) page URL every scan of this campaign is checked from. For `ad_tag` and `vast` campaigns this is the publisher page the tag is embedded in \u2014 NOT the creative and NOT its landing page; the browser commits the page document on this URL without fetching the publisher, so the creative renders as if embedded there, and its cross-origin subrequests receive the origin only (https://publisher.example/, no path). For `url` and `ad_discovery` campaigns this is where the visitor came from, sent in full as the Referer of the page request.";
|
|
3685
|
+
var campaignReferrerField = campaignReferrerUrl.optional().describe(CAMPAIGN_REFERRER_DESCRIPTION);
|
|
3686
|
+
var campaignReferrerUpdateField = campaignReferrerUrl.nullable().optional().describe(`${CAMPAIGN_REFERRER_DESCRIPTION} Pass null to clear it.`);
|
|
3605
3687
|
|
|
3606
3688
|
// src/application/tools/campaigns/create-campaign.tool.ts
|
|
3607
3689
|
var CreateCampaignInputShape = {
|
|
@@ -3616,6 +3698,7 @@ var CreateCampaignInputShape = {
|
|
|
3616
3698
|
vast_tag: z.string().optional().describe(
|
|
3617
3699
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (required if campaign_type=vast)."
|
|
3618
3700
|
),
|
|
3701
|
+
referrer: campaignReferrerField,
|
|
3619
3702
|
country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
|
|
3620
3703
|
group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
|
|
3621
3704
|
...campaignConfigFields,
|
|
@@ -3625,7 +3708,7 @@ var CreateCampaignInputShape = {
|
|
|
3625
3708
|
};
|
|
3626
3709
|
var createCampaignTool = {
|
|
3627
3710
|
name: "create_campaign",
|
|
3628
|
-
description: "Create a recurring scan campaign (template). The schedule produces N scans per run where N = number of countries times number of device profiles
|
|
3711
|
+
description: "Create a recurring scan campaign (template). The schedule produces N scans per run where N = number of countries times number of device profiles times `repeat_count`. Scans cost credits when they run, not when the campaign is created, so a high `repeat_count` multiplies the bill on every run. The returned campaign echoes `repeat_count`, `repeat_mode`, and `retry_max_attempts`, so read it back to confirm what the campaign will do.",
|
|
3629
3712
|
annotations: {
|
|
3630
3713
|
title: "Create Campaign",
|
|
3631
3714
|
readOnlyHint: false,
|
|
@@ -3642,6 +3725,7 @@ var createCampaignTool = {
|
|
|
3642
3725
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3643
3726
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3644
3727
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
3728
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
3645
3729
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3646
3730
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3647
3731
|
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
@@ -3658,7 +3742,7 @@ var GetCampaignInputShape = {
|
|
|
3658
3742
|
};
|
|
3659
3743
|
var getCampaignTool = {
|
|
3660
3744
|
name: "get_campaign",
|
|
3661
|
-
description: "Get one campaign by UUID: name, type (url|ad_tag|vast|ad_discovery), target URL / ad-tag / VAST tag (vast_tag), countries, schedule status, archive status, parent group. `ad_discovery` campaigns store the publisher page in the URL field.",
|
|
3745
|
+
description: "Get one campaign by UUID: name, type (url|ad_tag|vast|ad_discovery), target URL / ad-tag / VAST tag (vast_tag), countries, emulator selection, proxy targeting, schedule status, archive status, parent group. Also echoes the repeat / retry settings \u2014 `repeat_count`, `repeat_mode`, `retry_max_attempts` \u2014 so this is how you confirm what `create_campaign` or `update_campaign` actually saved, and how many scans a run will produce (countries x device profiles x `repeat_count`). `ad_discovery` campaigns store the publisher page in the URL field.",
|
|
3662
3746
|
annotations: {
|
|
3663
3747
|
title: "Get Campaign",
|
|
3664
3748
|
readOnlyHint: true,
|
|
@@ -3709,7 +3793,7 @@ var ListCampaignsInputShape = {
|
|
|
3709
3793
|
};
|
|
3710
3794
|
var listCampaignsTool = {
|
|
3711
3795
|
name: "list_campaigns",
|
|
3712
|
-
description: "List campaigns for the caller's organization, optionally filtered by group, archived flag, or name substring. Paginated.",
|
|
3796
|
+
description: "List campaigns for the caller's organization, optionally filtered by group, archived flag, or name substring. Paginated. Each row carries the same fields as `get_campaign`, including the repeat / retry settings (`repeat_count`, `repeat_mode`, `retry_max_attempts`) \u2014 use them to spot the campaigns that multiply their per-run scan count.",
|
|
3713
3797
|
annotations: {
|
|
3714
3798
|
title: "List Campaigns",
|
|
3715
3799
|
readOnlyHint: true,
|
|
@@ -3812,6 +3896,7 @@ var UpdateCampaignInputShape = {
|
|
|
3812
3896
|
vast_tag: z.string().optional().describe(
|
|
3813
3897
|
"New VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (vast-type campaigns)."
|
|
3814
3898
|
),
|
|
3899
|
+
referrer: campaignReferrerUpdateField,
|
|
3815
3900
|
country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
|
|
3816
3901
|
group_id: z.string().uuid().optional().describe("Move the campaign to another group."),
|
|
3817
3902
|
...campaignConfigFields,
|
|
@@ -3821,7 +3906,7 @@ var UpdateCampaignInputShape = {
|
|
|
3821
3906
|
};
|
|
3822
3907
|
var updateCampaignTool = {
|
|
3823
3908
|
name: "update_campaign",
|
|
3824
|
-
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding.",
|
|
3909
|
+
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding, and `referrer` accepts null to clear the publisher page scans are checked from.",
|
|
3825
3910
|
annotations: {
|
|
3826
3911
|
title: "Update Campaign",
|
|
3827
3912
|
readOnlyHint: false,
|
|
@@ -3836,6 +3921,7 @@ var updateCampaignTool = {
|
|
|
3836
3921
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
3837
3922
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3838
3923
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
3924
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
3839
3925
|
...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
|
|
3840
3926
|
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3841
3927
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
@@ -3848,6 +3934,21 @@ var updateCampaignTool = {
|
|
|
3848
3934
|
return ok(result.value);
|
|
3849
3935
|
}
|
|
3850
3936
|
};
|
|
3937
|
+
var COMBO_MATCH_SCOPE_DOC = 'For `rule_type=\'combo\'` the optional rule-level key `match_scope` decides where conditions are counted. `"scan"` (the default, and the behaviour when the key is absent) checks them against the union of all tags on the scan. With `"url"`, every condition must be satisfied by tags on the same link, and the tag is assigned to that link \u2014 so a rule built only from link-less tags (AI verdicts, crawler behaviour) has no link to attach to and will never match; keep those on the whole scan. Mixing the two kinds does not help either: AI verdicts and per-link detections reach the engine in different scan parts and are not evaluated together today, so such a combo fires under neither scope. Give a `"url"` rule at least one positive condition (`all_of` / `any_of` / `tag_category` + `count_gte`) \u2014 a config carrying only `none_of` matches every link that merely lacks those tags, and the API does not reject that on this path. Thresholds stay "N or more" (`count_gte` / `any_of_min`) in both scopes; no other `match_scope` value is accepted.';
|
|
3938
|
+
var COMBO_MATCH_SCOPE_READ_DOC = 'For `rule_type=\'combo\'` the returned `config` may carry the rule-level key `match_scope` ("scan" = count conditions across the whole scan, "url" = every condition must be satisfied by tags on the same link). Resend it verbatim when updating: `update_custom_rule` replaces `config` wholesale, so dropping the key reverts the rule to whole-scan matching.';
|
|
3939
|
+
var ruleConfigField = z.record(z.unknown()).superRefine((config, ctx) => {
|
|
3940
|
+
const scope = config["match_scope"];
|
|
3941
|
+
if (scope === void 0 || scope === "scan" || scope === "url") {
|
|
3942
|
+
return;
|
|
3943
|
+
}
|
|
3944
|
+
ctx.addIssue({
|
|
3945
|
+
code: z.ZodIssueCode.custom,
|
|
3946
|
+
path: ["match_scope"],
|
|
3947
|
+
message: 'config.match_scope must be "scan" or "url". Omit the key for scan-wide matching (the default).'
|
|
3948
|
+
});
|
|
3949
|
+
});
|
|
3950
|
+
|
|
3951
|
+
// src/application/tools/custom-rules/create-custom-rule.tool.ts
|
|
3851
3952
|
var CreateCustomRuleInputShape = {
|
|
3852
3953
|
name: z.string().min(1).max(200).describe(
|
|
3853
3954
|
"Display name. For non-LLM rules this also becomes the `display_name` of the auto-registered tag definition (see `tag_slug`)."
|
|
@@ -3858,8 +3959,8 @@ var CreateCustomRuleInputShape = {
|
|
|
3858
3959
|
rule_type: z.string().max(50).describe(
|
|
3859
3960
|
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`. The API validates."
|
|
3860
3961
|
),
|
|
3861
|
-
config:
|
|
3862
|
-
"Rule-type-specific configuration object. Shape depends on `rule_type`. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`)."
|
|
3962
|
+
config: ruleConfigField.describe(
|
|
3963
|
+
"Rule-type-specific configuration object. Shape depends on `rule_type`. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`). " + COMBO_MATCH_SCOPE_DOC
|
|
3863
3964
|
),
|
|
3864
3965
|
target: z.string().max(30).optional().describe(
|
|
3865
3966
|
"Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
|
|
@@ -3867,7 +3968,7 @@ var CreateCustomRuleInputShape = {
|
|
|
3867
3968
|
};
|
|
3868
3969
|
var createCustomRuleTool = {
|
|
3869
3970
|
name: "create_custom_rule",
|
|
3870
|
-
description: "Define a custom tag-detection rule. The API auto-registers a tag definition for each slug the rule emits (`tag_slug` for non-LLM rules; `config.tags` keys for `rule_type='llm'`); slugs that collide with a built-in system tag are rejected with HTTP 422 / code `checking.system_slug_reserved`. Matches tag every future scan; existing scans are untouched until you call `recheck_scans`.",
|
|
3971
|
+
description: "Define a custom tag-detection rule. The API auto-registers a tag definition for each slug the rule emits (`tag_slug` for non-LLM rules; `config.tags` keys for `rule_type='llm'`); slugs that collide with a built-in system tag are rejected with HTTP 422 / code `checking.system_slug_reserved`. For `rule_type='combo'` set `config.match_scope='url'` when the thresholds must be met inside one link instead of anywhere on the scan (default `'scan'`). Matches tag every future scan; existing scans are untouched until you call `recheck_scans`.",
|
|
3871
3972
|
annotations: {
|
|
3872
3973
|
title: "Create Custom Rule",
|
|
3873
3974
|
readOnlyHint: false,
|
|
@@ -3912,7 +4013,7 @@ var deleteCustomRuleTool = {
|
|
|
3912
4013
|
var GetCustomRuleInputShape = { rule_id: z.string().uuid().describe("Rule UUID.") };
|
|
3913
4014
|
var getCustomRuleTool = {
|
|
3914
4015
|
name: "get_custom_rule",
|
|
3915
|
-
description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag.",
|
|
4016
|
+
description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag. " + COMBO_MATCH_SCOPE_READ_DOC,
|
|
3916
4017
|
annotations: {
|
|
3917
4018
|
title: "Get Custom Rule",
|
|
3918
4019
|
readOnlyHint: true,
|
|
@@ -3933,7 +4034,7 @@ var ListCustomRulesInputShape = {
|
|
|
3933
4034
|
};
|
|
3934
4035
|
var listCustomRulesTool = {
|
|
3935
4036
|
name: "list_custom_rules",
|
|
3936
|
-
description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed.",
|
|
4037
|
+
description: "Paginated list of the organization's custom tag-detection rules (regex / heuristics) with their config, target, and active flag. Returns `{items, total, page, limit}`. To iterate: there is a next page iff `page * limit < total` (or equivalently `items.length === limit` until the last page). `page` is 1-indexed. " + COMBO_MATCH_SCOPE_READ_DOC,
|
|
3937
4038
|
annotations: {
|
|
3938
4039
|
title: "List Custom Rules",
|
|
3939
4040
|
readOnlyHint: true,
|
|
@@ -3952,8 +4053,8 @@ var TestCustomRuleInputShape = {
|
|
|
3952
4053
|
rule_type: z.string().max(50).describe(
|
|
3953
4054
|
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`."
|
|
3954
4055
|
),
|
|
3955
|
-
config:
|
|
3956
|
-
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`."
|
|
4056
|
+
config: ruleConfigField.describe(
|
|
4057
|
+
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`. " + COMBO_MATCH_SCOPE_DOC
|
|
3957
4058
|
),
|
|
3958
4059
|
target: z.string().max(30).describe(
|
|
3959
4060
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
@@ -3962,7 +4063,7 @@ var TestCustomRuleInputShape = {
|
|
|
3962
4063
|
};
|
|
3963
4064
|
var testCustomRuleTool = {
|
|
3964
4065
|
name: "test_custom_rule",
|
|
3965
|
-
description: "Preview-test a rule definition against an existing scan WITHOUT persisting the rule. Returns `matched: bool`, evaluation time, and per-tag-slug details. Use to validate config before `create_custom_rule`. NOTE: slug-collision validation does NOT run in preview mode \u2014 verify any `tag_slug` (or LLM `config.tags` keys) against `list_tags` (`scope=system`) before promoting to `create_custom_rule`, which would otherwise reject the same payload with HTTP 422 / code `checking.system_slug_reserved`.",
|
|
4066
|
+
description: "Preview-test a rule definition against an existing scan WITHOUT persisting the rule. Returns `matched: bool`, evaluation time, and per-tag-slug details. Use to validate config before `create_custom_rule`. Combo rules are evaluated against the scan's existing tags, so `config.match_scope` is honoured \u2014 but per-URL hits are collapsed to one row per tag slug, so the result says whether the rule fires, not which URLs it would tag. NOTE: slug-collision validation does NOT run in preview mode \u2014 verify any `tag_slug` (or LLM `config.tags` keys) against `list_tags` (`scope=system`) before promoting to `create_custom_rule`, which would otherwise reject the same payload with HTTP 422 / code `checking.system_slug_reserved`.",
|
|
3966
4067
|
annotations: {
|
|
3967
4068
|
title: "Test Custom Rule",
|
|
3968
4069
|
readOnlyHint: true,
|
|
@@ -3990,8 +4091,8 @@ var UpdateCustomRuleInputShape = {
|
|
|
3990
4091
|
tag_slug: z.string().max(100).optional().describe(
|
|
3991
4092
|
"New tag slug to assign on match. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `scope=system`); colliding requests return 422 with code `checking.system_slug_reserved`. Re-registering an existing custom slug refreshes its tag definition's `display_name` / `description`."
|
|
3992
4093
|
),
|
|
3993
|
-
config:
|
|
3994
|
-
"New rule-type-specific config object. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract."
|
|
4094
|
+
config: ruleConfigField.optional().describe(
|
|
4095
|
+
"New rule-type-specific config object. Replaces the stored config wholesale \u2014 resend every key you want to keep, including a combo rule's `match_scope`. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract. " + COMBO_MATCH_SCOPE_DOC
|
|
3995
4096
|
),
|
|
3996
4097
|
target: z.string().max(30).optional().describe(
|
|
3997
4098
|
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
@@ -4000,7 +4101,7 @@ var UpdateCustomRuleInputShape = {
|
|
|
4000
4101
|
};
|
|
4001
4102
|
var updateCustomRuleTool = {
|
|
4002
4103
|
name: "update_custom_rule",
|
|
4003
|
-
description: "Update a custom tag-detection rule. Only supplied fields are sent. Changing `tag_slug` (or `config.tags` keys for `rule_type='llm'`) onto a built-in system slug returns HTTP 422 / code `checking.system_slug_reserved`. To toggle activation, pass `is_active`. Existing tagged scans are NOT re-evaluated \u2014 call `recheck_scans` for that. (Rule engine `rule_type` cannot be changed after creation; create a new rule instead.)",
|
|
4104
|
+
description: "Update a custom tag-detection rule. Only supplied fields are sent, but a supplied `config` replaces the stored one wholesale (omitting a combo rule's `match_scope` reverts it to the scan-wide default). Changing `tag_slug` (or `config.tags` keys for `rule_type='llm'`) onto a built-in system slug returns HTTP 422 / code `checking.system_slug_reserved`. To toggle activation, pass `is_active`. Existing tagged scans are NOT re-evaluated \u2014 call `recheck_scans` for that. (Rule engine `rule_type` cannot be changed after creation; create a new rule instead.)",
|
|
4004
4105
|
annotations: {
|
|
4005
4106
|
title: "Update Custom Rule",
|
|
4006
4107
|
readOnlyHint: false,
|
|
@@ -4663,6 +4764,9 @@ var scanProxyField = z.object({
|
|
|
4663
4764
|
city: z.string().optional().describe("Proxy city targeting."),
|
|
4664
4765
|
isp: z.string().optional().describe("Proxy ISP targeting.")
|
|
4665
4766
|
}).optional().describe("Optional proxy geo targeting for the crawl. Omit to use the org default.");
|
|
4767
|
+
var scanReferrerField = z.string().url().regex(/^https?:\/\//i, "Must be an http(s) URL.").max(2048).optional().describe(
|
|
4768
|
+
"Optional http(s) page URL the check is performed from. With `ad_tag` or `vast_tag` this is the publisher page the tag is embedded in \u2014 NOT the creative and NOT its landing page; the browser commits the page document on this URL without fetching the publisher, so the creative renders as if embedded there, and its cross-origin subrequests receive the origin only (https://publisher.example/, no path). With a `url` target this is where the visitor came from, sent in full as the Referer of the page request."
|
|
4769
|
+
);
|
|
4666
4770
|
|
|
4667
4771
|
// src/application/tools/scans/create-bulk-scans.tool.ts
|
|
4668
4772
|
var CreateBulkScansInputShape = {
|
|
@@ -4673,14 +4777,16 @@ var CreateBulkScansInputShape = {
|
|
|
4673
4777
|
vast_tag: z.string().optional().describe(
|
|
4674
4778
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML. Provide exactly one of `url`, `ad_tag`, or `vast_tag`."
|
|
4675
4779
|
),
|
|
4780
|
+
referrer: scanReferrerField,
|
|
4676
4781
|
country_codes: z.array(z.string().length(2)).min(1).max(50).describe("List of ISO 3166-1 alpha-2 country codes; one scan per country is created."),
|
|
4677
4782
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
|
|
4678
4783
|
proxy: scanProxyField,
|
|
4679
|
-
labels: z.record(z.string()).optional().describe("Arbitrary metadata copied onto every created scan.")
|
|
4784
|
+
labels: z.record(z.string()).optional().describe("Arbitrary metadata copied onto every created scan."),
|
|
4785
|
+
...repeatRetryFields
|
|
4680
4786
|
};
|
|
4681
4787
|
var createBulkScansTool = {
|
|
4682
4788
|
name: "create_bulk_scans",
|
|
4683
|
-
description: "Queue one new scan per country in a single call (e.g. test the same URL, ad-tag, or VAST video tag from US + DE + JP). COSTS N CREDITS where N = number of countries
|
|
4789
|
+
description: "Queue one new scan per country in a single call (e.g. test the same URL, ad-tag, or VAST video tag from US + DE + JP). COSTS N CREDITS where N = number of countries times `repeat_count`. Returns one entry per country; each entry's `repeat_scan_ids` lists that country's extra repeats.",
|
|
4684
4790
|
annotations: {
|
|
4685
4791
|
title: "Create Bulk Scans",
|
|
4686
4792
|
readOnlyHint: false,
|
|
@@ -4696,8 +4802,10 @@ var createBulkScansTool = {
|
|
|
4696
4802
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
4697
4803
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4698
4804
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4805
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4699
4806
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
4700
|
-
...input.labels !== void 0 ? { labels: input.labels } : {}
|
|
4807
|
+
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
4808
|
+
...pickRepeatRetryBody(input)
|
|
4701
4809
|
};
|
|
4702
4810
|
const result = await ctx.api.createBulkScans(body);
|
|
4703
4811
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -4714,6 +4822,7 @@ var CreateScanInputShape = {
|
|
|
4714
4822
|
vast_tag: z.string().optional().describe(
|
|
4715
4823
|
"VAST video ad tag: an http(s) URL of a VAST endpoint OR raw VAST XML (a document containing a <VAST> element). Fetched and played in a real browser. Provide exactly one of `url`, `ad_tag`, or `vast_tag`."
|
|
4716
4824
|
),
|
|
4825
|
+
referrer: scanReferrerField,
|
|
4717
4826
|
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
|
|
4718
4827
|
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
|
|
4719
4828
|
proxy: scanProxyField,
|
|
@@ -4722,11 +4831,12 @@ var CreateScanInputShape = {
|
|
|
4722
4831
|
run_id: z.string().uuid().optional().describe("Optional run UUID inside the campaign."),
|
|
4723
4832
|
ad_discovery: z.boolean().optional().describe(
|
|
4724
4833
|
"Publisher ad discovery: detect ad blocks on the page and spawn one child scan per detected ad (banner/pop). Only valid with `url`. Each child is a separate billed scan; list them with `list_scan_children`."
|
|
4725
|
-
)
|
|
4834
|
+
),
|
|
4835
|
+
...repeatRetryFields
|
|
4726
4836
|
};
|
|
4727
4837
|
var createScanTool = {
|
|
4728
4838
|
name: "create_scan",
|
|
4729
|
-
description: "Queue a single new scan for a URL, ad-tag, or VAST video tag against one country. COSTS CREDITS and bills the caller's organization. Returns the newly-created scan record.",
|
|
4839
|
+
description: "Queue a single new scan for a URL, ad-tag, or VAST video tag against one country. Takes one target and one country \u2014 there is no multi-URL form; call it once per URL. COSTS CREDITS and bills the caller's organization: this call creates exactly `repeat_count` scans (default 1) and bills every one of them. Returns the newly-created scan record; when `repeat_count` > 1 the response's `repeat_scan_ids` lists the sibling scans this call also created.",
|
|
4730
4840
|
annotations: {
|
|
4731
4841
|
title: "Create Scan",
|
|
4732
4842
|
readOnlyHint: false,
|
|
@@ -4742,11 +4852,13 @@ var createScanTool = {
|
|
|
4742
4852
|
...input.url !== void 0 ? { url: input.url } : {},
|
|
4743
4853
|
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
4744
4854
|
...input.vast_tag !== void 0 ? { vast_tag: input.vast_tag } : {},
|
|
4855
|
+
...input.referrer !== void 0 ? { referrer: input.referrer } : {},
|
|
4745
4856
|
...input.proxy !== void 0 ? { proxy: input.proxy } : {},
|
|
4746
4857
|
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
4747
4858
|
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
4748
4859
|
...input.run_id !== void 0 ? { run_id: input.run_id } : {},
|
|
4749
|
-
...input.ad_discovery !== void 0 ? { ad_discovery: input.ad_discovery } : {}
|
|
4860
|
+
...input.ad_discovery !== void 0 ? { ad_discovery: input.ad_discovery } : {},
|
|
4861
|
+
...pickRepeatRetryBody(input)
|
|
4750
4862
|
};
|
|
4751
4863
|
const result = await ctx.api.createScan(body);
|
|
4752
4864
|
if (result.isErr()) return err(mapApiError(result.error));
|
|
@@ -4758,7 +4870,7 @@ var GetScanInputShape = {
|
|
|
4758
4870
|
};
|
|
4759
4871
|
var getScanTool = {
|
|
4760
4872
|
name: "get_scan",
|
|
4761
|
-
description: "Get full detail for one scan by UUID: status, offer URL, absolute screenshot URL, report_url + public_report_url deep-links, timing, labels, and the parent campaign if any. VAST video scans also carry `vast_tag`, `creative_kind` (banner
|
|
4873
|
+
description: "Get full detail for one scan by UUID: status, offer URL, absolute screenshot URL, report_url + public_report_url deep-links, timing, labels, and the parent campaign if any. VAST video scans also carry `vast_tag`, `creative_kind` (`banner` or `video` today; treat it as an open string), and a `video` block (duration, media-file URL, the creative's `click_through` destination, VAST version, ad system, VPAID flag, wrapper depth). Repeats and retries: `repeat_index` / `repeat_total` place this scan inside its repeat group (0-based, so 2 of 5 reads as repeat_index 1), a non-null `repeat_session_id` means the group ran in `shared` mode (same browser, same IP, cookies carried over) and is the key to correlate its members \u2014 a shared group of one has nothing to carry over, so it stays null \u2014 `repeat_scan_ids` is filled only on the create response, and `retry_attempt` / `retry_max_attempts` show how many technical re-crawls this scan already consumed. Link users with the returned `report_url` / `public_report_url` \u2014 never construct URLs yourself.",
|
|
4762
4874
|
annotations: {
|
|
4763
4875
|
title: "Get Scan",
|
|
4764
4876
|
readOnlyHint: true,
|
|
@@ -4797,7 +4909,9 @@ var getScanCreativeScreenshotTool = {
|
|
|
4797
4909
|
var GetScanLandingScreenshotInputShape = {
|
|
4798
4910
|
scan_id: z.string().uuid().describe("Scan UUID (ad-tag scan)."),
|
|
4799
4911
|
landing_ord: z.number().int().min(0).max(50).describe("0-indexed landing slot (see `get_scan(...).landings[*].ord`)."),
|
|
4800
|
-
width: z.number().int().min(50).max(2e3).optional().describe(
|
|
4912
|
+
width: z.number().int().min(50).max(2e3).optional().describe(
|
|
4913
|
+
"Optional resize width in pixels. The API top-crops a resized landing screenshot whose height exceeds 2.5x its width, so a long landing page comes back as its top region only \u2014 never conclude that something is absent from the page from a resized capture. Fetch without `width` when you need the whole page."
|
|
4914
|
+
)
|
|
4801
4915
|
};
|
|
4802
4916
|
var getScanLandingScreenshotTool = {
|
|
4803
4917
|
name: "get_scan_landing_screenshot",
|
|
@@ -4823,7 +4937,7 @@ var getScanLandingScreenshotTool = {
|
|
|
4823
4937
|
var GetScanScreenshotInputShape = {
|
|
4824
4938
|
scan_id: z.string().uuid().describe("Scan UUID."),
|
|
4825
4939
|
width: z.number().int().min(50).max(2e3).optional().describe(
|
|
4826
|
-
"Optional resize width in pixels. The API resizes server-side to keep the image small for the agent's context window."
|
|
4940
|
+
"Optional resize width in pixels. The API resizes server-side to keep the image small for the agent's context window. It also top-crops a resized page screenshot whose height exceeds 2.5x its width, so a long page comes back as its top region only \u2014 never conclude that something is absent from the page from a resized capture. Fetch without `width` when you need the whole page."
|
|
4827
4941
|
)
|
|
4828
4942
|
};
|
|
4829
4943
|
var getScanScreenshotTool = {
|
|
@@ -4850,7 +4964,7 @@ var ListScanChildrenInputShape = {
|
|
|
4850
4964
|
};
|
|
4851
4965
|
var listScanChildrenTool = {
|
|
4852
4966
|
name: "list_scan_children",
|
|
4853
|
-
description: "List the discovered-ad child scans of a publisher ad-discovery scan \u2014 one per detected ad block on the page. Each child brief carries ad_kind (banner|pop), the attributed ad network, and slot_index. Returns a paginated envelope with screenshot + report deep-links; link users with those, never hand-build URLs.",
|
|
4967
|
+
description: "List the discovered-ad child scans of a publisher ad-discovery scan \u2014 one per detected ad block on the page. Each child brief carries ad_kind (banner|pop), the attributed ad network, and slot_index. The repeat / retry fields are present but never meaningful here: a child is always its own group of one (`repeat_index` 0, `repeat_total` 1, `repeat_session_id` null) and is never re-crawled, because repeats and retries apply to the publisher scan \u2014 repeating an ad-discovery scan gives you several parents, each with its own children. Returns a paginated envelope with screenshot + report deep-links; link users with those, never hand-build URLs.",
|
|
4854
4968
|
annotations: {
|
|
4855
4969
|
title: "List Scan Children",
|
|
4856
4970
|
readOnlyHint: true,
|
|
@@ -4901,7 +5015,7 @@ var ListScansInputShape = {
|
|
|
4901
5015
|
};
|
|
4902
5016
|
var listScansTool = {
|
|
4903
5017
|
name: "list_scans",
|
|
4904
|
-
description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range, run/campaign/group, tag, AI/IAB/brand category, dynamic labels). Returns a paginated envelope; each brief carries `is_ad_tag` and `is_vast` flags, an absolute screenshot URL plus report_url (auth dashboard) and public_report_url (shareable) deep-links \u2014 link users with those, never hand-build URLs.",
|
|
5018
|
+
description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range, run/campaign/group, tag, AI/IAB/brand category, dynamic labels). Returns a paginated envelope; each brief carries `is_ad_tag` and `is_vast` flags, an absolute screenshot URL plus report_url (auth dashboard) and public_report_url (shareable) deep-links \u2014 link users with those, never hand-build URLs. Repeated scans look like near-duplicate rows: `repeat_index` / `repeat_total` place each one inside its repeat group (0-based) and a non-null `repeat_session_id` means the group ran in `shared` mode (one browser, one IP, cookies carried across repeats) \u2014 group by it to compare the repeats of one combination. `retry_attempt` / `retry_max_attempts` count technical re-crawls of that same scan, not extra scans. `repeat_scan_ids` is not part of this response; use `get_scan` for a single scan's full detail.",
|
|
4905
5019
|
annotations: {
|
|
4906
5020
|
title: "List Scans",
|
|
4907
5021
|
readOnlyHint: true,
|
|
@@ -5535,5 +5649,5 @@ function formatToolError(error) {
|
|
|
5535
5649
|
}
|
|
5536
5650
|
|
|
5537
5651
|
export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
5538
|
-
//# sourceMappingURL=chunk-
|
|
5539
|
-
//# sourceMappingURL=chunk-
|
|
5652
|
+
//# sourceMappingURL=chunk-XCAKB2QW.js.map
|
|
5653
|
+
//# sourceMappingURL=chunk-XCAKB2QW.js.map
|