@kaminari-ad/mcp 0.2.0 → 0.2.2
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 +93 -1
- package/README.md +28 -6
- package/dist/bin.js +47 -5706
- package/dist/bin.js.map +1 -1
- package/dist/chunk-4MNLSWSZ.js +4289 -0
- package/dist/chunk-4MNLSWSZ.js.map +1 -0
- package/dist/chunk-BAZQPU6T.js +10 -0
- package/dist/chunk-BAZQPU6T.js.map +1 -0
- package/dist/http-bootstrap-PW4K2NDQ.js +321 -0
- package/dist/http-bootstrap-PW4K2NDQ.js.map +1 -0
- package/dist/stdio-bootstrap-PSX3TI3I.js +48 -0
- package/dist/stdio-bootstrap-PSX3TI3I.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,4289 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { err, ok } from './chunk-BAZQPU6T.js';
|
|
3
|
+
import { createHash, randomUUID } from 'crypto';
|
|
4
|
+
import createClient from 'openapi-fetch';
|
|
5
|
+
import { fetch } from 'undici';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { stdTimeFunctions, pino } from 'pino';
|
|
8
|
+
import pinoPretty from 'pino-pretty';
|
|
9
|
+
import { ListResourcesRequestSchema, ListPromptsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
|
|
11
|
+
var REDACTED = "[BearerToken redacted]";
|
|
12
|
+
var BEARER_HASH_PREFIX_LEN = 8;
|
|
13
|
+
var MAX_HEADER_LEN = 4096;
|
|
14
|
+
var BearerToken = class _BearerToken {
|
|
15
|
+
#raw;
|
|
16
|
+
constructor(raw) {
|
|
17
|
+
this.#raw = raw;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Construct from a raw string. Trims surrounding whitespace; rejects
|
|
21
|
+
* empty input. Does NOT validate format — the API is the single
|
|
22
|
+
* source of truth for token validity.
|
|
23
|
+
*/
|
|
24
|
+
static fromString(raw) {
|
|
25
|
+
const trimmed = raw.trim();
|
|
26
|
+
if (trimmed.length === 0) return void 0;
|
|
27
|
+
return new _BearerToken(trimmed);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse from a raw `Authorization` header value. Returns `undefined`
|
|
31
|
+
* for missing / malformed input (anything not `Bearer <token>`).
|
|
32
|
+
*
|
|
33
|
+
* **Normalization policy.** The regex is case-insensitive on the
|
|
34
|
+
* `Bearer` scheme (per RFC 6750 §2.1, the scheme name is
|
|
35
|
+
* case-insensitive) but the outbound header is always re-emitted
|
|
36
|
+
* with the canonical capitalization (`Bearer <token>`) by
|
|
37
|
+
* {@link toAuthorizationHeader}. We intentionally do NOT preserve
|
|
38
|
+
* the inbound casing byte-for-byte:
|
|
39
|
+
*
|
|
40
|
+
* - The Kaminari Ad API accepts the canonical form.
|
|
41
|
+
* - Outbound canonicalization simplifies any future signature /
|
|
42
|
+
* proxy that re-hashes the header.
|
|
43
|
+
* - The TOKEN value itself is preserved exactly (the capture
|
|
44
|
+
* group's `\S+` keeps the secret intact); only the SCHEME word
|
|
45
|
+
* is normalized.
|
|
46
|
+
*
|
|
47
|
+
* Token length is capped at {@link MAX_TOKEN_LEN} bytes to keep a
|
|
48
|
+
* pathological client from blowing the heap; longer headers are
|
|
49
|
+
* rejected as malformed.
|
|
50
|
+
*/
|
|
51
|
+
static fromAuthorizationHeader(headerValue) {
|
|
52
|
+
if (headerValue === void 0) return void 0;
|
|
53
|
+
if (headerValue.length > MAX_HEADER_LEN) return void 0;
|
|
54
|
+
const match = /^Bearer\s+(\S+)\s*$/i.exec(headerValue);
|
|
55
|
+
if (match?.[1] === void 0) return void 0;
|
|
56
|
+
return _BearerToken.fromString(match[1]);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the first {@link BEARER_HASH_PREFIX_LEN} hex chars of
|
|
60
|
+
* `sha256(token)`. Used as the `bearer_hash` log field.
|
|
61
|
+
*/
|
|
62
|
+
hash() {
|
|
63
|
+
return createHash("sha256").update(this.#raw).digest("hex").slice(0, BEARER_HASH_PREFIX_LEN);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns the full SHA-256 hex digest. Used as the session-binding
|
|
67
|
+
* key in {@link SessionStore}. Not for logs.
|
|
68
|
+
*/
|
|
69
|
+
fullHash() {
|
|
70
|
+
return createHash("sha256").update(this.#raw).digest("hex");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the literal `Authorization` header value. Use ONLY when
|
|
74
|
+
* constructing an outbound HTTP request to the API.
|
|
75
|
+
*/
|
|
76
|
+
toAuthorizationHeader() {
|
|
77
|
+
return `Bearer ${this.#raw}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Redaction. Logger / JSON serialisation / template strings see the
|
|
81
|
+
* placeholder, not the token.
|
|
82
|
+
*/
|
|
83
|
+
toString() {
|
|
84
|
+
return REDACTED;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* `JSON.stringify(bearer)` -> `"[BearerToken redacted]"`. Same
|
|
88
|
+
* intent as {@link toString}: any path that serializes the VO never
|
|
89
|
+
* sees the secret.
|
|
90
|
+
*/
|
|
91
|
+
toJSON() {
|
|
92
|
+
return REDACTED;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Node's `util.inspect` hook — keeps `console.log(token)` /
|
|
96
|
+
* structured logs from accidentally revealing the secret.
|
|
97
|
+
*/
|
|
98
|
+
[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
|
|
99
|
+
return REDACTED;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
function newRequestId() {
|
|
103
|
+
return randomUUID();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/infrastructure/api/error-mapping.ts
|
|
107
|
+
function detail(parsed) {
|
|
108
|
+
if (parsed === null || typeof parsed !== "object") return "Upstream error";
|
|
109
|
+
if (!("detail" in parsed)) return "Upstream error";
|
|
110
|
+
const d = parsed.detail;
|
|
111
|
+
if (typeof d === "string") return d;
|
|
112
|
+
if (Array.isArray(d)) {
|
|
113
|
+
const messages = d.map((entry) => {
|
|
114
|
+
if (entry === null || typeof entry !== "object") return void 0;
|
|
115
|
+
const e = entry;
|
|
116
|
+
const loc = Array.isArray(e.loc) ? e.loc.filter((p) => typeof p === "string").join(".") : "";
|
|
117
|
+
const msg = typeof e.msg === "string" ? e.msg : "";
|
|
118
|
+
if (loc === "" && msg === "") return void 0;
|
|
119
|
+
return loc === "" ? msg : `${loc}: ${msg}`;
|
|
120
|
+
}).filter((m) => m !== void 0);
|
|
121
|
+
if (messages.length > 0) return messages.join("; ");
|
|
122
|
+
}
|
|
123
|
+
return "Upstream error";
|
|
124
|
+
}
|
|
125
|
+
function errCode(parsed) {
|
|
126
|
+
if (parsed !== null && typeof parsed === "object" && "code" in parsed) {
|
|
127
|
+
const c = parsed.code;
|
|
128
|
+
if (typeof c === "string") return c;
|
|
129
|
+
}
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
function toApiError(status2, parsed, retryAfterHeader) {
|
|
133
|
+
const message = detail(parsed);
|
|
134
|
+
if (status2 === 401) return { kind: "unauthorized", detail: message };
|
|
135
|
+
if (status2 === 403) {
|
|
136
|
+
const code = errCode(parsed);
|
|
137
|
+
return code === void 0 ? { kind: "forbidden", detail: message } : { kind: "forbidden", detail: message, code };
|
|
138
|
+
}
|
|
139
|
+
if (status2 === 404) return { kind: "not-found", detail: message };
|
|
140
|
+
if (status2 === 422 || status2 === 400) {
|
|
141
|
+
const code = errCode(parsed);
|
|
142
|
+
return code === void 0 ? { kind: "invalid-input", detail: message } : { kind: "invalid-input", detail: message, code };
|
|
143
|
+
}
|
|
144
|
+
if (status2 === 429) {
|
|
145
|
+
const ra = Array.isArray(retryAfterHeader) ? retryAfterHeader[0] : retryAfterHeader;
|
|
146
|
+
const retryMs = ra === void 0 ? void 0 : Number.parseInt(ra, 10) * 1e3;
|
|
147
|
+
return retryMs === void 0 || Number.isNaN(retryMs) ? { kind: "rate-limited", detail: message } : { kind: "rate-limited", detail: message, retryAfterMs: retryMs };
|
|
148
|
+
}
|
|
149
|
+
return { kind: "upstream", detail: message, status: status2 };
|
|
150
|
+
}
|
|
151
|
+
var OrgResponse = z.object({
|
|
152
|
+
id: z.string().uuid(),
|
|
153
|
+
name: z.string(),
|
|
154
|
+
owner_id: z.string().uuid(),
|
|
155
|
+
is_active: z.boolean(),
|
|
156
|
+
created_at: z.string().datetime({ offset: true })
|
|
157
|
+
}).passthrough();
|
|
158
|
+
z.object({ name: z.union([z.string(), z.null()]) }).partial().passthrough();
|
|
159
|
+
var ValidationError = z.object({
|
|
160
|
+
loc: z.array(z.union([z.string(), z.number()])),
|
|
161
|
+
msg: z.string(),
|
|
162
|
+
type: z.string(),
|
|
163
|
+
input: z.unknown().optional(),
|
|
164
|
+
ctx: z.object({}).partial().passthrough().optional()
|
|
165
|
+
}).passthrough();
|
|
166
|
+
z.object({ detail: z.array(ValidationError) }).partial().passthrough();
|
|
167
|
+
z.object({
|
|
168
|
+
key: z.string(),
|
|
169
|
+
display_name: z.string(),
|
|
170
|
+
position: z.number().int(),
|
|
171
|
+
auto_extract: z.boolean()
|
|
172
|
+
}).passthrough();
|
|
173
|
+
var LabelDefinitionItem = z.object({
|
|
174
|
+
key: z.string().max(50),
|
|
175
|
+
display_name: z.string().max(100),
|
|
176
|
+
auto_extract: z.boolean().optional().default(false)
|
|
177
|
+
}).passthrough();
|
|
178
|
+
z.object({ labels: z.array(LabelDefinitionItem) }).passthrough();
|
|
179
|
+
var UserResponse = z.object({
|
|
180
|
+
id: z.string().uuid(),
|
|
181
|
+
email: z.string(),
|
|
182
|
+
name: z.string(),
|
|
183
|
+
role_name: z.string(),
|
|
184
|
+
is_active: z.boolean(),
|
|
185
|
+
created_at: z.string().datetime({ offset: true })
|
|
186
|
+
}).passthrough();
|
|
187
|
+
z.object({
|
|
188
|
+
email: z.string().max(254),
|
|
189
|
+
name: z.string().max(200).optional().default(""),
|
|
190
|
+
role_id: z.string().uuid(),
|
|
191
|
+
timezone: z.union([z.string(), z.null()]).optional()
|
|
192
|
+
});
|
|
193
|
+
z.object({ role_id: z.string().uuid() }).passthrough();
|
|
194
|
+
var ApiKeyResponse = z.object({
|
|
195
|
+
id: z.string().uuid(),
|
|
196
|
+
key_prefix: z.string(),
|
|
197
|
+
name: z.string(),
|
|
198
|
+
expires_at: z.union([z.string(), z.null()]),
|
|
199
|
+
created_at: z.string().datetime({ offset: true })
|
|
200
|
+
}).passthrough();
|
|
201
|
+
z.object({
|
|
202
|
+
name: z.string(),
|
|
203
|
+
expires_at: z.union([z.string(), z.null()]).optional()
|
|
204
|
+
}).passthrough();
|
|
205
|
+
var ApiKeyCreatedResponse = z.object({
|
|
206
|
+
id: z.string().uuid(),
|
|
207
|
+
key_prefix: z.string(),
|
|
208
|
+
full_key: z.string(),
|
|
209
|
+
name: z.string(),
|
|
210
|
+
expires_at: z.union([z.string(), z.null()]),
|
|
211
|
+
created_at: z.string().datetime({ offset: true })
|
|
212
|
+
}).passthrough();
|
|
213
|
+
var RoleResponse = z.object({
|
|
214
|
+
id: z.string().uuid(),
|
|
215
|
+
name: z.string(),
|
|
216
|
+
scope: z.string(),
|
|
217
|
+
is_system: z.boolean(),
|
|
218
|
+
permissions: z.array(z.string())
|
|
219
|
+
}).passthrough();
|
|
220
|
+
z.object({ name: z.string(), permissions: z.array(z.string()) }).passthrough();
|
|
221
|
+
var ProxyTargetRequest = z.object({
|
|
222
|
+
proxy_type: z.string().default("residential"),
|
|
223
|
+
region: z.string().default(""),
|
|
224
|
+
city: z.string().default(""),
|
|
225
|
+
isp: z.string().default("")
|
|
226
|
+
}).partial().passthrough();
|
|
227
|
+
z.object({
|
|
228
|
+
url: z.union([z.string(), z.null()]).optional(),
|
|
229
|
+
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
230
|
+
country_code: z.string().min(2).max(2),
|
|
231
|
+
emulator_id: z.string().min(1).max(100),
|
|
232
|
+
proxy: ProxyTargetRequest.optional(),
|
|
233
|
+
labels: z.record(z.string()).optional(),
|
|
234
|
+
campaign_id: z.union([z.string(), z.null()]).optional(),
|
|
235
|
+
run_id: z.union([z.string(), z.null()]).optional()
|
|
236
|
+
}).passthrough();
|
|
237
|
+
var ScanStatus = z.enum([
|
|
238
|
+
"pending",
|
|
239
|
+
"running",
|
|
240
|
+
"crawled",
|
|
241
|
+
"checking",
|
|
242
|
+
"checking_async",
|
|
243
|
+
"completed",
|
|
244
|
+
"partial",
|
|
245
|
+
"failed",
|
|
246
|
+
"cancelled"
|
|
247
|
+
]);
|
|
248
|
+
var SubRequestResponse = z.lazy(
|
|
249
|
+
() => z.object({
|
|
250
|
+
url: z.string(),
|
|
251
|
+
resource_type: z.string(),
|
|
252
|
+
status_code: z.number().int(),
|
|
253
|
+
content_type: z.string(),
|
|
254
|
+
body_size: z.number().int(),
|
|
255
|
+
timestamp_ms: z.number().int(),
|
|
256
|
+
children: z.array(SubRequestResponse).optional()
|
|
257
|
+
}).passthrough()
|
|
258
|
+
);
|
|
259
|
+
var RedirectHopResponse = z.object({
|
|
260
|
+
url: z.string(),
|
|
261
|
+
status_code: z.number().int(),
|
|
262
|
+
content_type: z.string(),
|
|
263
|
+
body_size: z.number().int(),
|
|
264
|
+
timestamp_ms: z.number().int(),
|
|
265
|
+
redirected_from: z.string(),
|
|
266
|
+
sub_requests: z.array(SubRequestResponse)
|
|
267
|
+
}).passthrough();
|
|
268
|
+
var ProxyTargetResponse = z.object({
|
|
269
|
+
proxy_type: z.string(),
|
|
270
|
+
region: z.string().optional().default(""),
|
|
271
|
+
city: z.string().optional().default(""),
|
|
272
|
+
isp: z.string().optional().default("")
|
|
273
|
+
}).passthrough();
|
|
274
|
+
var IabCategoryResponse = z.object({
|
|
275
|
+
tier1: z.string(),
|
|
276
|
+
tier2: z.union([z.string(), z.null()]).optional(),
|
|
277
|
+
tier3: z.union([z.string(), z.null()]).optional(),
|
|
278
|
+
tier4: z.union([z.string(), z.null()]).optional()
|
|
279
|
+
}).passthrough();
|
|
280
|
+
var ScanClassificationResponse = z.object({
|
|
281
|
+
brand: z.union([z.string(), z.null()]),
|
|
282
|
+
iab_v2: z.union([IabCategoryResponse, z.null()]),
|
|
283
|
+
iab_v3: z.union([IabCategoryResponse, z.null()])
|
|
284
|
+
}).partial().passthrough();
|
|
285
|
+
var LandingResponse = z.object({
|
|
286
|
+
ord: z.number().int(),
|
|
287
|
+
opener_url: z.string().optional().default(""),
|
|
288
|
+
final_url: z.string().optional().default(""),
|
|
289
|
+
offer_url: z.string().optional().default(""),
|
|
290
|
+
page_title: z.string().optional().default(""),
|
|
291
|
+
screenshot_url: z.string().optional().default(""),
|
|
292
|
+
redirect_chain: z.array(RedirectHopResponse).optional(),
|
|
293
|
+
elapsed_ms: z.number().int().optional().default(0),
|
|
294
|
+
created_at: z.string().datetime({ offset: true })
|
|
295
|
+
}).passthrough();
|
|
296
|
+
var ScanResponse = z.object({
|
|
297
|
+
id: z.string().uuid(),
|
|
298
|
+
url: z.string(),
|
|
299
|
+
country_code: z.string(),
|
|
300
|
+
emulator_id: z.string(),
|
|
301
|
+
status: ScanStatus,
|
|
302
|
+
offer_url: z.string(),
|
|
303
|
+
redirect_chain: z.array(RedirectHopResponse),
|
|
304
|
+
screenshot_url: z.string().optional().default(""),
|
|
305
|
+
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
306
|
+
creative_screenshot_url: z.string().optional().default(""),
|
|
307
|
+
creative_width: z.number().int().optional().default(0),
|
|
308
|
+
creative_height: z.number().int().optional().default(0),
|
|
309
|
+
proxy: z.union([ProxyTargetResponse, z.null()]).optional(),
|
|
310
|
+
page_title: z.string(),
|
|
311
|
+
elapsed_ms: z.number().int(),
|
|
312
|
+
error: z.string(),
|
|
313
|
+
labels: z.record(z.string()).optional(),
|
|
314
|
+
classification: z.union([ScanClassificationResponse, z.null()]).optional(),
|
|
315
|
+
campaign_id: z.union([z.string(), z.null()]).optional(),
|
|
316
|
+
campaign_name: z.union([z.string(), z.null()]).optional(),
|
|
317
|
+
created_at: z.string().datetime({ offset: true }),
|
|
318
|
+
completed_at: z.union([z.string(), z.null()]),
|
|
319
|
+
landings: z.array(LandingResponse).optional()
|
|
320
|
+
}).passthrough();
|
|
321
|
+
z.union([z.string(), z.null()]).optional();
|
|
322
|
+
var ScanBriefResponse = z.object({
|
|
323
|
+
id: z.string().uuid(),
|
|
324
|
+
url: z.string(),
|
|
325
|
+
country_code: z.string(),
|
|
326
|
+
proxy_type: z.string().optional().default("residential"),
|
|
327
|
+
status: ScanStatus,
|
|
328
|
+
offer_url: z.string(),
|
|
329
|
+
screenshot_url: z.string().optional().default(""),
|
|
330
|
+
labels: z.record(z.string()).optional(),
|
|
331
|
+
classification: z.union([ScanClassificationResponse, z.null()]).optional(),
|
|
332
|
+
elapsed_ms: z.number().int(),
|
|
333
|
+
created_at: z.string().datetime({ offset: true }),
|
|
334
|
+
campaign_id: z.union([z.string(), z.null()]).optional(),
|
|
335
|
+
campaign_name: z.union([z.string(), z.null()]).optional(),
|
|
336
|
+
is_ad_tag: z.boolean().optional().default(false)
|
|
337
|
+
}).passthrough();
|
|
338
|
+
z.object({
|
|
339
|
+
items: z.array(ScanBriefResponse),
|
|
340
|
+
total: z.number().int(),
|
|
341
|
+
page: z.number().int(),
|
|
342
|
+
limit: z.number().int(),
|
|
343
|
+
pages: z.number().int()
|
|
344
|
+
}).passthrough();
|
|
345
|
+
z.object({
|
|
346
|
+
url: z.union([z.string(), z.null()]).optional(),
|
|
347
|
+
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
348
|
+
country_codes: z.array(z.string()).min(1),
|
|
349
|
+
emulator_id: z.string().min(1).max(100),
|
|
350
|
+
proxy: ProxyTargetRequest.optional(),
|
|
351
|
+
labels: z.record(z.string()).optional()
|
|
352
|
+
}).passthrough();
|
|
353
|
+
z.object({
|
|
354
|
+
scope_type: z.enum(["last_n", "hours"]),
|
|
355
|
+
scope_value: z.number().int().gt(0)
|
|
356
|
+
}).passthrough();
|
|
357
|
+
z.object({ queued_count: z.number().int() }).passthrough();
|
|
358
|
+
z.object({ cancelled_count: z.number().int() }).passthrough();
|
|
359
|
+
z.union([z.number(), z.null()]).optional();
|
|
360
|
+
var GeoResponse = z.object({
|
|
361
|
+
country_code: z.string(),
|
|
362
|
+
name: z.string(),
|
|
363
|
+
region: z.string(),
|
|
364
|
+
tier: z.string()
|
|
365
|
+
}).passthrough();
|
|
366
|
+
var EmulatorResponse = z.object({
|
|
367
|
+
id: z.string(),
|
|
368
|
+
display_name: z.string(),
|
|
369
|
+
category: z.string(),
|
|
370
|
+
browser: z.string()
|
|
371
|
+
}).passthrough();
|
|
372
|
+
z.object({ name: z.string().min(1).max(200) }).passthrough();
|
|
373
|
+
var CampaignGroupResponse = z.object({
|
|
374
|
+
id: z.string().uuid(),
|
|
375
|
+
name: z.string(),
|
|
376
|
+
is_default: z.boolean(),
|
|
377
|
+
is_archived: z.boolean(),
|
|
378
|
+
schedule_paused: z.boolean(),
|
|
379
|
+
created_at: z.string().datetime({ offset: true }),
|
|
380
|
+
campaign_count: z.union([z.number(), z.null()]).optional()
|
|
381
|
+
}).passthrough();
|
|
382
|
+
z.object({ name: z.union([z.string(), z.null()]) }).partial().passthrough();
|
|
383
|
+
var BulkCampaignFailure = z.object({
|
|
384
|
+
campaign_id: z.string().uuid(),
|
|
385
|
+
error_code: z.string(),
|
|
386
|
+
detail: z.string()
|
|
387
|
+
}).passthrough();
|
|
388
|
+
var GroupActionResponse = z.object({
|
|
389
|
+
group_id: z.string().uuid(),
|
|
390
|
+
affected_campaigns: z.number().int(),
|
|
391
|
+
cancelled_count: z.number().int().optional().default(0),
|
|
392
|
+
run_ids: z.array(z.string().uuid()).optional(),
|
|
393
|
+
failures: z.array(BulkCampaignFailure).optional()
|
|
394
|
+
}).passthrough();
|
|
395
|
+
z.object({
|
|
396
|
+
name: z.string().min(1).max(200),
|
|
397
|
+
campaign_type: z.string().optional().default("url"),
|
|
398
|
+
url: z.union([z.string(), z.null()]).optional(),
|
|
399
|
+
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
400
|
+
country_codes: z.array(z.string()).min(1),
|
|
401
|
+
group_id: z.union([z.string(), z.null()]).optional(),
|
|
402
|
+
emulator_categories: z.array(z.string()).optional(),
|
|
403
|
+
emulator_specific_ids: z.array(z.string()).optional(),
|
|
404
|
+
emulator_mode: z.string().optional().default("random"),
|
|
405
|
+
proxy_type: z.string().optional().default("residential"),
|
|
406
|
+
proxy_region: z.string().optional().default(""),
|
|
407
|
+
proxy_city: z.string().optional().default(""),
|
|
408
|
+
proxy_isp: z.string().optional().default(""),
|
|
409
|
+
labels: z.record(z.string()).optional(),
|
|
410
|
+
policy_set_id: z.union([z.string(), z.null()]).optional(),
|
|
411
|
+
schedule_type: z.union([z.string(), z.null()]).optional(),
|
|
412
|
+
schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]).optional(),
|
|
413
|
+
schedule_interval_seconds: z.union([z.number(), z.null()]).optional(),
|
|
414
|
+
schedule_enabled: z.union([z.boolean(), z.null()]).optional(),
|
|
415
|
+
schedule_timezone: z.union([z.string(), z.null()]).optional()
|
|
416
|
+
}).passthrough();
|
|
417
|
+
var EmulatorSelectionResponse = z.object({
|
|
418
|
+
categories: z.array(z.string()),
|
|
419
|
+
specific_ids: z.array(z.string()),
|
|
420
|
+
mode: z.string()
|
|
421
|
+
}).passthrough();
|
|
422
|
+
var CampaignResponse = z.object({
|
|
423
|
+
id: z.string().uuid(),
|
|
424
|
+
name: z.string(),
|
|
425
|
+
campaign_type: z.string().optional().default("url"),
|
|
426
|
+
url: z.string(),
|
|
427
|
+
ad_tag: z.union([z.string(), z.null()]).optional(),
|
|
428
|
+
country_codes: z.array(z.string()),
|
|
429
|
+
group_id: z.string().uuid(),
|
|
430
|
+
emulator_selection: EmulatorSelectionResponse,
|
|
431
|
+
proxy_type: z.string().optional().default("residential"),
|
|
432
|
+
proxy_region: z.string().optional().default(""),
|
|
433
|
+
proxy_city: z.string().optional().default(""),
|
|
434
|
+
proxy_isp: z.string().optional().default(""),
|
|
435
|
+
schedule_type: z.union([z.string(), z.null()]).optional(),
|
|
436
|
+
schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]).optional(),
|
|
437
|
+
schedule_interval_seconds: z.union([z.number(), z.null()]).optional(),
|
|
438
|
+
schedule_timezone: z.union([z.string(), z.null()]).optional(),
|
|
439
|
+
labels: z.record(z.string()).optional(),
|
|
440
|
+
policy_set_id: z.union([z.string(), z.null()]).optional(),
|
|
441
|
+
schedule_enabled: z.boolean(),
|
|
442
|
+
is_archived: z.boolean(),
|
|
443
|
+
created_at: z.string().datetime({ offset: true }),
|
|
444
|
+
last_run_at: z.union([z.string(), z.null()]).optional()
|
|
445
|
+
}).passthrough();
|
|
446
|
+
z.object({
|
|
447
|
+
items: z.array(CampaignResponse),
|
|
448
|
+
total: z.number().int(),
|
|
449
|
+
page: z.number().int(),
|
|
450
|
+
limit: z.number().int(),
|
|
451
|
+
pages: z.number().int()
|
|
452
|
+
}).passthrough();
|
|
453
|
+
var CampaignPickerItem = z.object({
|
|
454
|
+
id: z.string().uuid(),
|
|
455
|
+
name: z.string(),
|
|
456
|
+
group_id: z.string().uuid(),
|
|
457
|
+
is_archived: z.boolean()
|
|
458
|
+
}).passthrough();
|
|
459
|
+
z.object({
|
|
460
|
+
name: z.union([z.string(), z.null()]),
|
|
461
|
+
url: z.union([z.string(), z.null()]),
|
|
462
|
+
ad_tag: z.union([z.string(), z.null()]),
|
|
463
|
+
country_codes: z.union([z.array(z.string()), z.null()]),
|
|
464
|
+
group_id: z.union([z.string(), z.null()]),
|
|
465
|
+
emulator_categories: z.union([z.array(z.string()), z.null()]),
|
|
466
|
+
emulator_specific_ids: z.union([z.array(z.string()), z.null()]),
|
|
467
|
+
emulator_mode: z.union([z.string(), z.null()]),
|
|
468
|
+
proxy_type: z.union([z.string(), z.null()]),
|
|
469
|
+
proxy_region: z.union([z.string(), z.null()]),
|
|
470
|
+
proxy_city: z.union([z.string(), z.null()]),
|
|
471
|
+
proxy_isp: z.union([z.string(), z.null()]),
|
|
472
|
+
labels: z.union([z.record(z.string()), z.null()]),
|
|
473
|
+
policy_set_id: z.union([z.string(), z.null()]),
|
|
474
|
+
schedule_type: z.union([z.string(), z.null()]),
|
|
475
|
+
schedule_weekly: z.union([z.record(z.array(z.number().int())), z.null()]),
|
|
476
|
+
schedule_interval_seconds: z.union([z.number(), z.null()]),
|
|
477
|
+
schedule_enabled: z.union([z.boolean(), z.null()]),
|
|
478
|
+
schedule_timezone: z.union([z.string(), z.null()])
|
|
479
|
+
}).partial().passthrough();
|
|
480
|
+
var RunSource = z.enum(["ui", "api"]);
|
|
481
|
+
var RunResponse = z.object({
|
|
482
|
+
id: z.string().uuid(),
|
|
483
|
+
campaign_id: z.string().uuid(),
|
|
484
|
+
label: z.string(),
|
|
485
|
+
total: z.number().int(),
|
|
486
|
+
completed: z.number().int(),
|
|
487
|
+
failed: z.number().int(),
|
|
488
|
+
partial: z.number().int(),
|
|
489
|
+
cancelled: z.number().int(),
|
|
490
|
+
source: RunSource,
|
|
491
|
+
created_at: z.string().datetime({ offset: true })
|
|
492
|
+
}).passthrough();
|
|
493
|
+
z.object({
|
|
494
|
+
items: z.array(RunResponse),
|
|
495
|
+
total: z.number().int(),
|
|
496
|
+
page: z.number().int(),
|
|
497
|
+
limit: z.number().int(),
|
|
498
|
+
pages: z.number().int()
|
|
499
|
+
}).passthrough();
|
|
500
|
+
var ScanTileResponse = z.object({
|
|
501
|
+
id: z.string().uuid(),
|
|
502
|
+
country_code: z.string(),
|
|
503
|
+
status: z.string(),
|
|
504
|
+
offer_url: z.string().optional().default(""),
|
|
505
|
+
screenshot_url: z.string().optional().default(""),
|
|
506
|
+
elapsed_ms: z.number().int().optional().default(0),
|
|
507
|
+
error: z.string().optional().default("")
|
|
508
|
+
}).passthrough();
|
|
509
|
+
z.object({
|
|
510
|
+
items: z.array(ScanTileResponse),
|
|
511
|
+
total: z.number().int(),
|
|
512
|
+
page: z.number().int(),
|
|
513
|
+
limit: z.number().int(),
|
|
514
|
+
pages: z.number().int()
|
|
515
|
+
}).passthrough();
|
|
516
|
+
var ScanTagResponse = z.object({
|
|
517
|
+
id: z.string().uuid(),
|
|
518
|
+
scan_id: z.string().uuid(),
|
|
519
|
+
tag_slug: z.string(),
|
|
520
|
+
detail: z.string(),
|
|
521
|
+
url: z.string().optional().default(""),
|
|
522
|
+
display_name: z.string().optional().default(""),
|
|
523
|
+
category: z.string().optional().default(""),
|
|
524
|
+
severity: z.string().optional().default(""),
|
|
525
|
+
created_at: z.string().datetime({ offset: true })
|
|
526
|
+
}).passthrough();
|
|
527
|
+
var TagDefinitionWithStatsResponse = z.object({
|
|
528
|
+
slug: z.string(),
|
|
529
|
+
category: z.string(),
|
|
530
|
+
source: z.string(),
|
|
531
|
+
display_name: z.string(),
|
|
532
|
+
description: z.string(),
|
|
533
|
+
is_system: z.boolean(),
|
|
534
|
+
organization_id: z.union([z.string(), z.null()]),
|
|
535
|
+
show_in_public_report: z.boolean(),
|
|
536
|
+
severity: z.string(),
|
|
537
|
+
scans_count: z.number().int(),
|
|
538
|
+
rules_count: z.number().int()
|
|
539
|
+
}).passthrough();
|
|
540
|
+
var LinkedRuleResponse = z.object({
|
|
541
|
+
id: z.string().uuid(),
|
|
542
|
+
name: z.string(),
|
|
543
|
+
rule_type: z.string(),
|
|
544
|
+
target: z.string(),
|
|
545
|
+
is_active: z.boolean()
|
|
546
|
+
}).passthrough();
|
|
547
|
+
var TagDefinitionDetailResponse = z.object({
|
|
548
|
+
slug: z.string(),
|
|
549
|
+
category: z.string(),
|
|
550
|
+
source: z.string(),
|
|
551
|
+
display_name: z.string(),
|
|
552
|
+
description: z.string(),
|
|
553
|
+
is_system: z.boolean(),
|
|
554
|
+
organization_id: z.union([z.string(), z.null()]),
|
|
555
|
+
show_in_public_report: z.boolean(),
|
|
556
|
+
severity: z.string(),
|
|
557
|
+
scans_count: z.number().int(),
|
|
558
|
+
rules_count: z.number().int(),
|
|
559
|
+
linked_rules: z.array(LinkedRuleResponse).optional()
|
|
560
|
+
}).passthrough();
|
|
561
|
+
var TagSeverity = z.enum(["high", "medium", "low"]);
|
|
562
|
+
z.object({
|
|
563
|
+
display_name: z.union([z.string(), z.null()]),
|
|
564
|
+
description: z.union([z.string(), z.null()]),
|
|
565
|
+
show_in_public_report: z.union([z.boolean(), z.null()]),
|
|
566
|
+
severity: z.union([TagSeverity, z.null()])
|
|
567
|
+
}).partial().passthrough();
|
|
568
|
+
z.object({
|
|
569
|
+
name: z.string().min(1).max(200),
|
|
570
|
+
tag_slug: z.string().max(100).optional().default(""),
|
|
571
|
+
rule_type: z.string().max(50),
|
|
572
|
+
config: z.object({}).partial().passthrough(),
|
|
573
|
+
target: z.string().max(30).optional().default("page")
|
|
574
|
+
}).passthrough();
|
|
575
|
+
var CustomRuleResponse = z.object({
|
|
576
|
+
id: z.string().uuid(),
|
|
577
|
+
organization_id: z.string().uuid(),
|
|
578
|
+
name: z.string(),
|
|
579
|
+
tag_slug: z.string(),
|
|
580
|
+
rule_type: z.string(),
|
|
581
|
+
config: z.object({}).partial().passthrough(),
|
|
582
|
+
target: z.string(),
|
|
583
|
+
is_active: z.boolean(),
|
|
584
|
+
created_at: z.string().datetime({ offset: true })
|
|
585
|
+
}).passthrough();
|
|
586
|
+
z.object({
|
|
587
|
+
items: z.array(CustomRuleResponse),
|
|
588
|
+
total: z.number().int(),
|
|
589
|
+
page: z.number().int(),
|
|
590
|
+
limit: z.number().int(),
|
|
591
|
+
pages: z.number().int()
|
|
592
|
+
}).passthrough();
|
|
593
|
+
z.object({
|
|
594
|
+
name: z.union([z.string(), z.null()]),
|
|
595
|
+
tag_slug: z.union([z.string(), z.null()]),
|
|
596
|
+
config: z.union([z.object({}).partial().passthrough(), z.null()]),
|
|
597
|
+
target: z.union([z.string(), z.null()]),
|
|
598
|
+
is_active: z.union([z.boolean(), z.null()])
|
|
599
|
+
}).partial().passthrough();
|
|
600
|
+
z.object({
|
|
601
|
+
scan_id: z.string().uuid(),
|
|
602
|
+
rule_type: z.string(),
|
|
603
|
+
config: z.object({}).partial().passthrough(),
|
|
604
|
+
target: z.string().optional().default("page")
|
|
605
|
+
}).passthrough();
|
|
606
|
+
var RuleTestTagResult = z.object({ tag_slug: z.string(), detail: z.string().optional().default("") }).passthrough();
|
|
607
|
+
var RuleTestResponse = z.object({
|
|
608
|
+
matched: z.boolean(),
|
|
609
|
+
tags: z.array(RuleTestTagResult),
|
|
610
|
+
elapsed_ms: z.number().int(),
|
|
611
|
+
llm_failed: z.boolean().optional().default(false),
|
|
612
|
+
llm_call_id: z.union([z.string(), z.null()]).optional(),
|
|
613
|
+
llm_prompt_url: z.string().optional().default(""),
|
|
614
|
+
llm_response_url: z.string().optional().default("")
|
|
615
|
+
}).passthrough();
|
|
616
|
+
var PolicyEntryRequest = z.object({
|
|
617
|
+
tag_slug: z.string().min(1).max(100),
|
|
618
|
+
country_codes: z.array(z.string()).max(50).optional()
|
|
619
|
+
}).passthrough();
|
|
620
|
+
z.object({
|
|
621
|
+
name: z.string().min(1).max(200),
|
|
622
|
+
description: z.string().max(2e3).optional().default(""),
|
|
623
|
+
entries: z.array(PolicyEntryRequest).min(1).max(500)
|
|
624
|
+
}).passthrough();
|
|
625
|
+
var PolicyEntryResponse = z.object({
|
|
626
|
+
id: z.string().uuid(),
|
|
627
|
+
tag_slug: z.string(),
|
|
628
|
+
country_codes: z.array(z.string())
|
|
629
|
+
}).passthrough();
|
|
630
|
+
var PolicySetResponse = z.object({
|
|
631
|
+
id: z.string().uuid(),
|
|
632
|
+
name: z.string(),
|
|
633
|
+
description: z.string(),
|
|
634
|
+
organization_id: z.string().uuid(),
|
|
635
|
+
visibility: z.string(),
|
|
636
|
+
is_approved: z.boolean(),
|
|
637
|
+
entries: z.array(PolicyEntryResponse),
|
|
638
|
+
created_at: z.string().datetime({ offset: true })
|
|
639
|
+
}).passthrough();
|
|
640
|
+
var VisibilityType = z.enum(["private", "public"]);
|
|
641
|
+
z.union([VisibilityType, z.null()]).optional();
|
|
642
|
+
var PolicySetListItem = z.object({
|
|
643
|
+
id: z.string().uuid(),
|
|
644
|
+
name: z.string(),
|
|
645
|
+
description: z.string(),
|
|
646
|
+
organization_id: z.string().uuid(),
|
|
647
|
+
visibility: z.string(),
|
|
648
|
+
is_approved: z.boolean(),
|
|
649
|
+
created_at: z.string().datetime({ offset: true })
|
|
650
|
+
}).passthrough();
|
|
651
|
+
z.object({
|
|
652
|
+
items: z.array(PolicySetListItem),
|
|
653
|
+
total: z.number().int(),
|
|
654
|
+
page: z.number().int(),
|
|
655
|
+
limit: z.number().int(),
|
|
656
|
+
pages: z.number().int()
|
|
657
|
+
}).passthrough();
|
|
658
|
+
z.object({
|
|
659
|
+
name: z.string().min(1).max(200),
|
|
660
|
+
description: z.string().max(2e3).optional().default(""),
|
|
661
|
+
entries: z.array(PolicyEntryRequest).min(1).max(500)
|
|
662
|
+
}).passthrough();
|
|
663
|
+
var AlertStatus = z.enum(["open", "acknowledged", "resolved", "dismissed"]);
|
|
664
|
+
z.union([AlertStatus, z.null()]).optional();
|
|
665
|
+
var AlertResponse = z.object({
|
|
666
|
+
id: z.string().uuid(),
|
|
667
|
+
scan_id: z.string().uuid(),
|
|
668
|
+
campaign_id: z.string().uuid(),
|
|
669
|
+
policy_set_id: z.union([z.string(), z.null()]),
|
|
670
|
+
violation_rule_id: z.union([z.string(), z.null()]),
|
|
671
|
+
organization_id: z.string().uuid(),
|
|
672
|
+
tag_slug: z.string(),
|
|
673
|
+
country_code: z.string(),
|
|
674
|
+
status: z.string(),
|
|
675
|
+
closed_by: z.union([z.string(), z.null()]),
|
|
676
|
+
created_at: z.string().datetime({ offset: true }),
|
|
677
|
+
updated_at: z.union([z.string(), z.null()]),
|
|
678
|
+
scan_url: z.string(),
|
|
679
|
+
offer_url: z.string(),
|
|
680
|
+
tag_display_name: z.string()
|
|
681
|
+
}).passthrough();
|
|
682
|
+
z.object({
|
|
683
|
+
items: z.array(AlertResponse),
|
|
684
|
+
total: z.number().int(),
|
|
685
|
+
page: z.number().int(),
|
|
686
|
+
limit: z.number().int(),
|
|
687
|
+
pages: z.number().int()
|
|
688
|
+
}).passthrough();
|
|
689
|
+
z.object({ status: AlertStatus }).passthrough();
|
|
690
|
+
var AlertStatsResponse = z.object({
|
|
691
|
+
open: z.number().int(),
|
|
692
|
+
acknowledged: z.number().int(),
|
|
693
|
+
resolved: z.number().int(),
|
|
694
|
+
dismissed: z.number().int()
|
|
695
|
+
}).passthrough();
|
|
696
|
+
var BlockReason = z.enum(["no_subscription", "suspended", "insufficient_funds"]);
|
|
697
|
+
var BillingSummaryResponse = z.object({
|
|
698
|
+
balance_micros: z.number().int(),
|
|
699
|
+
plan_name: z.union([z.string(), z.null()]),
|
|
700
|
+
plan_id: z.union([z.string(), z.null()]),
|
|
701
|
+
checks_per_period: z.union([z.number(), z.null()]),
|
|
702
|
+
checks_used: z.union([z.number(), z.null()]),
|
|
703
|
+
period_start: z.union([z.string(), z.null()]),
|
|
704
|
+
period_end: z.union([z.string(), z.null()]),
|
|
705
|
+
price_per_extra_check_micros: z.union([z.number(), z.null()]),
|
|
706
|
+
current_plan_is_custom: z.boolean().optional().default(false),
|
|
707
|
+
is_suspended: z.boolean().optional().default(false),
|
|
708
|
+
scheduled_next_plan_id: z.union([z.string(), z.null()]).optional(),
|
|
709
|
+
scheduled_next_plan_name: z.union([z.string(), z.null()]).optional(),
|
|
710
|
+
scheduled_effective_at: z.union([z.string(), z.null()]).optional(),
|
|
711
|
+
can_create_scan: z.boolean().optional().default(true),
|
|
712
|
+
block_reason: z.union([BlockReason, z.null()]).optional(),
|
|
713
|
+
billing_mode: z.string().optional().default("prepaid"),
|
|
714
|
+
credit_limit_micros: z.number().int().optional().default(0),
|
|
715
|
+
effective_minimum_balance_micros: z.number().int().optional().default(0)
|
|
716
|
+
}).passthrough();
|
|
717
|
+
var UsageResponse = z.object({
|
|
718
|
+
id: z.string().uuid(),
|
|
719
|
+
scan_id: z.string().uuid(),
|
|
720
|
+
charged_micros: z.number().int(),
|
|
721
|
+
balance_after_micros: z.number().int(),
|
|
722
|
+
within_plan: z.boolean(),
|
|
723
|
+
event_type: z.string(),
|
|
724
|
+
created_at: z.string().datetime({ offset: true })
|
|
725
|
+
}).passthrough();
|
|
726
|
+
z.object({
|
|
727
|
+
items: z.array(UsageResponse),
|
|
728
|
+
total: z.number().int(),
|
|
729
|
+
page: z.number().int(),
|
|
730
|
+
limit: z.number().int(),
|
|
731
|
+
pages: z.number().int()
|
|
732
|
+
}).passthrough();
|
|
733
|
+
var UsagePeriodSummaryResponse = z.object({
|
|
734
|
+
period_start: z.string().datetime({ offset: true }),
|
|
735
|
+
period_end: z.string().datetime({ offset: true }),
|
|
736
|
+
checks: z.number().int(),
|
|
737
|
+
rechecks: z.number().int(),
|
|
738
|
+
within_plan: z.number().int(),
|
|
739
|
+
overage: z.number().int(),
|
|
740
|
+
charged_micros: z.number().int()
|
|
741
|
+
}).passthrough();
|
|
742
|
+
var BalanceTransactionType = z.enum([
|
|
743
|
+
"initial_balance",
|
|
744
|
+
"top_up_manual",
|
|
745
|
+
"usage_charge",
|
|
746
|
+
"subscription_renewal",
|
|
747
|
+
"subscription_upgrade",
|
|
748
|
+
"admin_adjustment",
|
|
749
|
+
"refund",
|
|
750
|
+
"invoice_settlement",
|
|
751
|
+
"crypto_top_up"
|
|
752
|
+
]);
|
|
753
|
+
z.union([z.array(BalanceTransactionType), z.null()]).optional();
|
|
754
|
+
var BalanceTransactionResponse = z.object({
|
|
755
|
+
id: z.string().uuid(),
|
|
756
|
+
type: z.string(),
|
|
757
|
+
amount_micros: z.number().int(),
|
|
758
|
+
balance_after_micros: z.number().int(),
|
|
759
|
+
description: z.string(),
|
|
760
|
+
reference_kind: z.union([z.string(), z.null()]),
|
|
761
|
+
reference_id: z.union([z.string(), z.null()]),
|
|
762
|
+
actor_user_id: z.union([z.string(), z.null()]),
|
|
763
|
+
created_at: z.string().datetime({ offset: true })
|
|
764
|
+
}).passthrough();
|
|
765
|
+
z.object({
|
|
766
|
+
items: z.array(BalanceTransactionResponse),
|
|
767
|
+
total: z.number().int(),
|
|
768
|
+
page: z.number().int(),
|
|
769
|
+
limit: z.number().int(),
|
|
770
|
+
pages: z.number().int()
|
|
771
|
+
}).passthrough();
|
|
772
|
+
var EndpointHealthResponse = z.object({
|
|
773
|
+
consecutive_failures: z.number().int(),
|
|
774
|
+
last_delivery_at: z.union([z.string(), z.null()]),
|
|
775
|
+
last_delivery_status: z.union([z.number(), z.null()]),
|
|
776
|
+
success_rate_7d: z.number()
|
|
777
|
+
}).passthrough();
|
|
778
|
+
var WebhookResponse = z.object({
|
|
779
|
+
id: z.string().uuid(),
|
|
780
|
+
url: z.string(),
|
|
781
|
+
description: z.string(),
|
|
782
|
+
event_types: z.array(z.string()),
|
|
783
|
+
campaign_ids: z.union([z.array(z.string().uuid()), z.null()]),
|
|
784
|
+
is_active: z.boolean(),
|
|
785
|
+
disabled_reason: z.union([z.string(), z.null()]),
|
|
786
|
+
disabled_reason_detail: z.union([z.string(), z.null()]),
|
|
787
|
+
disabled_at: z.union([z.string(), z.null()]),
|
|
788
|
+
health: EndpointHealthResponse,
|
|
789
|
+
created_at: z.string().datetime({ offset: true }),
|
|
790
|
+
updated_at: z.string().datetime({ offset: true })
|
|
791
|
+
}).passthrough();
|
|
792
|
+
z.object({
|
|
793
|
+
url: z.string().min(1).max(2048),
|
|
794
|
+
description: z.string().max(256).optional().default(""),
|
|
795
|
+
event_types: z.array(z.string()).optional(),
|
|
796
|
+
campaign_ids: z.union([z.array(z.string().uuid()), z.null()]).optional()
|
|
797
|
+
}).passthrough();
|
|
798
|
+
z.object({ webhook: WebhookResponse, secret: z.string() }).passthrough();
|
|
799
|
+
var EventCatalogEntryResponse = z.object({
|
|
800
|
+
event_type: z.string(),
|
|
801
|
+
description: z.string(),
|
|
802
|
+
sample_payload: z.object({}).partial().passthrough()
|
|
803
|
+
}).passthrough();
|
|
804
|
+
var EventCatalogResponse = z.object({ entries: z.array(EventCatalogEntryResponse) }).passthrough();
|
|
805
|
+
z.object({
|
|
806
|
+
url: z.union([z.string(), z.null()]),
|
|
807
|
+
description: z.union([z.string(), z.null()]),
|
|
808
|
+
event_types: z.union([z.array(z.string()), z.null()]),
|
|
809
|
+
campaign_ids: z.union([z.array(z.string().uuid()), z.null()]),
|
|
810
|
+
clear_campaign_ids: z.boolean().default(false),
|
|
811
|
+
is_active: z.union([z.boolean(), z.null()])
|
|
812
|
+
}).partial().passthrough();
|
|
813
|
+
z.object({ event_type: z.string() }).passthrough();
|
|
814
|
+
var TestWebhookResponse = z.object({
|
|
815
|
+
success: z.boolean(),
|
|
816
|
+
response_status: z.union([z.number(), z.null()]),
|
|
817
|
+
elapsed_ms: z.number().int(),
|
|
818
|
+
error_code: z.union([z.string(), z.null()]),
|
|
819
|
+
response_body: z.string()
|
|
820
|
+
}).passthrough();
|
|
821
|
+
z.union([z.boolean(), z.null()]).optional();
|
|
822
|
+
var DeliveryAttemptResponse = z.object({
|
|
823
|
+
id: z.string().uuid(),
|
|
824
|
+
event_id: z.string().uuid(),
|
|
825
|
+
event_type: z.string(),
|
|
826
|
+
response_status: z.union([z.number(), z.null()]),
|
|
827
|
+
response_body: z.union([z.string(), z.null()]),
|
|
828
|
+
success: z.boolean(),
|
|
829
|
+
attempt_number: z.number().int(),
|
|
830
|
+
error_code: z.union([z.string(), z.null()]),
|
|
831
|
+
elapsed_ms: z.union([z.number(), z.null()]),
|
|
832
|
+
created_at: z.string().datetime({ offset: true })
|
|
833
|
+
}).passthrough();
|
|
834
|
+
z.object({
|
|
835
|
+
items: z.array(DeliveryAttemptResponse),
|
|
836
|
+
total: z.number().int(),
|
|
837
|
+
page: z.number().int(),
|
|
838
|
+
limit: z.number().int(),
|
|
839
|
+
pages: z.number().int()
|
|
840
|
+
}).passthrough();
|
|
841
|
+
z.object({
|
|
842
|
+
from_ts: z.string().datetime({ offset: true }),
|
|
843
|
+
to_ts: z.string().datetime({ offset: true })
|
|
844
|
+
}).passthrough();
|
|
845
|
+
var BulkReplayResponse = z.object({ replayed: z.number().int(), skipped: z.number().int() }).passthrough();
|
|
846
|
+
var AlertNotificationVersion = z.enum(["public", "internal"]);
|
|
847
|
+
var AlertNotificationDestinationResponse = z.object({
|
|
848
|
+
id: z.string().uuid(),
|
|
849
|
+
organization_id: z.string().uuid(),
|
|
850
|
+
channel: z.string(),
|
|
851
|
+
name: z.string(),
|
|
852
|
+
is_active: z.boolean(),
|
|
853
|
+
is_default_target: z.boolean(),
|
|
854
|
+
version: AlertNotificationVersion,
|
|
855
|
+
consecutive_failures: z.number().int(),
|
|
856
|
+
last_delivery_at: z.union([z.string(), z.null()]),
|
|
857
|
+
last_delivery_status: z.union([z.number(), z.null()]),
|
|
858
|
+
slack_workspace_id: z.union([z.string(), z.null()]),
|
|
859
|
+
slack_channel_id: z.union([z.string(), z.null()]),
|
|
860
|
+
slack_channel_name: z.union([z.string(), z.null()]),
|
|
861
|
+
telegram_chat_id: z.union([z.number(), z.null()]),
|
|
862
|
+
telegram_chat_title: z.union([z.string(), z.null()]),
|
|
863
|
+
telegram_chat_type: z.union([z.string(), z.null()]),
|
|
864
|
+
email_address: z.union([z.string(), z.null()]),
|
|
865
|
+
included_label_keys: z.array(z.string()),
|
|
866
|
+
created_at: z.string().datetime({ offset: true }),
|
|
867
|
+
updated_at: z.string().datetime({ offset: true })
|
|
868
|
+
}).passthrough();
|
|
869
|
+
z.object({ version: AlertNotificationVersion }).passthrough();
|
|
870
|
+
var CampaignOverridesResponse = z.object({
|
|
871
|
+
campaign_id: z.string().uuid(),
|
|
872
|
+
mode: z.string(),
|
|
873
|
+
destination_ids: z.array(z.string().uuid())
|
|
874
|
+
}).passthrough();
|
|
875
|
+
z.object({
|
|
876
|
+
mode: z.string().min(1).max(16),
|
|
877
|
+
destination_ids: z.array(z.string().uuid()).optional().default([])
|
|
878
|
+
}).passthrough();
|
|
879
|
+
var InvoiceType = z.enum(["proforma", "final"]);
|
|
880
|
+
z.union([InvoiceType, z.null()]).optional();
|
|
881
|
+
var InvoiceStatus = z.enum(["draft", "issued", "paid", "voided", "overdue"]);
|
|
882
|
+
z.union([InvoiceStatus, z.null()]).optional();
|
|
883
|
+
var InvoiceResponse = z.object({
|
|
884
|
+
id: z.string().uuid(),
|
|
885
|
+
number: z.string(),
|
|
886
|
+
organization_id: z.string().uuid(),
|
|
887
|
+
type: z.string(),
|
|
888
|
+
status: z.string(),
|
|
889
|
+
total_micros: z.number().int(),
|
|
890
|
+
currency: z.string(),
|
|
891
|
+
period_start: z.union([z.string(), z.null()]),
|
|
892
|
+
period_end: z.union([z.string(), z.null()]),
|
|
893
|
+
issued_at: z.union([z.string(), z.null()]),
|
|
894
|
+
paid_at: z.union([z.string(), z.null()]),
|
|
895
|
+
voided_at: z.union([z.string(), z.null()]),
|
|
896
|
+
has_pdf: z.boolean(),
|
|
897
|
+
description: z.string(),
|
|
898
|
+
payment_method: z.string(),
|
|
899
|
+
created_at: z.string().datetime({ offset: true })
|
|
900
|
+
}).passthrough();
|
|
901
|
+
z.object({
|
|
902
|
+
items: z.array(InvoiceResponse),
|
|
903
|
+
total: z.number().int(),
|
|
904
|
+
page: z.number().int(),
|
|
905
|
+
limit: z.number().int(),
|
|
906
|
+
pages: z.number().int()
|
|
907
|
+
}).passthrough();
|
|
908
|
+
z.object({ url: z.union([z.string(), z.null()]), ready: z.boolean() }).passthrough();
|
|
909
|
+
z.object({
|
|
910
|
+
name: z.string().min(2).max(100),
|
|
911
|
+
email: z.string().email(),
|
|
912
|
+
message: z.string().min(10).max(2e3),
|
|
913
|
+
source: z.string().max(512).optional().default("")
|
|
914
|
+
}).passthrough();
|
|
915
|
+
z.object({
|
|
916
|
+
id: z.string().uuid(),
|
|
917
|
+
received_at: z.string().datetime({ offset: true })
|
|
918
|
+
}).passthrough();
|
|
919
|
+
var PreferredContactChannel = z.enum(["telegram", "whatsapp", "email"]);
|
|
920
|
+
z.object({
|
|
921
|
+
first_name: z.string().min(2).max(60),
|
|
922
|
+
last_name: z.string().min(2).max(60),
|
|
923
|
+
company_email: z.string().email(),
|
|
924
|
+
company_name: z.string().min(2).max(120),
|
|
925
|
+
preferred_channel: PreferredContactChannel,
|
|
926
|
+
contact_handle: z.string().max(120).optional().default(""),
|
|
927
|
+
comment: z.string().max(2e3).optional().default(""),
|
|
928
|
+
privacy_accepted: z.boolean(),
|
|
929
|
+
source: z.string().max(512).optional().default("")
|
|
930
|
+
}).passthrough();
|
|
931
|
+
z.object({
|
|
932
|
+
id: z.string().uuid(),
|
|
933
|
+
received_at: z.string().datetime({ offset: true })
|
|
934
|
+
}).passthrough();
|
|
935
|
+
var schemas = {
|
|
936
|
+
OrgResponse,
|
|
937
|
+
UserResponse,
|
|
938
|
+
ApiKeyResponse,
|
|
939
|
+
ApiKeyCreatedResponse,
|
|
940
|
+
RoleResponse,
|
|
941
|
+
ScanResponse,
|
|
942
|
+
ScanBriefResponse,
|
|
943
|
+
GeoResponse,
|
|
944
|
+
EmulatorResponse,
|
|
945
|
+
CampaignGroupResponse,
|
|
946
|
+
GroupActionResponse,
|
|
947
|
+
CampaignResponse,
|
|
948
|
+
CampaignPickerItem,
|
|
949
|
+
RunResponse,
|
|
950
|
+
ScanTileResponse,
|
|
951
|
+
ScanTagResponse,
|
|
952
|
+
TagDefinitionWithStatsResponse,
|
|
953
|
+
LinkedRuleResponse,
|
|
954
|
+
TagDefinitionDetailResponse,
|
|
955
|
+
CustomRuleResponse,
|
|
956
|
+
RuleTestResponse,
|
|
957
|
+
PolicyEntryResponse,
|
|
958
|
+
PolicySetResponse,
|
|
959
|
+
PolicySetListItem,
|
|
960
|
+
AlertResponse,
|
|
961
|
+
AlertStatsResponse,
|
|
962
|
+
BillingSummaryResponse,
|
|
963
|
+
UsageResponse,
|
|
964
|
+
UsagePeriodSummaryResponse,
|
|
965
|
+
BalanceTransactionResponse,
|
|
966
|
+
WebhookResponse,
|
|
967
|
+
EventCatalogResponse,
|
|
968
|
+
TestWebhookResponse,
|
|
969
|
+
DeliveryAttemptResponse,
|
|
970
|
+
BulkReplayResponse,
|
|
971
|
+
AlertNotificationDestinationResponse,
|
|
972
|
+
CampaignOverridesResponse,
|
|
973
|
+
InvoiceResponse};
|
|
974
|
+
function parseWithSchema(schema, raw, label) {
|
|
975
|
+
const parsed = schema.safeParse(raw);
|
|
976
|
+
if (parsed.success) return ok(stripUndefinedKeys(parsed.data));
|
|
977
|
+
const first = parsed.error.issues[0];
|
|
978
|
+
const path = first?.path.join(".") ?? "";
|
|
979
|
+
const message = first?.message ?? "validation failed";
|
|
980
|
+
const detail2 = path === "" ? `malformed ${label}: ${message}` : `malformed ${label}: ${path}: ${message}`;
|
|
981
|
+
return err({ kind: "upstream", detail: detail2 });
|
|
982
|
+
}
|
|
983
|
+
function stripUndefinedKeys(value) {
|
|
984
|
+
if (Array.isArray(value)) return value.map(stripUndefinedKeys);
|
|
985
|
+
if (value === null || typeof value !== "object") return value;
|
|
986
|
+
const out = {};
|
|
987
|
+
for (const [k, v] of Object.entries(value)) {
|
|
988
|
+
if (v === void 0) continue;
|
|
989
|
+
out[k] = stripUndefinedKeys(v);
|
|
990
|
+
}
|
|
991
|
+
return out;
|
|
992
|
+
}
|
|
993
|
+
function parsePagedWithItemSchema(itemSchema, raw, label) {
|
|
994
|
+
const envelope = z.object({
|
|
995
|
+
items: z.array(itemSchema),
|
|
996
|
+
total: z.number(),
|
|
997
|
+
page: z.number(),
|
|
998
|
+
limit: z.number()
|
|
999
|
+
}).passthrough();
|
|
1000
|
+
return parseWithSchema(envelope, raw, `${label} page`);
|
|
1001
|
+
}
|
|
1002
|
+
function parseArrayOrItemsWithSchema(itemSchema, raw, label) {
|
|
1003
|
+
if (Array.isArray(raw)) {
|
|
1004
|
+
return parseWithSchema(z.array(itemSchema), raw, label);
|
|
1005
|
+
}
|
|
1006
|
+
if (raw !== null && typeof raw === "object" && "items" in raw) {
|
|
1007
|
+
const items = raw.items;
|
|
1008
|
+
return parseWithSchema(z.array(itemSchema), items, label);
|
|
1009
|
+
}
|
|
1010
|
+
return err({
|
|
1011
|
+
kind: "upstream",
|
|
1012
|
+
detail: `expected array (or {items:[]}) of ${label}`
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/infrastructure/api/parsers/parse-alert.ts
|
|
1017
|
+
var AlertSchema = schemas.AlertResponse.pick({
|
|
1018
|
+
id: true,
|
|
1019
|
+
scan_id: true,
|
|
1020
|
+
campaign_id: true,
|
|
1021
|
+
policy_set_id: true,
|
|
1022
|
+
violation_rule_id: true,
|
|
1023
|
+
tag_slug: true,
|
|
1024
|
+
tag_display_name: true,
|
|
1025
|
+
country_code: true,
|
|
1026
|
+
status: true,
|
|
1027
|
+
closed_by: true,
|
|
1028
|
+
scan_url: true,
|
|
1029
|
+
offer_url: true,
|
|
1030
|
+
created_at: true,
|
|
1031
|
+
updated_at: true
|
|
1032
|
+
}).strip();
|
|
1033
|
+
var parseAlertPage = (raw) => parsePagedWithItemSchema(AlertSchema, raw, "alerts");
|
|
1034
|
+
var ApiKeySchema = schemas.ApiKeyResponse.pick({
|
|
1035
|
+
id: true,
|
|
1036
|
+
key_prefix: true,
|
|
1037
|
+
name: true,
|
|
1038
|
+
expires_at: true,
|
|
1039
|
+
created_at: true
|
|
1040
|
+
}).strip();
|
|
1041
|
+
var ApiKeyListSchema = z.array(ApiKeySchema);
|
|
1042
|
+
var parseApiKeyList = (raw) => parseWithSchema(ApiKeyListSchema, raw, "api-keys");
|
|
1043
|
+
|
|
1044
|
+
// src/infrastructure/api/parsers/parse-billing-summary.ts
|
|
1045
|
+
var BillingSummarySchema = schemas.BillingSummaryResponse.pick({
|
|
1046
|
+
balance_micros: true,
|
|
1047
|
+
plan_id: true,
|
|
1048
|
+
plan_name: true,
|
|
1049
|
+
checks_per_period: true,
|
|
1050
|
+
checks_used: true,
|
|
1051
|
+
period_start: true,
|
|
1052
|
+
period_end: true,
|
|
1053
|
+
price_per_extra_check_micros: true,
|
|
1054
|
+
is_suspended: true,
|
|
1055
|
+
can_create_scan: true,
|
|
1056
|
+
billing_mode: true,
|
|
1057
|
+
block_reason: true
|
|
1058
|
+
}).strip();
|
|
1059
|
+
var parseBillingSummary = (raw) => parseWithSchema(BillingSummarySchema, raw, "billing-summary");
|
|
1060
|
+
|
|
1061
|
+
// src/infrastructure/api/parsers/parse-campaign.ts
|
|
1062
|
+
var CampaignSchema = schemas.CampaignResponse.pick({
|
|
1063
|
+
id: true,
|
|
1064
|
+
name: true,
|
|
1065
|
+
campaign_type: true,
|
|
1066
|
+
url: true,
|
|
1067
|
+
ad_tag: true,
|
|
1068
|
+
country_codes: true,
|
|
1069
|
+
group_id: true,
|
|
1070
|
+
labels: true,
|
|
1071
|
+
policy_set_id: true,
|
|
1072
|
+
schedule_enabled: true,
|
|
1073
|
+
schedule_type: true,
|
|
1074
|
+
is_archived: true,
|
|
1075
|
+
created_at: true,
|
|
1076
|
+
last_run_at: true
|
|
1077
|
+
}).strip();
|
|
1078
|
+
var parseCampaign = (raw) => parseWithSchema(CampaignSchema, raw, "campaign");
|
|
1079
|
+
var parseCampaignPage = (raw) => parsePagedWithItemSchema(CampaignSchema, raw, "campaigns");
|
|
1080
|
+
|
|
1081
|
+
// src/infrastructure/api/parsers/parse-campaign-group.ts
|
|
1082
|
+
var CampaignGroupSchema = schemas.CampaignGroupResponse.pick({
|
|
1083
|
+
id: true,
|
|
1084
|
+
name: true,
|
|
1085
|
+
is_default: true,
|
|
1086
|
+
is_archived: true,
|
|
1087
|
+
schedule_paused: true,
|
|
1088
|
+
campaign_count: true,
|
|
1089
|
+
created_at: true
|
|
1090
|
+
}).strip();
|
|
1091
|
+
var parseCampaignGroup = (raw) => parseWithSchema(CampaignGroupSchema, raw, "campaign-group");
|
|
1092
|
+
var parseCampaignGroupArray = (raw) => parseArrayOrItemsWithSchema(CampaignGroupSchema, raw, "campaign-groups");
|
|
1093
|
+
var CampaignPickerSchema = schemas.CampaignPickerItem.pick({
|
|
1094
|
+
id: true,
|
|
1095
|
+
name: true,
|
|
1096
|
+
group_id: true,
|
|
1097
|
+
is_archived: true
|
|
1098
|
+
}).strip();
|
|
1099
|
+
var CampaignPickerArraySchema = z.array(CampaignPickerSchema);
|
|
1100
|
+
var parseCampaignPickerArray = (raw) => parseWithSchema(CampaignPickerArraySchema, raw, "campaigns-picker");
|
|
1101
|
+
|
|
1102
|
+
// src/infrastructure/api/parsers/shared.ts
|
|
1103
|
+
function isStringRecord(v) {
|
|
1104
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/infrastructure/api/parsers/parse-count-envelope.ts
|
|
1108
|
+
function parseIntField(raw, fieldName) {
|
|
1109
|
+
if (!isStringRecord(raw)) {
|
|
1110
|
+
return err({ kind: "upstream", detail: `expected object with ${fieldName}` });
|
|
1111
|
+
}
|
|
1112
|
+
const value = raw[fieldName];
|
|
1113
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
1114
|
+
return err({ kind: "upstream", detail: `${fieldName} must be an integer` });
|
|
1115
|
+
}
|
|
1116
|
+
return ok({ [fieldName]: value });
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// src/infrastructure/api/parsers/parse-custom-rule.ts
|
|
1120
|
+
var CustomRuleSchema = schemas.CustomRuleResponse.pick({
|
|
1121
|
+
id: true,
|
|
1122
|
+
organization_id: true,
|
|
1123
|
+
name: true,
|
|
1124
|
+
tag_slug: true,
|
|
1125
|
+
rule_type: true,
|
|
1126
|
+
config: true,
|
|
1127
|
+
target: true,
|
|
1128
|
+
is_active: true,
|
|
1129
|
+
created_at: true
|
|
1130
|
+
}).strip();
|
|
1131
|
+
var parseCustomRule = (raw) => parseWithSchema(CustomRuleSchema, raw, "custom-rule");
|
|
1132
|
+
|
|
1133
|
+
// src/infrastructure/api/parsers/parse-custom-rule-page.ts
|
|
1134
|
+
var CustomRuleSchema2 = schemas.CustomRuleResponse.pick({
|
|
1135
|
+
id: true,
|
|
1136
|
+
organization_id: true,
|
|
1137
|
+
name: true,
|
|
1138
|
+
tag_slug: true,
|
|
1139
|
+
rule_type: true,
|
|
1140
|
+
config: true,
|
|
1141
|
+
target: true,
|
|
1142
|
+
is_active: true,
|
|
1143
|
+
created_at: true
|
|
1144
|
+
}).strip();
|
|
1145
|
+
var parseCustomRulePage = (raw) => parsePagedWithItemSchema(CustomRuleSchema2, raw, "custom-rules");
|
|
1146
|
+
|
|
1147
|
+
// src/infrastructure/api/parsers/parse-empty.ts
|
|
1148
|
+
function parseEmpty(_raw) {
|
|
1149
|
+
return ok(null);
|
|
1150
|
+
}
|
|
1151
|
+
var EmulatorSchema = schemas.EmulatorResponse.pick({
|
|
1152
|
+
id: true,
|
|
1153
|
+
display_name: true,
|
|
1154
|
+
category: true,
|
|
1155
|
+
browser: true
|
|
1156
|
+
}).strip();
|
|
1157
|
+
var EmulatorListSchema = z.array(EmulatorSchema);
|
|
1158
|
+
var parseEmulatorList = (raw) => parseWithSchema(EmulatorListSchema, raw, "emulators");
|
|
1159
|
+
|
|
1160
|
+
// src/infrastructure/api/parsers/parse-generic.ts
|
|
1161
|
+
function parsePageOf(parseItem) {
|
|
1162
|
+
return (raw) => {
|
|
1163
|
+
if (raw === null || typeof raw !== "object") {
|
|
1164
|
+
return err({ kind: "upstream", detail: "malformed page envelope" });
|
|
1165
|
+
}
|
|
1166
|
+
const r = raw;
|
|
1167
|
+
if (!Array.isArray(r.items) || typeof r.total !== "number" || typeof r.page !== "number" || typeof r.limit !== "number") {
|
|
1168
|
+
return err({ kind: "upstream", detail: "page envelope: wrong field types" });
|
|
1169
|
+
}
|
|
1170
|
+
const out = [];
|
|
1171
|
+
for (const item of r.items) {
|
|
1172
|
+
const parsed = parseItem(item);
|
|
1173
|
+
if (parsed.isErr()) return err(parsed.error);
|
|
1174
|
+
out.push(parsed.value);
|
|
1175
|
+
}
|
|
1176
|
+
return ok({ items: out, total: r.total, page: r.page, limit: r.limit });
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
function parseArrayOf(parseItem) {
|
|
1180
|
+
return (raw) => {
|
|
1181
|
+
if (!Array.isArray(raw)) return err({ kind: "upstream", detail: "expected array" });
|
|
1182
|
+
const out = [];
|
|
1183
|
+
for (const item of raw) {
|
|
1184
|
+
const r = parseItem(item);
|
|
1185
|
+
if (r.isErr()) return err(r.error);
|
|
1186
|
+
out.push(r.value);
|
|
1187
|
+
}
|
|
1188
|
+
return ok(out);
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
var OrgSchema = schemas.OrgResponse.pick({
|
|
1192
|
+
id: true,
|
|
1193
|
+
name: true,
|
|
1194
|
+
owner_id: true,
|
|
1195
|
+
is_active: true,
|
|
1196
|
+
created_at: true
|
|
1197
|
+
}).strip();
|
|
1198
|
+
var parseOrg = (raw) => parseWithSchema(OrgSchema, raw, "org");
|
|
1199
|
+
var UserSchema = schemas.UserResponse.pick({
|
|
1200
|
+
id: true,
|
|
1201
|
+
email: true,
|
|
1202
|
+
name: true,
|
|
1203
|
+
role_name: true,
|
|
1204
|
+
is_active: true,
|
|
1205
|
+
created_at: true
|
|
1206
|
+
}).strip();
|
|
1207
|
+
var parseUser = (raw) => parseWithSchema(UserSchema, raw, "user");
|
|
1208
|
+
var RoleSchema = schemas.RoleResponse.pick({
|
|
1209
|
+
id: true,
|
|
1210
|
+
name: true,
|
|
1211
|
+
scope: true,
|
|
1212
|
+
is_system: true,
|
|
1213
|
+
permissions: true
|
|
1214
|
+
}).strip();
|
|
1215
|
+
var parseRole = (raw) => parseWithSchema(RoleSchema, raw, "role");
|
|
1216
|
+
var ApiKeyCreatedSchema = schemas.ApiKeyCreatedResponse.pick({
|
|
1217
|
+
id: true,
|
|
1218
|
+
key_prefix: true,
|
|
1219
|
+
full_key: true,
|
|
1220
|
+
name: true,
|
|
1221
|
+
expires_at: true,
|
|
1222
|
+
created_at: true
|
|
1223
|
+
}).strip();
|
|
1224
|
+
var parseApiKeyCreated = (raw) => parseWithSchema(ApiKeyCreatedSchema, raw, "api-key-created");
|
|
1225
|
+
var ScanTagSchema = schemas.ScanTagResponse.pick({
|
|
1226
|
+
id: true,
|
|
1227
|
+
scan_id: true,
|
|
1228
|
+
tag_slug: true,
|
|
1229
|
+
detail: true,
|
|
1230
|
+
url: true,
|
|
1231
|
+
display_name: true,
|
|
1232
|
+
category: true,
|
|
1233
|
+
severity: true,
|
|
1234
|
+
created_at: true
|
|
1235
|
+
}).strip();
|
|
1236
|
+
var parseScanTag = (raw) => parseWithSchema(ScanTagSchema, raw, "scan-tag");
|
|
1237
|
+
var RuleTestSchema = schemas.RuleTestResponse.pick({
|
|
1238
|
+
matched: true,
|
|
1239
|
+
elapsed_ms: true,
|
|
1240
|
+
tags: true
|
|
1241
|
+
}).strip();
|
|
1242
|
+
var parseRuleTest = (raw) => parseWithSchema(RuleTestSchema, raw, "rule-test");
|
|
1243
|
+
var AlertStatsSchema = schemas.AlertStatsResponse.pick({
|
|
1244
|
+
open: true,
|
|
1245
|
+
acknowledged: true,
|
|
1246
|
+
resolved: true,
|
|
1247
|
+
dismissed: true
|
|
1248
|
+
}).strip();
|
|
1249
|
+
var parseAlertStats = (raw) => parseWithSchema(AlertStatsSchema, raw, "alert-stats");
|
|
1250
|
+
var UsageSchema = schemas.UsageResponse.pick({
|
|
1251
|
+
id: true,
|
|
1252
|
+
scan_id: true,
|
|
1253
|
+
charged_micros: true,
|
|
1254
|
+
balance_after_micros: true,
|
|
1255
|
+
within_plan: true,
|
|
1256
|
+
event_type: true,
|
|
1257
|
+
created_at: true
|
|
1258
|
+
}).strip();
|
|
1259
|
+
var parseUsage = (raw) => parseWithSchema(UsageSchema, raw, "usage");
|
|
1260
|
+
var UsageSummarySchema = schemas.UsagePeriodSummaryResponse.pick({
|
|
1261
|
+
period_start: true,
|
|
1262
|
+
period_end: true,
|
|
1263
|
+
checks: true,
|
|
1264
|
+
rechecks: true,
|
|
1265
|
+
within_plan: true,
|
|
1266
|
+
overage: true,
|
|
1267
|
+
charged_micros: true
|
|
1268
|
+
}).strip();
|
|
1269
|
+
var parseUsageSummary = (raw) => parseWithSchema(UsageSummarySchema, raw, "usage-summary");
|
|
1270
|
+
var BalanceTxSchema = schemas.BalanceTransactionResponse.pick({
|
|
1271
|
+
id: true,
|
|
1272
|
+
type: true,
|
|
1273
|
+
amount_micros: true,
|
|
1274
|
+
balance_after_micros: true,
|
|
1275
|
+
description: true,
|
|
1276
|
+
reference_kind: true,
|
|
1277
|
+
reference_id: true,
|
|
1278
|
+
actor_user_id: true,
|
|
1279
|
+
created_at: true
|
|
1280
|
+
}).strip();
|
|
1281
|
+
var parseBalanceTx = (raw) => parseWithSchema(BalanceTxSchema, raw, "balance-tx");
|
|
1282
|
+
var InvoiceSchema = schemas.InvoiceResponse.pick({
|
|
1283
|
+
id: true,
|
|
1284
|
+
number: true,
|
|
1285
|
+
type: true,
|
|
1286
|
+
status: true,
|
|
1287
|
+
total_micros: true,
|
|
1288
|
+
currency: true,
|
|
1289
|
+
period_start: true,
|
|
1290
|
+
period_end: true,
|
|
1291
|
+
issued_at: true,
|
|
1292
|
+
paid_at: true,
|
|
1293
|
+
voided_at: true,
|
|
1294
|
+
has_pdf: true,
|
|
1295
|
+
description: true,
|
|
1296
|
+
payment_method: true,
|
|
1297
|
+
created_at: true
|
|
1298
|
+
}).strip();
|
|
1299
|
+
var parseInvoice = (raw) => parseWithSchema(InvoiceSchema, raw, "invoice");
|
|
1300
|
+
var WebhookDeliverySchema = schemas.DeliveryAttemptResponse.pick({
|
|
1301
|
+
id: true,
|
|
1302
|
+
event_id: true,
|
|
1303
|
+
event_type: true,
|
|
1304
|
+
response_status: true,
|
|
1305
|
+
success: true,
|
|
1306
|
+
attempt_number: true,
|
|
1307
|
+
error_code: true,
|
|
1308
|
+
elapsed_ms: true,
|
|
1309
|
+
created_at: true
|
|
1310
|
+
}).strip();
|
|
1311
|
+
var parseWebhookDelivery = (raw) => parseWithSchema(WebhookDeliverySchema, raw, "webhook-delivery");
|
|
1312
|
+
var EventCatalogSchema = schemas.EventCatalogResponse.pick({
|
|
1313
|
+
entries: true
|
|
1314
|
+
}).strip();
|
|
1315
|
+
var parseEventCatalog = (raw) => parseWithSchema(EventCatalogSchema, raw, "event-catalog");
|
|
1316
|
+
var AlertDestinationSchema = schemas.AlertNotificationDestinationResponse.pick({
|
|
1317
|
+
id: true,
|
|
1318
|
+
channel: true,
|
|
1319
|
+
name: true,
|
|
1320
|
+
is_active: true,
|
|
1321
|
+
is_default_target: true,
|
|
1322
|
+
version: true,
|
|
1323
|
+
consecutive_failures: true,
|
|
1324
|
+
last_delivery_at: true,
|
|
1325
|
+
last_delivery_status: true,
|
|
1326
|
+
slack_workspace_id: true,
|
|
1327
|
+
slack_channel_name: true,
|
|
1328
|
+
telegram_chat_title: true,
|
|
1329
|
+
telegram_chat_type: true,
|
|
1330
|
+
email_address: true,
|
|
1331
|
+
included_label_keys: true,
|
|
1332
|
+
created_at: true,
|
|
1333
|
+
updated_at: true
|
|
1334
|
+
}).strip();
|
|
1335
|
+
var parseAlertDestination = (raw) => parseWithSchema(AlertDestinationSchema, raw, "alert-destination");
|
|
1336
|
+
var CampaignOverridesSchema = schemas.CampaignOverridesResponse.pick({
|
|
1337
|
+
campaign_id: true,
|
|
1338
|
+
mode: true,
|
|
1339
|
+
destination_ids: true
|
|
1340
|
+
}).strip();
|
|
1341
|
+
var parseCampaignAlertOverrides = (raw) => parseWithSchema(CampaignOverridesSchema, raw, "campaign-overrides");
|
|
1342
|
+
var BulkReplaySchema = schemas.BulkReplayResponse.pick({
|
|
1343
|
+
replayed: true,
|
|
1344
|
+
skipped: true
|
|
1345
|
+
}).strip();
|
|
1346
|
+
var parseBulkReplay = (raw) => parseWithSchema(BulkReplaySchema, raw, "bulk-replay");
|
|
1347
|
+
var GroupActionSchema = schemas.GroupActionResponse.pick({
|
|
1348
|
+
group_id: true,
|
|
1349
|
+
affected_campaigns: true,
|
|
1350
|
+
cancelled_count: true,
|
|
1351
|
+
run_ids: true,
|
|
1352
|
+
failures: true
|
|
1353
|
+
}).strip();
|
|
1354
|
+
var parseGroupAction = (raw) => parseWithSchema(GroupActionSchema, raw, "group-action");
|
|
1355
|
+
schemas.PolicyEntryResponse.pick({
|
|
1356
|
+
id: true,
|
|
1357
|
+
tag_slug: true,
|
|
1358
|
+
country_codes: true
|
|
1359
|
+
}).strip();
|
|
1360
|
+
var GeoSchema = schemas.GeoResponse.pick({
|
|
1361
|
+
country_code: true,
|
|
1362
|
+
name: true,
|
|
1363
|
+
region: true,
|
|
1364
|
+
tier: true
|
|
1365
|
+
}).strip();
|
|
1366
|
+
var GeoListSchema = z.array(GeoSchema);
|
|
1367
|
+
var parseGeoList = (raw) => parseWithSchema(GeoListSchema, raw, "geos");
|
|
1368
|
+
|
|
1369
|
+
// src/infrastructure/api/parsers/parse-policy-set.ts
|
|
1370
|
+
var PolicySetSchema = schemas.PolicySetResponse.pick({
|
|
1371
|
+
id: true,
|
|
1372
|
+
name: true,
|
|
1373
|
+
description: true,
|
|
1374
|
+
organization_id: true,
|
|
1375
|
+
visibility: true,
|
|
1376
|
+
is_approved: true,
|
|
1377
|
+
entries: true,
|
|
1378
|
+
created_at: true
|
|
1379
|
+
}).strip();
|
|
1380
|
+
var parsePolicySet = (raw) => parseWithSchema(PolicySetSchema, raw, "policy-set");
|
|
1381
|
+
|
|
1382
|
+
// src/infrastructure/api/parsers/parse-policy-set-page.ts
|
|
1383
|
+
var PolicySetListItemSchema = schemas.PolicySetListItem.pick({
|
|
1384
|
+
id: true,
|
|
1385
|
+
name: true,
|
|
1386
|
+
description: true,
|
|
1387
|
+
organization_id: true,
|
|
1388
|
+
visibility: true,
|
|
1389
|
+
is_approved: true,
|
|
1390
|
+
created_at: true
|
|
1391
|
+
}).strip();
|
|
1392
|
+
var parsePolicySetPage = (raw) => parsePagedWithItemSchema(PolicySetListItemSchema, raw, "policy-sets");
|
|
1393
|
+
|
|
1394
|
+
// src/infrastructure/api/parsers/parse-run.ts
|
|
1395
|
+
var RunSchema = schemas.RunResponse.pick({
|
|
1396
|
+
id: true,
|
|
1397
|
+
campaign_id: true,
|
|
1398
|
+
label: true,
|
|
1399
|
+
total: true,
|
|
1400
|
+
completed: true,
|
|
1401
|
+
failed: true,
|
|
1402
|
+
partial: true,
|
|
1403
|
+
cancelled: true,
|
|
1404
|
+
source: true,
|
|
1405
|
+
created_at: true
|
|
1406
|
+
}).strip();
|
|
1407
|
+
var parseRun = (raw) => parseWithSchema(RunSchema, raw, "run");
|
|
1408
|
+
|
|
1409
|
+
// src/infrastructure/api/parsers/parse-run-scan-page.ts
|
|
1410
|
+
var ScanTileSchema = schemas.ScanTileResponse.pick({
|
|
1411
|
+
id: true,
|
|
1412
|
+
country_code: true,
|
|
1413
|
+
status: true,
|
|
1414
|
+
offer_url: true,
|
|
1415
|
+
screenshot_url: true,
|
|
1416
|
+
elapsed_ms: true,
|
|
1417
|
+
error: true
|
|
1418
|
+
}).strip();
|
|
1419
|
+
var parseRunScanPage = (raw) => parsePagedWithItemSchema(ScanTileSchema, raw, "run-scans");
|
|
1420
|
+
var ScanSchema = schemas.ScanResponse.pick({
|
|
1421
|
+
id: true,
|
|
1422
|
+
url: true,
|
|
1423
|
+
country_code: true,
|
|
1424
|
+
emulator_id: true,
|
|
1425
|
+
status: true,
|
|
1426
|
+
offer_url: true,
|
|
1427
|
+
screenshot_url: true,
|
|
1428
|
+
ad_tag: true,
|
|
1429
|
+
creative_screenshot_url: true,
|
|
1430
|
+
page_title: true,
|
|
1431
|
+
elapsed_ms: true,
|
|
1432
|
+
error: true,
|
|
1433
|
+
labels: true,
|
|
1434
|
+
campaign_id: true,
|
|
1435
|
+
campaign_name: true,
|
|
1436
|
+
created_at: true,
|
|
1437
|
+
completed_at: true
|
|
1438
|
+
}).strip();
|
|
1439
|
+
var ScanArraySchema = z.array(ScanSchema);
|
|
1440
|
+
var parseScan = (raw) => parseWithSchema(ScanSchema, raw, "scan");
|
|
1441
|
+
var parseScanArray = (raw) => parseWithSchema(ScanArraySchema, raw, "scans");
|
|
1442
|
+
|
|
1443
|
+
// src/infrastructure/api/parsers/parse-scan-page.ts
|
|
1444
|
+
var ScanBriefSchema = schemas.ScanBriefResponse.pick({
|
|
1445
|
+
id: true,
|
|
1446
|
+
url: true,
|
|
1447
|
+
country_code: true,
|
|
1448
|
+
status: true,
|
|
1449
|
+
offer_url: true,
|
|
1450
|
+
screenshot_url: true,
|
|
1451
|
+
labels: true,
|
|
1452
|
+
elapsed_ms: true,
|
|
1453
|
+
campaign_id: true,
|
|
1454
|
+
campaign_name: true,
|
|
1455
|
+
is_ad_tag: true,
|
|
1456
|
+
created_at: true
|
|
1457
|
+
}).strip();
|
|
1458
|
+
var parseScanPage = (raw) => parsePagedWithItemSchema(ScanBriefSchema, raw, "scans");
|
|
1459
|
+
var TagDefinitionSchema = schemas.TagDefinitionWithStatsResponse.pick({
|
|
1460
|
+
slug: true,
|
|
1461
|
+
category: true,
|
|
1462
|
+
source: true,
|
|
1463
|
+
display_name: true,
|
|
1464
|
+
description: true,
|
|
1465
|
+
severity: true,
|
|
1466
|
+
is_system: true,
|
|
1467
|
+
organization_id: true,
|
|
1468
|
+
show_in_public_report: true,
|
|
1469
|
+
scans_count: true,
|
|
1470
|
+
rules_count: true
|
|
1471
|
+
}).strip();
|
|
1472
|
+
var TagDefinitionArraySchema = z.array(TagDefinitionSchema);
|
|
1473
|
+
var LinkedRuleSchema = schemas.LinkedRuleResponse.pick({
|
|
1474
|
+
id: true,
|
|
1475
|
+
name: true,
|
|
1476
|
+
is_active: true
|
|
1477
|
+
}).strip();
|
|
1478
|
+
var TagDetailSchema = schemas.TagDefinitionDetailResponse.pick({
|
|
1479
|
+
slug: true,
|
|
1480
|
+
category: true,
|
|
1481
|
+
source: true,
|
|
1482
|
+
display_name: true,
|
|
1483
|
+
description: true,
|
|
1484
|
+
severity: true,
|
|
1485
|
+
is_system: true,
|
|
1486
|
+
organization_id: true,
|
|
1487
|
+
show_in_public_report: true,
|
|
1488
|
+
scans_count: true,
|
|
1489
|
+
rules_count: true
|
|
1490
|
+
}).extend({ linked_rules: z.array(LinkedRuleSchema).optional() }).strip();
|
|
1491
|
+
var parseTagDefinitionArray = (raw) => parseWithSchema(TagDefinitionArraySchema, raw, "tag-definitions");
|
|
1492
|
+
var parseTagDetail = (raw) => parseWithSchema(TagDetailSchema, raw, "tag-definition");
|
|
1493
|
+
var WebhookSchema = schemas.WebhookResponse.pick({
|
|
1494
|
+
id: true,
|
|
1495
|
+
url: true,
|
|
1496
|
+
description: true,
|
|
1497
|
+
event_types: true,
|
|
1498
|
+
campaign_ids: true,
|
|
1499
|
+
is_active: true,
|
|
1500
|
+
disabled_reason: true,
|
|
1501
|
+
disabled_at: true,
|
|
1502
|
+
health: true,
|
|
1503
|
+
created_at: true,
|
|
1504
|
+
updated_at: true
|
|
1505
|
+
}).strip();
|
|
1506
|
+
var WebhookListSchema = z.array(WebhookSchema);
|
|
1507
|
+
var WebhookCreatedSchema = z.object({
|
|
1508
|
+
webhook: WebhookSchema,
|
|
1509
|
+
secret: z.string()
|
|
1510
|
+
}).strip();
|
|
1511
|
+
var TestWebhookResponseSchema = schemas.TestWebhookResponse.pick({
|
|
1512
|
+
success: true,
|
|
1513
|
+
response_status: true,
|
|
1514
|
+
elapsed_ms: true,
|
|
1515
|
+
error_code: true,
|
|
1516
|
+
response_body: true
|
|
1517
|
+
}).strip();
|
|
1518
|
+
var parseWebhook = (raw) => parseWithSchema(WebhookSchema, raw, "webhook");
|
|
1519
|
+
var parseWebhookList = (raw) => parseWithSchema(WebhookListSchema, raw, "webhooks");
|
|
1520
|
+
var parseWebhookCreated = (raw) => parseWithSchema(WebhookCreatedSchema, raw, "webhook-created");
|
|
1521
|
+
var parseTestWebhookResponse = (raw) => parseWithSchema(TestWebhookResponseSchema, raw, "test-webhook");
|
|
1522
|
+
|
|
1523
|
+
// src/infrastructure/api/http-api-gateway.ts
|
|
1524
|
+
function createHttpApiGateway(config) {
|
|
1525
|
+
const { baseUrl, bearer, requestId, logger, dispatcher } = config;
|
|
1526
|
+
const fetchImpl = async (input, init) => {
|
|
1527
|
+
if (typeof input === "string" || input instanceof URL) {
|
|
1528
|
+
throw new TypeError(
|
|
1529
|
+
"createHttpApiGateway.fetchImpl: openapi-fetch is expected to always pass a Request"
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
const baseInit = { ...init ?? {} };
|
|
1533
|
+
baseInit["method"] ??= input.method;
|
|
1534
|
+
if (baseInit["headers"] === void 0) {
|
|
1535
|
+
const merged = {};
|
|
1536
|
+
input.headers.forEach((value, key) => {
|
|
1537
|
+
merged[key] = value;
|
|
1538
|
+
});
|
|
1539
|
+
baseInit["headers"] = merged;
|
|
1540
|
+
}
|
|
1541
|
+
if (baseInit["body"] === void 0 && input.body !== null) {
|
|
1542
|
+
baseInit["body"] = await input.text();
|
|
1543
|
+
baseInit["duplex"] ??= "half";
|
|
1544
|
+
}
|
|
1545
|
+
if (dispatcher !== void 0) {
|
|
1546
|
+
baseInit["dispatcher"] = dispatcher;
|
|
1547
|
+
}
|
|
1548
|
+
return fetch(
|
|
1549
|
+
input.url,
|
|
1550
|
+
baseInit
|
|
1551
|
+
);
|
|
1552
|
+
};
|
|
1553
|
+
const client = createClient({
|
|
1554
|
+
baseUrl,
|
|
1555
|
+
fetch: fetchImpl,
|
|
1556
|
+
headers: {
|
|
1557
|
+
authorization: bearer.toAuthorizationHeader(),
|
|
1558
|
+
"content-type": "application/json",
|
|
1559
|
+
accept: "application/json",
|
|
1560
|
+
"user-agent": "kaminari-ad-mcp",
|
|
1561
|
+
"x-request-id": requestId
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
const dispatch = {
|
|
1565
|
+
GET: client.GET.bind(client),
|
|
1566
|
+
POST: client.POST.bind(client),
|
|
1567
|
+
PATCH: client.PATCH.bind(client),
|
|
1568
|
+
PUT: client.PUT.bind(client),
|
|
1569
|
+
DELETE: client.DELETE.bind(client)
|
|
1570
|
+
};
|
|
1571
|
+
async function call(method, path, init, parse) {
|
|
1572
|
+
const startedAtMs = Date.now();
|
|
1573
|
+
let result;
|
|
1574
|
+
try {
|
|
1575
|
+
result = await dispatch[method](path, init);
|
|
1576
|
+
} catch (cause) {
|
|
1577
|
+
logger.warn({ api_path: path, elapsed_ms: Date.now() - startedAtMs }, "api.network_error");
|
|
1578
|
+
const inner = cause instanceof Error && cause.cause instanceof Error ? cause.cause : cause instanceof Error ? cause : null;
|
|
1579
|
+
return err({
|
|
1580
|
+
kind: "upstream",
|
|
1581
|
+
detail: inner !== null ? inner.message : "network error"
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
const { data, error, response } = result;
|
|
1585
|
+
const status2 = response.status;
|
|
1586
|
+
logger.info(
|
|
1587
|
+
{ api_path: path, api_status: status2, elapsed_ms: Date.now() - startedAtMs },
|
|
1588
|
+
"api.done"
|
|
1589
|
+
);
|
|
1590
|
+
if (status2 >= 200 && status2 < 300) {
|
|
1591
|
+
return parse(data === void 0 ? null : data);
|
|
1592
|
+
}
|
|
1593
|
+
return err(toApiError(status2, error ?? data, response.headers.get("retry-after") ?? void 0));
|
|
1594
|
+
}
|
|
1595
|
+
return {
|
|
1596
|
+
// ── Account ───────────────────────────────────────────────────
|
|
1597
|
+
async getAccount() {
|
|
1598
|
+
return call("GET", "/api/v1/account", {}, parseOrg);
|
|
1599
|
+
},
|
|
1600
|
+
async updateOrg(body) {
|
|
1601
|
+
return call("PATCH", "/api/v1/account", { body }, parseOrg);
|
|
1602
|
+
},
|
|
1603
|
+
async listOrgUsers() {
|
|
1604
|
+
return call("GET", "/api/v1/account/users", {}, parseArrayOf(parseUser));
|
|
1605
|
+
},
|
|
1606
|
+
async inviteUser(body) {
|
|
1607
|
+
return call("POST", "/api/v1/account/users/invite", { body }, parseUser);
|
|
1608
|
+
},
|
|
1609
|
+
async updateUserRole(userId, body) {
|
|
1610
|
+
return call(
|
|
1611
|
+
"PATCH",
|
|
1612
|
+
"/api/v1/account/users/{user_id}/role",
|
|
1613
|
+
{ params: { path: { user_id: userId } }, body },
|
|
1614
|
+
parseEmpty
|
|
1615
|
+
);
|
|
1616
|
+
},
|
|
1617
|
+
async removeUser(userId) {
|
|
1618
|
+
return call(
|
|
1619
|
+
"DELETE",
|
|
1620
|
+
"/api/v1/account/users/{user_id}",
|
|
1621
|
+
{ params: { path: { user_id: userId } } },
|
|
1622
|
+
parseEmpty
|
|
1623
|
+
);
|
|
1624
|
+
},
|
|
1625
|
+
async transferOwnership(userId) {
|
|
1626
|
+
return call(
|
|
1627
|
+
"POST",
|
|
1628
|
+
"/api/v1/account/users/{user_id}/transfer-ownership",
|
|
1629
|
+
{ params: { path: { user_id: userId } } },
|
|
1630
|
+
parseEmpty
|
|
1631
|
+
);
|
|
1632
|
+
},
|
|
1633
|
+
async listOrgRoles() {
|
|
1634
|
+
return call("GET", "/api/v1/account/roles", {}, parseArrayOf(parseRole));
|
|
1635
|
+
},
|
|
1636
|
+
async listApiKeys() {
|
|
1637
|
+
return call("GET", "/api/v1/account/api-keys", {}, parseApiKeyList);
|
|
1638
|
+
},
|
|
1639
|
+
async createApiKey(body) {
|
|
1640
|
+
return call("POST", "/api/v1/account/api-keys", { body }, parseApiKeyCreated);
|
|
1641
|
+
},
|
|
1642
|
+
async revokeApiKey(keyId) {
|
|
1643
|
+
return call(
|
|
1644
|
+
"DELETE",
|
|
1645
|
+
"/api/v1/account/api-keys/{key_id}",
|
|
1646
|
+
{ params: { path: { key_id: keyId } } },
|
|
1647
|
+
parseEmpty
|
|
1648
|
+
);
|
|
1649
|
+
},
|
|
1650
|
+
// ── Scans ─────────────────────────────────────────────────────
|
|
1651
|
+
async listScans(filters) {
|
|
1652
|
+
return call("GET", "/api/v1/scans", { params: { query: filters } }, parseScanPage);
|
|
1653
|
+
},
|
|
1654
|
+
async getScan(scanId) {
|
|
1655
|
+
return call(
|
|
1656
|
+
"GET",
|
|
1657
|
+
"/api/v1/scans/{scan_id}",
|
|
1658
|
+
{ params: { path: { scan_id: scanId } } },
|
|
1659
|
+
parseScan
|
|
1660
|
+
);
|
|
1661
|
+
},
|
|
1662
|
+
async createScan(body) {
|
|
1663
|
+
return call("POST", "/api/v1/scans", { body }, parseScan);
|
|
1664
|
+
},
|
|
1665
|
+
async createBulkScans(body) {
|
|
1666
|
+
return call("POST", "/api/v1/scans/bulk", { body }, parseScanArray);
|
|
1667
|
+
},
|
|
1668
|
+
async recheckScans(body) {
|
|
1669
|
+
return call(
|
|
1670
|
+
"POST",
|
|
1671
|
+
"/api/v1/scans/recheck",
|
|
1672
|
+
{ body },
|
|
1673
|
+
(raw) => parseIntField(raw, "queued_count")
|
|
1674
|
+
);
|
|
1675
|
+
},
|
|
1676
|
+
async cancelScan(scanId) {
|
|
1677
|
+
return call(
|
|
1678
|
+
"POST",
|
|
1679
|
+
"/api/v1/scans/{scan_id}/cancel",
|
|
1680
|
+
{ params: { path: { scan_id: scanId } } },
|
|
1681
|
+
(raw) => parseIntField(raw, "cancelled_count")
|
|
1682
|
+
);
|
|
1683
|
+
},
|
|
1684
|
+
async listScanTags(scanId) {
|
|
1685
|
+
return call(
|
|
1686
|
+
"GET",
|
|
1687
|
+
"/api/v1/scans/{scan_id}/tags",
|
|
1688
|
+
{ params: { path: { scan_id: scanId } } },
|
|
1689
|
+
parseArrayOf(parseScanTag)
|
|
1690
|
+
);
|
|
1691
|
+
},
|
|
1692
|
+
// ── Geos / emulators ──────────────────────────────────────────
|
|
1693
|
+
async listGeos() {
|
|
1694
|
+
return call("GET", "/api/v1/geos", {}, parseGeoList);
|
|
1695
|
+
},
|
|
1696
|
+
async listEmulators() {
|
|
1697
|
+
return call("GET", "/api/v1/emulators", {}, parseEmulatorList);
|
|
1698
|
+
},
|
|
1699
|
+
// ── Campaigns ─────────────────────────────────────────────────
|
|
1700
|
+
async listCampaigns(filters) {
|
|
1701
|
+
return call("GET", "/api/v1/campaigns", { params: { query: filters } }, parseCampaignPage);
|
|
1702
|
+
},
|
|
1703
|
+
async getCampaign(id) {
|
|
1704
|
+
return call(
|
|
1705
|
+
"GET",
|
|
1706
|
+
"/api/v1/campaigns/{campaign_id}",
|
|
1707
|
+
{ params: { path: { campaign_id: id } } },
|
|
1708
|
+
parseCampaign
|
|
1709
|
+
);
|
|
1710
|
+
},
|
|
1711
|
+
async createCampaign(body) {
|
|
1712
|
+
return call("POST", "/api/v1/campaigns", { body }, parseCampaign);
|
|
1713
|
+
},
|
|
1714
|
+
async updateCampaign(id, body) {
|
|
1715
|
+
return call(
|
|
1716
|
+
"PATCH",
|
|
1717
|
+
"/api/v1/campaigns/{campaign_id}",
|
|
1718
|
+
{ params: { path: { campaign_id: id } }, body },
|
|
1719
|
+
parseCampaign
|
|
1720
|
+
);
|
|
1721
|
+
},
|
|
1722
|
+
async runCampaign(id) {
|
|
1723
|
+
return call(
|
|
1724
|
+
"POST",
|
|
1725
|
+
"/api/v1/campaigns/{campaign_id}/run",
|
|
1726
|
+
{ params: { path: { campaign_id: id } } },
|
|
1727
|
+
parseRun
|
|
1728
|
+
);
|
|
1729
|
+
},
|
|
1730
|
+
async archiveCampaign(id) {
|
|
1731
|
+
return call(
|
|
1732
|
+
"POST",
|
|
1733
|
+
"/api/v1/campaigns/{campaign_id}/archive",
|
|
1734
|
+
{ params: { path: { campaign_id: id } } },
|
|
1735
|
+
parseCampaign
|
|
1736
|
+
);
|
|
1737
|
+
},
|
|
1738
|
+
async unarchiveCampaign(id) {
|
|
1739
|
+
return call(
|
|
1740
|
+
"POST",
|
|
1741
|
+
"/api/v1/campaigns/{campaign_id}/unarchive",
|
|
1742
|
+
{ params: { path: { campaign_id: id } } },
|
|
1743
|
+
parseCampaign
|
|
1744
|
+
);
|
|
1745
|
+
},
|
|
1746
|
+
async cancelCampaign(id) {
|
|
1747
|
+
return call(
|
|
1748
|
+
"POST",
|
|
1749
|
+
"/api/v1/campaigns/{campaign_id}/cancel",
|
|
1750
|
+
{ params: { path: { campaign_id: id } } },
|
|
1751
|
+
(raw) => parseIntField(raw, "cancelled_count")
|
|
1752
|
+
);
|
|
1753
|
+
},
|
|
1754
|
+
async listCampaignRuns(campaignId, filters) {
|
|
1755
|
+
return call(
|
|
1756
|
+
"GET",
|
|
1757
|
+
"/api/v1/campaigns/{campaign_id}/runs",
|
|
1758
|
+
{ params: { path: { campaign_id: campaignId }, query: filters } },
|
|
1759
|
+
parsePageOf(parseRun)
|
|
1760
|
+
);
|
|
1761
|
+
},
|
|
1762
|
+
async listCampaignsPicker() {
|
|
1763
|
+
return call("GET", "/api/v1/campaigns/picker", {}, parseCampaignPickerArray);
|
|
1764
|
+
},
|
|
1765
|
+
// ── Runs ──────────────────────────────────────────────────────
|
|
1766
|
+
async getRun(id) {
|
|
1767
|
+
return call("GET", "/api/v1/runs/{run_id}", { params: { path: { run_id: id } } }, parseRun);
|
|
1768
|
+
},
|
|
1769
|
+
async cancelRun(id) {
|
|
1770
|
+
return call(
|
|
1771
|
+
"POST",
|
|
1772
|
+
"/api/v1/runs/{run_id}/cancel",
|
|
1773
|
+
{ params: { path: { run_id: id } } },
|
|
1774
|
+
(raw) => parseIntField(raw, "cancelled_count")
|
|
1775
|
+
);
|
|
1776
|
+
},
|
|
1777
|
+
async listRunScans(runId, filters) {
|
|
1778
|
+
return call(
|
|
1779
|
+
"GET",
|
|
1780
|
+
"/api/v1/runs/{run_id}/scans",
|
|
1781
|
+
{ params: { path: { run_id: runId }, query: filters } },
|
|
1782
|
+
parseRunScanPage
|
|
1783
|
+
);
|
|
1784
|
+
},
|
|
1785
|
+
// ── Campaign groups ───────────────────────────────────────────
|
|
1786
|
+
async listCampaignGroups(filters) {
|
|
1787
|
+
return call(
|
|
1788
|
+
"GET",
|
|
1789
|
+
"/api/v1/campaign-groups",
|
|
1790
|
+
{ params: { query: filters ?? {} } },
|
|
1791
|
+
parseCampaignGroupArray
|
|
1792
|
+
);
|
|
1793
|
+
},
|
|
1794
|
+
async getCampaignGroup(id) {
|
|
1795
|
+
return call(
|
|
1796
|
+
"GET",
|
|
1797
|
+
"/api/v1/campaign-groups/{group_id}",
|
|
1798
|
+
{ params: { path: { group_id: id } } },
|
|
1799
|
+
parseCampaignGroup
|
|
1800
|
+
);
|
|
1801
|
+
},
|
|
1802
|
+
async createCampaignGroup(body) {
|
|
1803
|
+
return call("POST", "/api/v1/campaign-groups", { body }, parseCampaignGroup);
|
|
1804
|
+
},
|
|
1805
|
+
async updateCampaignGroup(id, body) {
|
|
1806
|
+
return call(
|
|
1807
|
+
"PATCH",
|
|
1808
|
+
"/api/v1/campaign-groups/{group_id}",
|
|
1809
|
+
{ params: { path: { group_id: id } }, body },
|
|
1810
|
+
parseCampaignGroup
|
|
1811
|
+
);
|
|
1812
|
+
},
|
|
1813
|
+
async runCampaignGroup(id) {
|
|
1814
|
+
return call(
|
|
1815
|
+
"POST",
|
|
1816
|
+
"/api/v1/campaign-groups/{group_id}/run",
|
|
1817
|
+
{ params: { path: { group_id: id } } },
|
|
1818
|
+
parseGroupAction
|
|
1819
|
+
);
|
|
1820
|
+
},
|
|
1821
|
+
async cancelCampaignGroup(id) {
|
|
1822
|
+
return call(
|
|
1823
|
+
"POST",
|
|
1824
|
+
"/api/v1/campaign-groups/{group_id}/cancel",
|
|
1825
|
+
{ params: { path: { group_id: id } } },
|
|
1826
|
+
parseGroupAction
|
|
1827
|
+
);
|
|
1828
|
+
},
|
|
1829
|
+
async archiveCampaignGroup(id) {
|
|
1830
|
+
return call(
|
|
1831
|
+
"POST",
|
|
1832
|
+
"/api/v1/campaign-groups/{group_id}/archive",
|
|
1833
|
+
{ params: { path: { group_id: id } } },
|
|
1834
|
+
parseGroupAction
|
|
1835
|
+
);
|
|
1836
|
+
},
|
|
1837
|
+
async unarchiveCampaignGroup(id) {
|
|
1838
|
+
return call(
|
|
1839
|
+
"POST",
|
|
1840
|
+
"/api/v1/campaign-groups/{group_id}/unarchive",
|
|
1841
|
+
{ params: { path: { group_id: id } } },
|
|
1842
|
+
parseGroupAction
|
|
1843
|
+
);
|
|
1844
|
+
},
|
|
1845
|
+
async pauseCampaignGroupSchedule(id) {
|
|
1846
|
+
return call(
|
|
1847
|
+
"POST",
|
|
1848
|
+
"/api/v1/campaign-groups/{group_id}/pause-schedule",
|
|
1849
|
+
{ params: { path: { group_id: id } } },
|
|
1850
|
+
parseCampaignGroup
|
|
1851
|
+
);
|
|
1852
|
+
},
|
|
1853
|
+
async resumeCampaignGroupSchedule(id) {
|
|
1854
|
+
return call(
|
|
1855
|
+
"POST",
|
|
1856
|
+
"/api/v1/campaign-groups/{group_id}/resume-schedule",
|
|
1857
|
+
{ params: { path: { group_id: id } } },
|
|
1858
|
+
parseCampaignGroup
|
|
1859
|
+
);
|
|
1860
|
+
},
|
|
1861
|
+
// ── Tag definitions ───────────────────────────────────────────
|
|
1862
|
+
async listTags() {
|
|
1863
|
+
return call("GET", "/api/v1/tag-definitions", {}, parseTagDefinitionArray);
|
|
1864
|
+
},
|
|
1865
|
+
async getTagDefinition(slug) {
|
|
1866
|
+
return call(
|
|
1867
|
+
"GET",
|
|
1868
|
+
"/api/v1/tag-definitions/{slug}",
|
|
1869
|
+
{ params: { path: { slug } } },
|
|
1870
|
+
parseTagDetail
|
|
1871
|
+
);
|
|
1872
|
+
},
|
|
1873
|
+
async updateTagDefinition(slug, body) {
|
|
1874
|
+
return call(
|
|
1875
|
+
"PATCH",
|
|
1876
|
+
"/api/v1/tag-definitions/{slug}",
|
|
1877
|
+
{ params: { path: { slug } }, body },
|
|
1878
|
+
parseEmpty
|
|
1879
|
+
);
|
|
1880
|
+
},
|
|
1881
|
+
async deleteTagDefinition(slug) {
|
|
1882
|
+
return call(
|
|
1883
|
+
"DELETE",
|
|
1884
|
+
"/api/v1/tag-definitions/{slug}",
|
|
1885
|
+
{ params: { path: { slug } } },
|
|
1886
|
+
parseEmpty
|
|
1887
|
+
);
|
|
1888
|
+
},
|
|
1889
|
+
// ── Custom rules ──────────────────────────────────────────────
|
|
1890
|
+
async listCustomRules(filters) {
|
|
1891
|
+
return call(
|
|
1892
|
+
"GET",
|
|
1893
|
+
"/api/v1/custom-rules",
|
|
1894
|
+
{ params: { query: filters } },
|
|
1895
|
+
parseCustomRulePage
|
|
1896
|
+
);
|
|
1897
|
+
},
|
|
1898
|
+
async getCustomRule(id) {
|
|
1899
|
+
return call(
|
|
1900
|
+
"GET",
|
|
1901
|
+
"/api/v1/custom-rules/{rule_id}",
|
|
1902
|
+
{ params: { path: { rule_id: id } } },
|
|
1903
|
+
parseCustomRule
|
|
1904
|
+
);
|
|
1905
|
+
},
|
|
1906
|
+
async createCustomRule(body) {
|
|
1907
|
+
return call("POST", "/api/v1/custom-rules", { body }, parseCustomRule);
|
|
1908
|
+
},
|
|
1909
|
+
async updateCustomRule(id, body) {
|
|
1910
|
+
return call(
|
|
1911
|
+
"PUT",
|
|
1912
|
+
"/api/v1/custom-rules/{rule_id}",
|
|
1913
|
+
{ params: { path: { rule_id: id } }, body },
|
|
1914
|
+
parseCustomRule
|
|
1915
|
+
);
|
|
1916
|
+
},
|
|
1917
|
+
async deleteCustomRule(id) {
|
|
1918
|
+
return call(
|
|
1919
|
+
"DELETE",
|
|
1920
|
+
"/api/v1/custom-rules/{rule_id}",
|
|
1921
|
+
{ params: { path: { rule_id: id } } },
|
|
1922
|
+
parseEmpty
|
|
1923
|
+
);
|
|
1924
|
+
},
|
|
1925
|
+
async testCustomRule(body) {
|
|
1926
|
+
return call("POST", "/api/v1/custom-rules/test", { body }, parseRuleTest);
|
|
1927
|
+
},
|
|
1928
|
+
// ── Policy sets ───────────────────────────────────────────────
|
|
1929
|
+
async listPolicySets(filters) {
|
|
1930
|
+
return call("GET", "/api/v1/policy-sets", { params: { query: filters } }, parsePolicySetPage);
|
|
1931
|
+
},
|
|
1932
|
+
async getPolicySet(id) {
|
|
1933
|
+
return call(
|
|
1934
|
+
"GET",
|
|
1935
|
+
"/api/v1/policy-sets/{policy_set_id}",
|
|
1936
|
+
{ params: { path: { policy_set_id: id } } },
|
|
1937
|
+
parsePolicySet
|
|
1938
|
+
);
|
|
1939
|
+
},
|
|
1940
|
+
async createPolicySet(body) {
|
|
1941
|
+
return call("POST", "/api/v1/policy-sets", { body }, parsePolicySet);
|
|
1942
|
+
},
|
|
1943
|
+
async updatePolicySet(id, body) {
|
|
1944
|
+
return call(
|
|
1945
|
+
"PUT",
|
|
1946
|
+
"/api/v1/policy-sets/{policy_set_id}",
|
|
1947
|
+
{ params: { path: { policy_set_id: id } }, body },
|
|
1948
|
+
parsePolicySet
|
|
1949
|
+
);
|
|
1950
|
+
},
|
|
1951
|
+
async deletePolicySet(id) {
|
|
1952
|
+
return call(
|
|
1953
|
+
"DELETE",
|
|
1954
|
+
"/api/v1/policy-sets/{policy_set_id}",
|
|
1955
|
+
{ params: { path: { policy_set_id: id } } },
|
|
1956
|
+
parseEmpty
|
|
1957
|
+
);
|
|
1958
|
+
},
|
|
1959
|
+
async requestPolicySetApproval(id) {
|
|
1960
|
+
return call(
|
|
1961
|
+
"POST",
|
|
1962
|
+
"/api/v1/policy-sets/{policy_set_id}/request-approval",
|
|
1963
|
+
{ params: { path: { policy_set_id: id } } },
|
|
1964
|
+
parseEmpty
|
|
1965
|
+
);
|
|
1966
|
+
},
|
|
1967
|
+
// ── Alerts ────────────────────────────────────────────────────
|
|
1968
|
+
async listAlerts(filters) {
|
|
1969
|
+
return call("GET", "/api/v1/alerts", { params: { query: filters } }, parseAlertPage);
|
|
1970
|
+
},
|
|
1971
|
+
async updateAlertStatus(alertId, body) {
|
|
1972
|
+
return call(
|
|
1973
|
+
"PATCH",
|
|
1974
|
+
"/api/v1/alerts/{alert_id}/status",
|
|
1975
|
+
{ params: { path: { alert_id: alertId } }, body },
|
|
1976
|
+
parseEmpty
|
|
1977
|
+
);
|
|
1978
|
+
},
|
|
1979
|
+
async getAlertStats() {
|
|
1980
|
+
return call("GET", "/api/v1/alerts/stats", {}, parseAlertStats);
|
|
1981
|
+
},
|
|
1982
|
+
// ── Webhooks ──────────────────────────────────────────────────
|
|
1983
|
+
async listWebhooks() {
|
|
1984
|
+
return call("GET", "/api/v1/webhooks", {}, parseWebhookList);
|
|
1985
|
+
},
|
|
1986
|
+
async getWebhook(id) {
|
|
1987
|
+
return call(
|
|
1988
|
+
"GET",
|
|
1989
|
+
"/api/v1/webhooks/{endpoint_id}",
|
|
1990
|
+
{ params: { path: { endpoint_id: id } } },
|
|
1991
|
+
parseWebhook
|
|
1992
|
+
);
|
|
1993
|
+
},
|
|
1994
|
+
async createWebhook(body) {
|
|
1995
|
+
return call("POST", "/api/v1/webhooks", { body }, parseWebhookCreated);
|
|
1996
|
+
},
|
|
1997
|
+
async updateWebhook(id, body) {
|
|
1998
|
+
return call(
|
|
1999
|
+
"PATCH",
|
|
2000
|
+
"/api/v1/webhooks/{endpoint_id}",
|
|
2001
|
+
{ params: { path: { endpoint_id: id } }, body },
|
|
2002
|
+
parseWebhook
|
|
2003
|
+
);
|
|
2004
|
+
},
|
|
2005
|
+
async deleteWebhook(id) {
|
|
2006
|
+
return call(
|
|
2007
|
+
"DELETE",
|
|
2008
|
+
"/api/v1/webhooks/{endpoint_id}",
|
|
2009
|
+
{ params: { path: { endpoint_id: id } } },
|
|
2010
|
+
parseEmpty
|
|
2011
|
+
);
|
|
2012
|
+
},
|
|
2013
|
+
async testWebhook(endpointId, body) {
|
|
2014
|
+
return call(
|
|
2015
|
+
"POST",
|
|
2016
|
+
"/api/v1/webhooks/{endpoint_id}/test",
|
|
2017
|
+
{ params: { path: { endpoint_id: endpointId } }, body },
|
|
2018
|
+
parseTestWebhookResponse
|
|
2019
|
+
);
|
|
2020
|
+
},
|
|
2021
|
+
async rotateWebhookSecret(endpointId) {
|
|
2022
|
+
return call(
|
|
2023
|
+
"POST",
|
|
2024
|
+
"/api/v1/webhooks/{endpoint_id}/rotate-secret",
|
|
2025
|
+
{ params: { path: { endpoint_id: endpointId } } },
|
|
2026
|
+
parseWebhookCreated
|
|
2027
|
+
);
|
|
2028
|
+
},
|
|
2029
|
+
async listWebhookEventTypes() {
|
|
2030
|
+
return call("GET", "/api/v1/webhooks/event-types", {}, parseEventCatalog);
|
|
2031
|
+
},
|
|
2032
|
+
async listWebhookDeliveries(endpointId, filters) {
|
|
2033
|
+
return call(
|
|
2034
|
+
"GET",
|
|
2035
|
+
"/api/v1/webhooks/{endpoint_id}/deliveries",
|
|
2036
|
+
{ params: { path: { endpoint_id: endpointId }, query: filters } },
|
|
2037
|
+
parsePageOf(parseWebhookDelivery)
|
|
2038
|
+
);
|
|
2039
|
+
},
|
|
2040
|
+
async replayWebhookDelivery(attemptId) {
|
|
2041
|
+
return call(
|
|
2042
|
+
"POST",
|
|
2043
|
+
"/api/v1/webhooks/deliveries/{attempt_id}/replay",
|
|
2044
|
+
{ params: { path: { attempt_id: attemptId } } },
|
|
2045
|
+
parseEmpty
|
|
2046
|
+
);
|
|
2047
|
+
},
|
|
2048
|
+
async bulkReplayWebhook(endpointId, body) {
|
|
2049
|
+
return call(
|
|
2050
|
+
"POST",
|
|
2051
|
+
"/api/v1/webhooks/{endpoint_id}/replay",
|
|
2052
|
+
{ params: { path: { endpoint_id: endpointId } }, body },
|
|
2053
|
+
parseBulkReplay
|
|
2054
|
+
);
|
|
2055
|
+
},
|
|
2056
|
+
// ── Billing ───────────────────────────────────────────────────
|
|
2057
|
+
async getBillingSummary() {
|
|
2058
|
+
return call("GET", "/api/v1/billing", {}, parseBillingSummary);
|
|
2059
|
+
},
|
|
2060
|
+
async listUsage(filters) {
|
|
2061
|
+
return call(
|
|
2062
|
+
"GET",
|
|
2063
|
+
"/api/v1/billing/usage",
|
|
2064
|
+
{ params: { query: filters } },
|
|
2065
|
+
parsePageOf(parseUsage)
|
|
2066
|
+
);
|
|
2067
|
+
},
|
|
2068
|
+
async getUsageSummary() {
|
|
2069
|
+
return call("GET", "/api/v1/billing/usage/summary", {}, parseUsageSummary);
|
|
2070
|
+
},
|
|
2071
|
+
async listBalanceHistory(filters) {
|
|
2072
|
+
return call(
|
|
2073
|
+
"GET",
|
|
2074
|
+
"/api/v1/billing/history",
|
|
2075
|
+
{ params: { query: filters } },
|
|
2076
|
+
parsePageOf(parseBalanceTx)
|
|
2077
|
+
);
|
|
2078
|
+
},
|
|
2079
|
+
// ── Invoicing ─────────────────────────────────────────────────
|
|
2080
|
+
async listInvoices(filters) {
|
|
2081
|
+
return call(
|
|
2082
|
+
"GET",
|
|
2083
|
+
"/api/v1/invoices",
|
|
2084
|
+
{ params: { query: filters } },
|
|
2085
|
+
parsePageOf(parseInvoice)
|
|
2086
|
+
);
|
|
2087
|
+
},
|
|
2088
|
+
// ── Alert notifications ───────────────────────────────────────
|
|
2089
|
+
async listAlertDestinations() {
|
|
2090
|
+
return call(
|
|
2091
|
+
"GET",
|
|
2092
|
+
"/api/v1/alert-notifications/destinations",
|
|
2093
|
+
{},
|
|
2094
|
+
parseArrayOf(parseAlertDestination)
|
|
2095
|
+
);
|
|
2096
|
+
},
|
|
2097
|
+
async deleteAlertDestination(id) {
|
|
2098
|
+
return call(
|
|
2099
|
+
"DELETE",
|
|
2100
|
+
"/api/v1/alert-notifications/destinations/{destination_id}",
|
|
2101
|
+
{ params: { path: { destination_id: id } } },
|
|
2102
|
+
parseEmpty
|
|
2103
|
+
);
|
|
2104
|
+
},
|
|
2105
|
+
async setAlertDestinationVersion(id, body) {
|
|
2106
|
+
return call(
|
|
2107
|
+
"PATCH",
|
|
2108
|
+
"/api/v1/alert-notifications/destinations/{destination_id}/version",
|
|
2109
|
+
{ params: { path: { destination_id: id } }, body },
|
|
2110
|
+
parseEmpty
|
|
2111
|
+
);
|
|
2112
|
+
},
|
|
2113
|
+
async getCampaignAlertOverrides(campaignId) {
|
|
2114
|
+
return call(
|
|
2115
|
+
"GET",
|
|
2116
|
+
"/api/v1/alert-notifications/campaigns/{campaign_id}/overrides",
|
|
2117
|
+
{ params: { path: { campaign_id: campaignId } } },
|
|
2118
|
+
parseCampaignAlertOverrides
|
|
2119
|
+
);
|
|
2120
|
+
},
|
|
2121
|
+
async setCampaignAlertOverrides(campaignId, body) {
|
|
2122
|
+
return call(
|
|
2123
|
+
"PUT",
|
|
2124
|
+
"/api/v1/alert-notifications/campaigns/{campaign_id}/overrides",
|
|
2125
|
+
{ params: { path: { campaign_id: campaignId } }, body },
|
|
2126
|
+
parseEmpty
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
var REDACTION_PATHS = [
|
|
2132
|
+
"authorization",
|
|
2133
|
+
"Authorization",
|
|
2134
|
+
"bearer",
|
|
2135
|
+
"Bearer",
|
|
2136
|
+
"*.authorization",
|
|
2137
|
+
"*.Authorization",
|
|
2138
|
+
"headers.authorization",
|
|
2139
|
+
"headers.Authorization",
|
|
2140
|
+
"req.headers.authorization",
|
|
2141
|
+
"req.headers.Authorization"
|
|
2142
|
+
];
|
|
2143
|
+
function createPinoLogger(level, format = "json", destination) {
|
|
2144
|
+
const options = {
|
|
2145
|
+
level,
|
|
2146
|
+
redact: { paths: [...REDACTION_PATHS], censor: "[REDACTED]" },
|
|
2147
|
+
base: null,
|
|
2148
|
+
timestamp: stdTimeFunctions.isoTime,
|
|
2149
|
+
formatters: {
|
|
2150
|
+
level: (label) => ({ level: label })
|
|
2151
|
+
}
|
|
2152
|
+
};
|
|
2153
|
+
const sink = destination ?? (format === "pretty" ? pinoPretty({
|
|
2154
|
+
colorize: true,
|
|
2155
|
+
ignore: "pid,hostname",
|
|
2156
|
+
destination: process.stderr.fd,
|
|
2157
|
+
sync: true
|
|
2158
|
+
}) : pino.destination(process.stderr.fd));
|
|
2159
|
+
return wrap(pino(options, sink));
|
|
2160
|
+
}
|
|
2161
|
+
function wrap(impl) {
|
|
2162
|
+
return {
|
|
2163
|
+
child(fields) {
|
|
2164
|
+
return wrap(impl.child({ ...fields }));
|
|
2165
|
+
},
|
|
2166
|
+
trace(fields, message) {
|
|
2167
|
+
impl.trace({ ...fields }, message);
|
|
2168
|
+
},
|
|
2169
|
+
debug(fields, message) {
|
|
2170
|
+
impl.debug({ ...fields }, message);
|
|
2171
|
+
},
|
|
2172
|
+
info(fields, message) {
|
|
2173
|
+
impl.info({ ...fields }, message);
|
|
2174
|
+
},
|
|
2175
|
+
warn(fields, message) {
|
|
2176
|
+
impl.warn({ ...fields }, message);
|
|
2177
|
+
},
|
|
2178
|
+
error(fields, message) {
|
|
2179
|
+
impl.error({ ...fields }, message);
|
|
2180
|
+
},
|
|
2181
|
+
fatal(fields, message) {
|
|
2182
|
+
impl.fatal({ ...fields }, message);
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
function declareEmptyResourcesAndPrompts(server) {
|
|
2187
|
+
server.server.registerCapabilities({ resources: {}, prompts: {} });
|
|
2188
|
+
server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
2189
|
+
await Promise.resolve();
|
|
2190
|
+
return { resources: [] };
|
|
2191
|
+
});
|
|
2192
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
2193
|
+
await Promise.resolve();
|
|
2194
|
+
return { prompts: [] };
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// src/application/services/api-error-mapper.ts
|
|
2199
|
+
function mapApiError(apiError) {
|
|
2200
|
+
switch (apiError.kind) {
|
|
2201
|
+
case "unauthorized":
|
|
2202
|
+
return { kind: "unauthorized", message: apiError.detail };
|
|
2203
|
+
case "forbidden":
|
|
2204
|
+
return apiError.code === void 0 ? { kind: "forbidden", message: apiError.detail } : { kind: "forbidden", message: apiError.detail, code: apiError.code };
|
|
2205
|
+
case "not-found":
|
|
2206
|
+
return { kind: "not-found", message: apiError.detail };
|
|
2207
|
+
case "rate-limited":
|
|
2208
|
+
return apiError.retryAfterMs === void 0 ? { kind: "rate-limited", message: apiError.detail } : { kind: "rate-limited", message: apiError.detail, retryAfterMs: apiError.retryAfterMs };
|
|
2209
|
+
case "invalid-input": {
|
|
2210
|
+
const out = { kind: "invalid-input", message: apiError.detail };
|
|
2211
|
+
if (apiError.fieldErrors !== void 0) out.fieldErrors = apiError.fieldErrors;
|
|
2212
|
+
if (apiError.code !== void 0) out.code = apiError.code;
|
|
2213
|
+
return out;
|
|
2214
|
+
}
|
|
2215
|
+
case "upstream":
|
|
2216
|
+
return apiError.status === void 0 ? { kind: "upstream", message: apiError.detail } : { kind: "upstream", message: apiError.detail, status: apiError.status };
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
// src/application/tools/account/create-api-key.tool.ts
|
|
2221
|
+
var CreateApiKeyInputShape = {
|
|
2222
|
+
name: z.string().min(1).max(100).describe("Human-readable label (e.g. `ci-pipeline`, `claude-mcp`)."),
|
|
2223
|
+
// Accept BOTH `null` and omit for "no expiry" — many JSON clients
|
|
2224
|
+
// (including a hand-written curl) send `null` explicitly; rejecting
|
|
2225
|
+
// it forces awkward request-body construction on the caller side.
|
|
2226
|
+
// Either form maps to API-side `expires_at: null` (the only shape
|
|
2227
|
+
// the API accepts for "no expiry").
|
|
2228
|
+
expires_at: z.string().datetime().nullable().optional().describe(
|
|
2229
|
+
"Optional ISO-8601 expiry timestamp. Omit or send `null` for a non-expiring key (operator can revoke any time)."
|
|
2230
|
+
)
|
|
2231
|
+
};
|
|
2232
|
+
var createApiKeyTool = {
|
|
2233
|
+
name: "create_api_key",
|
|
2234
|
+
description: "Mint a new API key for the caller's organization. The full secret is returned in `full_key` THIS ONE TIME ONLY \u2014 show it to the user and instruct them to store it; the server keeps only a hash and cannot reveal it again.",
|
|
2235
|
+
annotations: {
|
|
2236
|
+
title: "Create API Key",
|
|
2237
|
+
readOnlyHint: false,
|
|
2238
|
+
destructiveHint: false,
|
|
2239
|
+
idempotentHint: false,
|
|
2240
|
+
openWorldHint: false
|
|
2241
|
+
},
|
|
2242
|
+
inputSchema: z.object(CreateApiKeyInputShape),
|
|
2243
|
+
handler: async (input, ctx) => {
|
|
2244
|
+
const body = {
|
|
2245
|
+
name: input.name,
|
|
2246
|
+
// Forward both `null` and omitted as omitted to the gateway DTO
|
|
2247
|
+
// (gateway treats absent field as "no expiry"). `null` would
|
|
2248
|
+
// collide with the gateway's `Pick<CreateApiKeyRequest, ...>`
|
|
2249
|
+
// type that does not include `null`.
|
|
2250
|
+
...input.expires_at !== void 0 && input.expires_at !== null ? { expires_at: input.expires_at } : {}
|
|
2251
|
+
};
|
|
2252
|
+
const result = await ctx.api.createApiKey(body);
|
|
2253
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2254
|
+
return ok(result.value);
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
var GetAccountInputShape = {};
|
|
2258
|
+
var getAccountTool = {
|
|
2259
|
+
name: "get_account",
|
|
2260
|
+
description: "Get the organization owning the current API key (id, name, owner_id, is_active, created_at). Use this to confirm authentication and capture the org context for follow-up tool calls.",
|
|
2261
|
+
annotations: {
|
|
2262
|
+
title: "Get Account",
|
|
2263
|
+
readOnlyHint: true,
|
|
2264
|
+
destructiveHint: false,
|
|
2265
|
+
idempotentHint: true,
|
|
2266
|
+
openWorldHint: false
|
|
2267
|
+
},
|
|
2268
|
+
inputSchema: z.object(GetAccountInputShape),
|
|
2269
|
+
handler: async (_input, ctx) => {
|
|
2270
|
+
const result = await ctx.api.getAccount();
|
|
2271
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2272
|
+
return ok(result.value);
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
var InviteUserInputShape = {
|
|
2276
|
+
email: z.string().email().describe("Email of the person to invite. They get a signup link."),
|
|
2277
|
+
role_id: z.string().uuid().describe("UUID of the role to assign on accept. Get UUIDs from `list_org_roles`."),
|
|
2278
|
+
name: z.string().min(1).optional().describe("Optional display name for the invitee.")
|
|
2279
|
+
};
|
|
2280
|
+
var inviteUserTool = {
|
|
2281
|
+
name: "invite_user",
|
|
2282
|
+
description: "Send an invitation email so a new person can join the caller's organization with a chosen role. Returns the pending member record.",
|
|
2283
|
+
annotations: {
|
|
2284
|
+
title: "Invite User",
|
|
2285
|
+
readOnlyHint: false,
|
|
2286
|
+
destructiveHint: false,
|
|
2287
|
+
idempotentHint: false,
|
|
2288
|
+
openWorldHint: false
|
|
2289
|
+
},
|
|
2290
|
+
inputSchema: z.object(InviteUserInputShape),
|
|
2291
|
+
handler: async (input, ctx) => {
|
|
2292
|
+
const body = {
|
|
2293
|
+
email: input.email,
|
|
2294
|
+
role_id: input.role_id
|
|
2295
|
+
};
|
|
2296
|
+
if (input.name !== void 0) body.name = input.name;
|
|
2297
|
+
const result = await ctx.api.inviteUser(body);
|
|
2298
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2299
|
+
return ok(result.value);
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
var ListApiKeysInputShape = {};
|
|
2303
|
+
var listApiKeysTool = {
|
|
2304
|
+
name: "list_api_keys",
|
|
2305
|
+
description: "List the organization's API keys: id, key prefix (first 8 chars of the secret), display name, expiry, created_at. The full secret is NEVER returned by this endpoint \u2014 only the prefix.",
|
|
2306
|
+
annotations: {
|
|
2307
|
+
title: "List Api Keys",
|
|
2308
|
+
readOnlyHint: true,
|
|
2309
|
+
destructiveHint: false,
|
|
2310
|
+
idempotentHint: true,
|
|
2311
|
+
openWorldHint: false
|
|
2312
|
+
},
|
|
2313
|
+
inputSchema: z.object(ListApiKeysInputShape),
|
|
2314
|
+
handler: async (_input, ctx) => {
|
|
2315
|
+
const result = await ctx.api.listApiKeys();
|
|
2316
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2317
|
+
return ok({ items: result.value, total: result.value.length });
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
var ListOrgRolesInputShape = {};
|
|
2321
|
+
var listOrgRolesTool = {
|
|
2322
|
+
name: "list_org_roles",
|
|
2323
|
+
description: "List the roles defined for the organization \u2014 built-in (owner, admin, member) plus any custom roles, with each role's permission set.",
|
|
2324
|
+
annotations: {
|
|
2325
|
+
title: "List Organization Roles",
|
|
2326
|
+
readOnlyHint: true,
|
|
2327
|
+
destructiveHint: false,
|
|
2328
|
+
idempotentHint: true,
|
|
2329
|
+
openWorldHint: false
|
|
2330
|
+
},
|
|
2331
|
+
inputSchema: z.object(ListOrgRolesInputShape),
|
|
2332
|
+
handler: async (_input, ctx) => {
|
|
2333
|
+
const result = await ctx.api.listOrgRoles();
|
|
2334
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2335
|
+
return ok({ items: result.value, total: result.value.length });
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
var ListOrgUsersInputShape = {};
|
|
2339
|
+
var listOrgUsersTool = {
|
|
2340
|
+
name: "list_org_users",
|
|
2341
|
+
description: "List every member of the caller's organization with their role, ownership flag, and join date.",
|
|
2342
|
+
annotations: {
|
|
2343
|
+
title: "List Organization Members",
|
|
2344
|
+
readOnlyHint: true,
|
|
2345
|
+
destructiveHint: false,
|
|
2346
|
+
idempotentHint: true,
|
|
2347
|
+
openWorldHint: false
|
|
2348
|
+
},
|
|
2349
|
+
inputSchema: z.object(ListOrgUsersInputShape),
|
|
2350
|
+
handler: async (_input, ctx) => {
|
|
2351
|
+
const result = await ctx.api.listOrgUsers();
|
|
2352
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2353
|
+
return ok({ items: result.value, total: result.value.length });
|
|
2354
|
+
}
|
|
2355
|
+
};
|
|
2356
|
+
var RemoveUserInputShape = {
|
|
2357
|
+
user_id: z.string().uuid().describe("UUID of the member to remove.")
|
|
2358
|
+
};
|
|
2359
|
+
var removeUserTool = {
|
|
2360
|
+
name: "remove_user",
|
|
2361
|
+
description: "Revoke a member's access to the organization. The user is signed out and any active API keys they created remain unless revoked separately. CANNOT remove the owner \u2014 use `transfer_ownership` first.",
|
|
2362
|
+
annotations: {
|
|
2363
|
+
title: "Remove User",
|
|
2364
|
+
readOnlyHint: false,
|
|
2365
|
+
destructiveHint: true,
|
|
2366
|
+
idempotentHint: true,
|
|
2367
|
+
openWorldHint: false
|
|
2368
|
+
},
|
|
2369
|
+
inputSchema: z.object(RemoveUserInputShape),
|
|
2370
|
+
handler: async (input, ctx) => {
|
|
2371
|
+
const result = await ctx.api.removeUser(input.user_id);
|
|
2372
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2373
|
+
return ok({ removed: true });
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
var RevokeApiKeyInputShape = {
|
|
2377
|
+
key_id: z.string().uuid().describe("UUID of the key to revoke (from `list_api_keys`).")
|
|
2378
|
+
};
|
|
2379
|
+
var revokeApiKeyTool = {
|
|
2380
|
+
name: "revoke_api_key",
|
|
2381
|
+
description: "Permanently invalidate an API key. Any subsequent request using it returns 401. Cannot be undone \u2014 the user would have to `create_api_key` again.",
|
|
2382
|
+
annotations: {
|
|
2383
|
+
title: "Revoke API Key",
|
|
2384
|
+
readOnlyHint: false,
|
|
2385
|
+
destructiveHint: true,
|
|
2386
|
+
idempotentHint: true,
|
|
2387
|
+
openWorldHint: false
|
|
2388
|
+
},
|
|
2389
|
+
inputSchema: z.object(RevokeApiKeyInputShape),
|
|
2390
|
+
handler: async (input, ctx) => {
|
|
2391
|
+
const result = await ctx.api.revokeApiKey(input.key_id);
|
|
2392
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2393
|
+
return ok({ revoked: true });
|
|
2394
|
+
}
|
|
2395
|
+
};
|
|
2396
|
+
var TransferOwnershipInputShape = {
|
|
2397
|
+
user_id: z.string().uuid().describe("UUID of the existing member to become the new owner.")
|
|
2398
|
+
};
|
|
2399
|
+
var transferOwnershipTool = {
|
|
2400
|
+
name: "transfer_ownership",
|
|
2401
|
+
description: "Hand the organization owner role to another existing member. ONE-WAY: the previous owner becomes a regular member afterwards. Require explicit confirmation from the user.",
|
|
2402
|
+
annotations: {
|
|
2403
|
+
title: "Transfer Organization Ownership",
|
|
2404
|
+
readOnlyHint: false,
|
|
2405
|
+
destructiveHint: true,
|
|
2406
|
+
idempotentHint: false,
|
|
2407
|
+
openWorldHint: false
|
|
2408
|
+
},
|
|
2409
|
+
inputSchema: z.object(TransferOwnershipInputShape),
|
|
2410
|
+
handler: async (input, ctx) => {
|
|
2411
|
+
const result = await ctx.api.transferOwnership(input.user_id);
|
|
2412
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2413
|
+
return ok({ transferred: true });
|
|
2414
|
+
}
|
|
2415
|
+
};
|
|
2416
|
+
var UpdateOrgInputShape = {
|
|
2417
|
+
name: z.string().min(1).max(200).optional().describe("New organization display name."),
|
|
2418
|
+
settings: z.record(z.unknown()).optional().describe("Replacement settings object. Fields not supplied are left unchanged.")
|
|
2419
|
+
};
|
|
2420
|
+
var updateOrgTool = {
|
|
2421
|
+
name: "update_org",
|
|
2422
|
+
description: "Update the caller's organization \u2014 display name and/or settings object. Only supplied fields are touched.",
|
|
2423
|
+
annotations: {
|
|
2424
|
+
title: "Update Organization",
|
|
2425
|
+
readOnlyHint: false,
|
|
2426
|
+
destructiveHint: false,
|
|
2427
|
+
idempotentHint: true,
|
|
2428
|
+
openWorldHint: false
|
|
2429
|
+
},
|
|
2430
|
+
inputSchema: z.object(UpdateOrgInputShape),
|
|
2431
|
+
handler: async (input, ctx) => {
|
|
2432
|
+
const body = {
|
|
2433
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
2434
|
+
...input.settings !== void 0 ? { settings: input.settings } : {}
|
|
2435
|
+
};
|
|
2436
|
+
const result = await ctx.api.updateOrg(body);
|
|
2437
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2438
|
+
return ok(result.value);
|
|
2439
|
+
}
|
|
2440
|
+
};
|
|
2441
|
+
var UpdateUserRoleInputShape = {
|
|
2442
|
+
user_id: z.string().uuid().describe("UUID of the member to update."),
|
|
2443
|
+
role_id: z.string().uuid().describe("UUID of the new role (see `list_org_roles`).")
|
|
2444
|
+
};
|
|
2445
|
+
var updateUserRoleTool = {
|
|
2446
|
+
name: "update_user_role",
|
|
2447
|
+
description: "Change an organization member's role. The owner role can only be transferred via `transfer_ownership`. Returns `{updated: true}` on success; refetch with `list_org_users` if you need the new role echoed.",
|
|
2448
|
+
annotations: {
|
|
2449
|
+
title: "Update User Role",
|
|
2450
|
+
readOnlyHint: false,
|
|
2451
|
+
destructiveHint: false,
|
|
2452
|
+
idempotentHint: true,
|
|
2453
|
+
openWorldHint: false
|
|
2454
|
+
},
|
|
2455
|
+
inputSchema: z.object(UpdateUserRoleInputShape),
|
|
2456
|
+
handler: async (input, ctx) => {
|
|
2457
|
+
const result = await ctx.api.updateUserRole(input.user_id, { role_id: input.role_id });
|
|
2458
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2459
|
+
return ok({ updated: true });
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
var DeleteAlertDestinationInputShape = {
|
|
2463
|
+
destination_id: z.string().uuid().describe("Destination UUID.")
|
|
2464
|
+
};
|
|
2465
|
+
var deleteAlertDestinationTool = {
|
|
2466
|
+
name: "delete_alert_destination",
|
|
2467
|
+
description: "Remove an alert-notification destination. The org stops receiving pings on this channel immediately; campaign-level overrides referencing it are pruned.",
|
|
2468
|
+
annotations: {
|
|
2469
|
+
title: "Delete Alert Destination",
|
|
2470
|
+
readOnlyHint: false,
|
|
2471
|
+
destructiveHint: true,
|
|
2472
|
+
idempotentHint: true,
|
|
2473
|
+
openWorldHint: false
|
|
2474
|
+
},
|
|
2475
|
+
inputSchema: z.object(DeleteAlertDestinationInputShape),
|
|
2476
|
+
handler: async (input, ctx) => {
|
|
2477
|
+
const result = await ctx.api.deleteAlertDestination(input.destination_id);
|
|
2478
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2479
|
+
return ok({ deleted: true });
|
|
2480
|
+
}
|
|
2481
|
+
};
|
|
2482
|
+
var GetCampaignAlertOverridesInputShape = {
|
|
2483
|
+
campaign_id: z.string().uuid().describe("Campaign UUID.")
|
|
2484
|
+
};
|
|
2485
|
+
var getCampaignAlertOverridesTool = {
|
|
2486
|
+
name: "get_campaign_alert_overrides",
|
|
2487
|
+
description: "Get the per-campaign override of which alert destinations receive its alerts. `mode` is one of `inherit` (use org defaults), `include` (use the listed destinations), or `exclude` (use everything EXCEPT the listed destinations).",
|
|
2488
|
+
annotations: {
|
|
2489
|
+
title: "Get Campaign Alert Overrides",
|
|
2490
|
+
readOnlyHint: true,
|
|
2491
|
+
destructiveHint: false,
|
|
2492
|
+
idempotentHint: true,
|
|
2493
|
+
openWorldHint: false
|
|
2494
|
+
},
|
|
2495
|
+
inputSchema: z.object(GetCampaignAlertOverridesInputShape),
|
|
2496
|
+
handler: async (input, ctx) => {
|
|
2497
|
+
const result = await ctx.api.getCampaignAlertOverrides(input.campaign_id);
|
|
2498
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2499
|
+
return ok(result.value);
|
|
2500
|
+
}
|
|
2501
|
+
};
|
|
2502
|
+
var ListAlertDestinationsInputShape = {};
|
|
2503
|
+
var listAlertDestinationsTool = {
|
|
2504
|
+
name: "list_alert_destinations",
|
|
2505
|
+
description: "List configured alert-notification destinations (channels): Slack workspaces, Telegram chats, email lists, generic webhooks. Each entry has id, kind, display name, version, and creation timestamp.",
|
|
2506
|
+
annotations: {
|
|
2507
|
+
title: "List Alert Destinations",
|
|
2508
|
+
readOnlyHint: true,
|
|
2509
|
+
destructiveHint: false,
|
|
2510
|
+
idempotentHint: true,
|
|
2511
|
+
openWorldHint: false
|
|
2512
|
+
},
|
|
2513
|
+
inputSchema: z.object(ListAlertDestinationsInputShape),
|
|
2514
|
+
handler: async (_input, ctx) => {
|
|
2515
|
+
const result = await ctx.api.listAlertDestinations();
|
|
2516
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2517
|
+
return ok({ items: result.value, total: result.value.length });
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
var SetAlertDestinationVersionInputShape = {
|
|
2521
|
+
destination_id: z.string().uuid().describe("Destination UUID."),
|
|
2522
|
+
version: z.enum(["public", "internal"]).describe(
|
|
2523
|
+
"Which version of the scan-report link to embed in alert messages: `public` (anonymous, no auth) or `internal` (requires UI login)."
|
|
2524
|
+
)
|
|
2525
|
+
};
|
|
2526
|
+
var setAlertDestinationVersionTool = {
|
|
2527
|
+
name: "set_alert_destination_version",
|
|
2528
|
+
description: "Switch a destination to a specific versioned config \u2014 used after re-authorizing a Slack workspace, rotating a Telegram bot token, etc. The new version must already exist in the destination's history. The API returns no body on success; this tool reports `{ updated: true }`. Use `list_alert_destinations` to read the new state if needed.",
|
|
2529
|
+
annotations: {
|
|
2530
|
+
title: "Set Destination Version",
|
|
2531
|
+
readOnlyHint: false,
|
|
2532
|
+
destructiveHint: false,
|
|
2533
|
+
idempotentHint: true,
|
|
2534
|
+
openWorldHint: false
|
|
2535
|
+
},
|
|
2536
|
+
inputSchema: z.object(SetAlertDestinationVersionInputShape),
|
|
2537
|
+
handler: async (input, ctx) => {
|
|
2538
|
+
const result = await ctx.api.setAlertDestinationVersion(input.destination_id, {
|
|
2539
|
+
version: input.version
|
|
2540
|
+
});
|
|
2541
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2542
|
+
return ok({ updated: true });
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
var SetCampaignAlertOverridesInputShape = {
|
|
2546
|
+
campaign_id: z.string().uuid().describe("Campaign UUID."),
|
|
2547
|
+
mode: z.enum(["inherit", "include", "exclude"]).describe(
|
|
2548
|
+
"Routing mode: `inherit` (use org defaults), `include` (route ONLY to listed destinations), `exclude` (route everywhere EXCEPT listed)."
|
|
2549
|
+
),
|
|
2550
|
+
destination_ids: z.array(z.string().uuid()).max(50).default([]).describe(
|
|
2551
|
+
"Destination UUIDs the mode acts on. Required for `include`/`exclude`; ignored for `inherit`."
|
|
2552
|
+
)
|
|
2553
|
+
};
|
|
2554
|
+
var setCampaignAlertOverridesTool = {
|
|
2555
|
+
name: "set_campaign_alert_overrides",
|
|
2556
|
+
description: "REPLACE the per-campaign alert-routing override. `mode=inherit` falls back to org defaults; `mode=include` routes ONLY to the listed destinations; `mode=exclude` routes everywhere EXCEPT the listed destinations. To read the new state, follow up with `get_campaign_alert_overrides`.",
|
|
2557
|
+
annotations: {
|
|
2558
|
+
title: "Set Campaign Alert Overrides",
|
|
2559
|
+
readOnlyHint: false,
|
|
2560
|
+
destructiveHint: false,
|
|
2561
|
+
idempotentHint: true,
|
|
2562
|
+
openWorldHint: false
|
|
2563
|
+
},
|
|
2564
|
+
inputSchema: z.object(SetCampaignAlertOverridesInputShape),
|
|
2565
|
+
handler: async (input, ctx) => {
|
|
2566
|
+
const result = await ctx.api.setCampaignAlertOverrides(input.campaign_id, {
|
|
2567
|
+
mode: input.mode,
|
|
2568
|
+
destination_ids: input.destination_ids
|
|
2569
|
+
});
|
|
2570
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2571
|
+
return ok({ updated: true });
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
var GetAlertStatsInputShape = {};
|
|
2575
|
+
var getAlertStatsTool = {
|
|
2576
|
+
name: "get_alert_stats",
|
|
2577
|
+
description: "Get alert counts grouped by status: open, ack, resolved, ignored, total.",
|
|
2578
|
+
annotations: {
|
|
2579
|
+
title: "Get Alert Stats",
|
|
2580
|
+
readOnlyHint: true,
|
|
2581
|
+
destructiveHint: false,
|
|
2582
|
+
idempotentHint: true,
|
|
2583
|
+
openWorldHint: false
|
|
2584
|
+
},
|
|
2585
|
+
inputSchema: z.object(GetAlertStatsInputShape),
|
|
2586
|
+
handler: async (_input, ctx) => {
|
|
2587
|
+
const result = await ctx.api.getAlertStats();
|
|
2588
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2589
|
+
return ok(result.value);
|
|
2590
|
+
}
|
|
2591
|
+
};
|
|
2592
|
+
var ListAlertsInputShape = {
|
|
2593
|
+
campaign_id: z.string().uuid().optional().describe("Filter to one campaign's alerts."),
|
|
2594
|
+
status: z.enum(["open", "ack", "resolved", "ignored"]).optional().describe("Filter by alert status."),
|
|
2595
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
2596
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
2597
|
+
};
|
|
2598
|
+
var listAlertsTool = {
|
|
2599
|
+
name: "list_alerts",
|
|
2600
|
+
description: "List violation alerts (one per scan + violating-tag combo) with offer URL, tag, country, status, scan back-reference.",
|
|
2601
|
+
annotations: {
|
|
2602
|
+
title: "List Alerts",
|
|
2603
|
+
readOnlyHint: true,
|
|
2604
|
+
destructiveHint: false,
|
|
2605
|
+
idempotentHint: true,
|
|
2606
|
+
openWorldHint: false
|
|
2607
|
+
},
|
|
2608
|
+
inputSchema: z.object(ListAlertsInputShape),
|
|
2609
|
+
handler: async (input, ctx) => {
|
|
2610
|
+
const filters = {
|
|
2611
|
+
page: input.page,
|
|
2612
|
+
limit: input.limit,
|
|
2613
|
+
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
2614
|
+
...input.status !== void 0 ? { status: input.status } : {}
|
|
2615
|
+
};
|
|
2616
|
+
const result = await ctx.api.listAlerts(filters);
|
|
2617
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2618
|
+
return ok(result.value);
|
|
2619
|
+
}
|
|
2620
|
+
};
|
|
2621
|
+
var UpdateAlertStatusInputShape = {
|
|
2622
|
+
alert_id: z.string().uuid().describe("Alert UUID."),
|
|
2623
|
+
status: z.enum(["open", "acknowledged", "resolved", "dismissed"]).describe("New status: open | acknowledged | resolved | dismissed.")
|
|
2624
|
+
};
|
|
2625
|
+
var updateAlertStatusTool = {
|
|
2626
|
+
name: "update_alert_status",
|
|
2627
|
+
description: "Update an alert's status in its lifecycle: open \u2192 acknowledged \u2192 resolved | dismissed. The API enforces valid transitions; an invalid one returns 422.",
|
|
2628
|
+
annotations: {
|
|
2629
|
+
title: "Update Alert Status",
|
|
2630
|
+
readOnlyHint: false,
|
|
2631
|
+
destructiveHint: false,
|
|
2632
|
+
idempotentHint: true,
|
|
2633
|
+
openWorldHint: false
|
|
2634
|
+
},
|
|
2635
|
+
inputSchema: z.object(UpdateAlertStatusInputShape),
|
|
2636
|
+
handler: async (input, ctx) => {
|
|
2637
|
+
const result = await ctx.api.updateAlertStatus(input.alert_id, { status: input.status });
|
|
2638
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2639
|
+
return ok({ updated: true });
|
|
2640
|
+
}
|
|
2641
|
+
};
|
|
2642
|
+
var GetBillingSummaryInputShape = {};
|
|
2643
|
+
var getBillingSummaryTool = {
|
|
2644
|
+
name: "get_billing_summary",
|
|
2645
|
+
description: "Get the organization's billing snapshot: balance (in micros), current plan, period usage counters, suspension state, and whether new scans are accepted right now.",
|
|
2646
|
+
annotations: {
|
|
2647
|
+
title: "Get Billing Summary",
|
|
2648
|
+
readOnlyHint: true,
|
|
2649
|
+
destructiveHint: false,
|
|
2650
|
+
idempotentHint: true,
|
|
2651
|
+
openWorldHint: false
|
|
2652
|
+
},
|
|
2653
|
+
inputSchema: z.object(GetBillingSummaryInputShape),
|
|
2654
|
+
handler: async (_input, ctx) => {
|
|
2655
|
+
const result = await ctx.api.getBillingSummary();
|
|
2656
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2657
|
+
return ok(result.value);
|
|
2658
|
+
}
|
|
2659
|
+
};
|
|
2660
|
+
var GetUsageSummaryInputShape = {};
|
|
2661
|
+
var getUsageSummaryTool = {
|
|
2662
|
+
name: "get_usage_summary",
|
|
2663
|
+
description: "Get a one-liner aggregate of usage for the current billing period: total cost (micros), check count, period start/end.",
|
|
2664
|
+
annotations: {
|
|
2665
|
+
title: "Get Usage Summary",
|
|
2666
|
+
readOnlyHint: true,
|
|
2667
|
+
destructiveHint: false,
|
|
2668
|
+
idempotentHint: true,
|
|
2669
|
+
openWorldHint: false
|
|
2670
|
+
},
|
|
2671
|
+
inputSchema: z.object(GetUsageSummaryInputShape),
|
|
2672
|
+
handler: async (_input, ctx) => {
|
|
2673
|
+
const result = await ctx.api.getUsageSummary();
|
|
2674
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2675
|
+
return ok(result.value);
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
var ListBalanceHistoryInputShape = {
|
|
2679
|
+
date_from: z.string().date().optional().describe("ISO date, inclusive."),
|
|
2680
|
+
date_to: z.string().date().optional().describe("ISO date, inclusive."),
|
|
2681
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
2682
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
2683
|
+
};
|
|
2684
|
+
var listBalanceHistoryTool = {
|
|
2685
|
+
name: "list_balance_history",
|
|
2686
|
+
description: "List ledger transactions (charges, refunds, top-ups, invoice settlements) on the organization's balance. Each row: type, amount in micros, description, timestamp.",
|
|
2687
|
+
annotations: {
|
|
2688
|
+
title: "List Balance History",
|
|
2689
|
+
readOnlyHint: true,
|
|
2690
|
+
destructiveHint: false,
|
|
2691
|
+
idempotentHint: true,
|
|
2692
|
+
openWorldHint: false
|
|
2693
|
+
},
|
|
2694
|
+
inputSchema: z.object(ListBalanceHistoryInputShape),
|
|
2695
|
+
handler: async (input, ctx) => {
|
|
2696
|
+
const filters = {
|
|
2697
|
+
page: input.page,
|
|
2698
|
+
limit: input.limit,
|
|
2699
|
+
...input.date_from !== void 0 ? { date_from: input.date_from } : {},
|
|
2700
|
+
...input.date_to !== void 0 ? { date_to: input.date_to } : {}
|
|
2701
|
+
};
|
|
2702
|
+
const result = await ctx.api.listBalanceHistory(filters);
|
|
2703
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2704
|
+
return ok(result.value);
|
|
2705
|
+
}
|
|
2706
|
+
};
|
|
2707
|
+
var ListUsageInputShape = {
|
|
2708
|
+
date_from: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive."),
|
|
2709
|
+
date_to: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive."),
|
|
2710
|
+
scan_id: z.string().uuid().optional().describe("Filter to one scan's cost rows."),
|
|
2711
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
2712
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
2713
|
+
};
|
|
2714
|
+
var listUsageTool = {
|
|
2715
|
+
name: "list_usage",
|
|
2716
|
+
description: "List per-scan usage rows (cost in micros, kind, scan id, timestamp). Use to attribute cost to specific scans or campaigns.",
|
|
2717
|
+
annotations: {
|
|
2718
|
+
title: "List Usage",
|
|
2719
|
+
readOnlyHint: true,
|
|
2720
|
+
destructiveHint: false,
|
|
2721
|
+
idempotentHint: true,
|
|
2722
|
+
openWorldHint: false
|
|
2723
|
+
},
|
|
2724
|
+
inputSchema: z.object(ListUsageInputShape),
|
|
2725
|
+
handler: async (input, ctx) => {
|
|
2726
|
+
const filters = {
|
|
2727
|
+
page: input.page,
|
|
2728
|
+
limit: input.limit,
|
|
2729
|
+
...input.date_from !== void 0 ? { date_from: input.date_from } : {},
|
|
2730
|
+
...input.date_to !== void 0 ? { date_to: input.date_to } : {},
|
|
2731
|
+
...input.scan_id !== void 0 ? { scan_id: input.scan_id } : {}
|
|
2732
|
+
};
|
|
2733
|
+
const result = await ctx.api.listUsage(filters);
|
|
2734
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2735
|
+
return ok(result.value);
|
|
2736
|
+
}
|
|
2737
|
+
};
|
|
2738
|
+
var ArchiveCampaignGroupInputShape = {
|
|
2739
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2740
|
+
};
|
|
2741
|
+
var archiveCampaignGroupTool = {
|
|
2742
|
+
name: "archive_campaign_group",
|
|
2743
|
+
description: "Soft-delete the group AND every campaign in it. The default group cannot be archived; ask the user to move campaigns out first.",
|
|
2744
|
+
annotations: {
|
|
2745
|
+
title: "Archive Campaign Group",
|
|
2746
|
+
readOnlyHint: false,
|
|
2747
|
+
destructiveHint: false,
|
|
2748
|
+
idempotentHint: true,
|
|
2749
|
+
openWorldHint: false
|
|
2750
|
+
},
|
|
2751
|
+
inputSchema: z.object(ArchiveCampaignGroupInputShape),
|
|
2752
|
+
handler: async (input, ctx) => {
|
|
2753
|
+
const result = await ctx.api.archiveCampaignGroup(input.group_id);
|
|
2754
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2755
|
+
return ok(result.value);
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
var CancelCampaignGroupInputShape = {
|
|
2759
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2760
|
+
};
|
|
2761
|
+
var cancelCampaignGroupTool = {
|
|
2762
|
+
name: "cancel_campaign_group",
|
|
2763
|
+
description: "Cancel every pending scan across every campaign in the group. Refunds credits for cancelled scans. Returns the total cancelled count.",
|
|
2764
|
+
annotations: {
|
|
2765
|
+
title: "Cancel Campaign Group",
|
|
2766
|
+
readOnlyHint: false,
|
|
2767
|
+
destructiveHint: false,
|
|
2768
|
+
idempotentHint: true,
|
|
2769
|
+
openWorldHint: false
|
|
2770
|
+
},
|
|
2771
|
+
inputSchema: z.object(CancelCampaignGroupInputShape),
|
|
2772
|
+
handler: async (input, ctx) => {
|
|
2773
|
+
const result = await ctx.api.cancelCampaignGroup(input.group_id);
|
|
2774
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2775
|
+
return ok(result.value);
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
var CreateCampaignGroupInputShape = {
|
|
2779
|
+
name: z.string().min(1).max(200).describe("Display name (1-200 chars).")
|
|
2780
|
+
};
|
|
2781
|
+
var createCampaignGroupTool = {
|
|
2782
|
+
name: "create_campaign_group",
|
|
2783
|
+
description: "Create a new campaign group (folder). Free operation, no scans queued.",
|
|
2784
|
+
annotations: {
|
|
2785
|
+
title: "Create Campaign Group",
|
|
2786
|
+
readOnlyHint: false,
|
|
2787
|
+
destructiveHint: false,
|
|
2788
|
+
idempotentHint: false,
|
|
2789
|
+
openWorldHint: false
|
|
2790
|
+
},
|
|
2791
|
+
inputSchema: z.object(CreateCampaignGroupInputShape),
|
|
2792
|
+
handler: async (input, ctx) => {
|
|
2793
|
+
const result = await ctx.api.createCampaignGroup({ name: input.name });
|
|
2794
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2795
|
+
return ok(result.value);
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2798
|
+
var GetCampaignGroupInputShape = {
|
|
2799
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2800
|
+
};
|
|
2801
|
+
var getCampaignGroupTool = {
|
|
2802
|
+
name: "get_campaign_group",
|
|
2803
|
+
description: "Get one campaign group by UUID with default/archive/pause flags and campaign count.",
|
|
2804
|
+
annotations: {
|
|
2805
|
+
title: "Get Campaign Group",
|
|
2806
|
+
readOnlyHint: true,
|
|
2807
|
+
destructiveHint: false,
|
|
2808
|
+
idempotentHint: true,
|
|
2809
|
+
openWorldHint: false
|
|
2810
|
+
},
|
|
2811
|
+
inputSchema: z.object(GetCampaignGroupInputShape),
|
|
2812
|
+
handler: async (input, ctx) => {
|
|
2813
|
+
const result = await ctx.api.getCampaignGroup(input.group_id);
|
|
2814
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2815
|
+
return ok(result.value);
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
var ListCampaignGroupsInputShape = {
|
|
2819
|
+
archived: z.boolean().optional().describe("If true, list ONLY archived groups. Default: only active groups.")
|
|
2820
|
+
};
|
|
2821
|
+
var listCampaignGroupsTool = {
|
|
2822
|
+
name: "list_campaign_groups",
|
|
2823
|
+
description: "List campaign groups \u2014 folders that group related campaigns. Includes per-group campaign count. Not paginated; the org-scoped list is typically small (a few dozen groups max).",
|
|
2824
|
+
annotations: {
|
|
2825
|
+
title: "List Campaign Groups",
|
|
2826
|
+
readOnlyHint: true,
|
|
2827
|
+
destructiveHint: false,
|
|
2828
|
+
idempotentHint: true,
|
|
2829
|
+
openWorldHint: false
|
|
2830
|
+
},
|
|
2831
|
+
inputSchema: z.object(ListCampaignGroupsInputShape),
|
|
2832
|
+
handler: async (input, ctx) => {
|
|
2833
|
+
const filters = input.archived === void 0 ? {} : { archived: input.archived };
|
|
2834
|
+
const result = await ctx.api.listCampaignGroups(filters);
|
|
2835
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2836
|
+
return ok({ items: result.value });
|
|
2837
|
+
}
|
|
2838
|
+
};
|
|
2839
|
+
var PauseCampaignGroupScheduleInputShape = {
|
|
2840
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2841
|
+
};
|
|
2842
|
+
var pauseCampaignGroupScheduleTool = {
|
|
2843
|
+
name: "pause_campaign_group_schedule",
|
|
2844
|
+
description: "Pause the scheduler for EVERY campaign in the group. Already-pending scans complete; no new scheduled runs are produced until you `resume_campaign_group_schedule`.",
|
|
2845
|
+
annotations: {
|
|
2846
|
+
title: "Pause Group Schedule",
|
|
2847
|
+
readOnlyHint: false,
|
|
2848
|
+
destructiveHint: false,
|
|
2849
|
+
idempotentHint: true,
|
|
2850
|
+
openWorldHint: false
|
|
2851
|
+
},
|
|
2852
|
+
inputSchema: z.object(PauseCampaignGroupScheduleInputShape),
|
|
2853
|
+
handler: async (input, ctx) => {
|
|
2854
|
+
const result = await ctx.api.pauseCampaignGroupSchedule(input.group_id);
|
|
2855
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2856
|
+
return ok(result.value);
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
var ResumeCampaignGroupScheduleInputShape = {
|
|
2860
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2861
|
+
};
|
|
2862
|
+
var resumeCampaignGroupScheduleTool = {
|
|
2863
|
+
name: "resume_campaign_group_schedule",
|
|
2864
|
+
description: "Re-enable the scheduler for every campaign in the group. Inverse of `pause_campaign_group_schedule`.",
|
|
2865
|
+
annotations: {
|
|
2866
|
+
title: "Resume Group Schedule",
|
|
2867
|
+
readOnlyHint: false,
|
|
2868
|
+
destructiveHint: false,
|
|
2869
|
+
idempotentHint: true,
|
|
2870
|
+
openWorldHint: false
|
|
2871
|
+
},
|
|
2872
|
+
inputSchema: z.object(ResumeCampaignGroupScheduleInputShape),
|
|
2873
|
+
handler: async (input, ctx) => {
|
|
2874
|
+
const result = await ctx.api.resumeCampaignGroupSchedule(input.group_id);
|
|
2875
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2876
|
+
return ok(result.value);
|
|
2877
|
+
}
|
|
2878
|
+
};
|
|
2879
|
+
var RunCampaignGroupInputShape = {
|
|
2880
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2881
|
+
};
|
|
2882
|
+
var runCampaignGroupTool = {
|
|
2883
|
+
name: "run_campaign_group",
|
|
2884
|
+
description: "Fire an immediate run of every active (non-archived, non-paused) campaign in the group. Returns aggregate stats: how many campaigns triggered, the per-campaign run UUIDs, and any per-campaign failures.",
|
|
2885
|
+
annotations: {
|
|
2886
|
+
title: "Run Campaign Group",
|
|
2887
|
+
readOnlyHint: false,
|
|
2888
|
+
destructiveHint: false,
|
|
2889
|
+
idempotentHint: false,
|
|
2890
|
+
openWorldHint: false
|
|
2891
|
+
},
|
|
2892
|
+
inputSchema: z.object(RunCampaignGroupInputShape),
|
|
2893
|
+
handler: async (input, ctx) => {
|
|
2894
|
+
const result = await ctx.api.runCampaignGroup(input.group_id);
|
|
2895
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2896
|
+
return ok(result.value);
|
|
2897
|
+
}
|
|
2898
|
+
};
|
|
2899
|
+
var UnarchiveCampaignGroupInputShape = {
|
|
2900
|
+
group_id: z.string().uuid().describe("Campaign group UUID.")
|
|
2901
|
+
};
|
|
2902
|
+
var unarchiveCampaignGroupTool = {
|
|
2903
|
+
name: "unarchive_campaign_group",
|
|
2904
|
+
description: "Restore an archived campaign group and re-expose its campaigns in default lists.",
|
|
2905
|
+
annotations: {
|
|
2906
|
+
title: "Unarchive Campaign Group",
|
|
2907
|
+
readOnlyHint: false,
|
|
2908
|
+
destructiveHint: false,
|
|
2909
|
+
idempotentHint: true,
|
|
2910
|
+
openWorldHint: false
|
|
2911
|
+
},
|
|
2912
|
+
inputSchema: z.object(UnarchiveCampaignGroupInputShape),
|
|
2913
|
+
handler: async (input, ctx) => {
|
|
2914
|
+
const result = await ctx.api.unarchiveCampaignGroup(input.group_id);
|
|
2915
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2916
|
+
return ok(result.value);
|
|
2917
|
+
}
|
|
2918
|
+
};
|
|
2919
|
+
var UpdateCampaignGroupInputShape = {
|
|
2920
|
+
group_id: z.string().uuid().describe("Group UUID to update."),
|
|
2921
|
+
name: z.string().min(1).max(200).optional().describe("New display name."),
|
|
2922
|
+
schedule_paused: z.boolean().optional().describe("Pause/resume the scheduler for every campaign in this group.")
|
|
2923
|
+
};
|
|
2924
|
+
var updateCampaignGroupTool = {
|
|
2925
|
+
name: "update_campaign_group",
|
|
2926
|
+
description: "Update a campaign group: rename, or pause/resume the scheduler (the pause cascades to every campaign in the group).",
|
|
2927
|
+
annotations: {
|
|
2928
|
+
title: "Update Campaign Group",
|
|
2929
|
+
readOnlyHint: false,
|
|
2930
|
+
destructiveHint: false,
|
|
2931
|
+
idempotentHint: true,
|
|
2932
|
+
openWorldHint: false
|
|
2933
|
+
},
|
|
2934
|
+
inputSchema: z.object(UpdateCampaignGroupInputShape),
|
|
2935
|
+
handler: async (input, ctx) => {
|
|
2936
|
+
const body = {
|
|
2937
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
2938
|
+
...input.schedule_paused !== void 0 ? { schedule_paused: input.schedule_paused } : {}
|
|
2939
|
+
};
|
|
2940
|
+
const result = await ctx.api.updateCampaignGroup(input.group_id, body);
|
|
2941
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2942
|
+
return ok(result.value);
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2945
|
+
var ArchiveCampaignInputShape = {
|
|
2946
|
+
campaign_id: z.string().uuid().describe("Campaign UUID to archive.")
|
|
2947
|
+
};
|
|
2948
|
+
var archiveCampaignTool = {
|
|
2949
|
+
name: "archive_campaign",
|
|
2950
|
+
description: "Soft-delete (archive) a campaign. Removes it from default lists and stops the scheduler; previously-collected scans are preserved.",
|
|
2951
|
+
annotations: {
|
|
2952
|
+
title: "Archive Campaign",
|
|
2953
|
+
readOnlyHint: false,
|
|
2954
|
+
destructiveHint: false,
|
|
2955
|
+
idempotentHint: true,
|
|
2956
|
+
openWorldHint: false
|
|
2957
|
+
},
|
|
2958
|
+
inputSchema: z.object(ArchiveCampaignInputShape),
|
|
2959
|
+
handler: async (input, ctx) => {
|
|
2960
|
+
const result = await ctx.api.archiveCampaign(input.campaign_id);
|
|
2961
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2962
|
+
return ok(result.value);
|
|
2963
|
+
}
|
|
2964
|
+
};
|
|
2965
|
+
var CancelCampaignInputShape = {
|
|
2966
|
+
campaign_id: z.string().uuid().describe("Campaign UUID.")
|
|
2967
|
+
};
|
|
2968
|
+
var cancelCampaignTool = {
|
|
2969
|
+
name: "cancel_campaign",
|
|
2970
|
+
description: "Cancel every pending scan across all unfinished runs of a campaign. Running scans complete normally; pending scans are marked cancelled and credits refunded. Returns count of cancelled scans.",
|
|
2971
|
+
annotations: {
|
|
2972
|
+
title: "Cancel Campaign",
|
|
2973
|
+
readOnlyHint: false,
|
|
2974
|
+
destructiveHint: false,
|
|
2975
|
+
idempotentHint: true,
|
|
2976
|
+
openWorldHint: false
|
|
2977
|
+
},
|
|
2978
|
+
inputSchema: z.object(CancelCampaignInputShape),
|
|
2979
|
+
handler: async (input, ctx) => {
|
|
2980
|
+
const result = await ctx.api.cancelCampaign(input.campaign_id);
|
|
2981
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
2982
|
+
return ok(result.value);
|
|
2983
|
+
}
|
|
2984
|
+
};
|
|
2985
|
+
var CreateCampaignInputShape = {
|
|
2986
|
+
name: z.string().min(1).max(200).describe("Display name (1-200 chars)."),
|
|
2987
|
+
campaign_type: z.enum(["url", "ad_tag"]).describe("`url` or `ad_tag` \u2014 must match the target field below."),
|
|
2988
|
+
url: z.string().url().optional().describe("Target URL (required if campaign_type=url)."),
|
|
2989
|
+
ad_tag: z.string().optional().describe("Ad-tag HTML/JS (required if campaign_type=ad_tag)."),
|
|
2990
|
+
country_codes: z.array(z.string().length(2)).min(1).describe("ISO 3166-1 alpha-2 codes \u2014 one scan per country per run."),
|
|
2991
|
+
group_id: z.string().uuid().optional().describe("Parent group UUID; defaults to the org's default group."),
|
|
2992
|
+
emulator_categories: z.array(z.string()).optional().describe("Categories of device profiles to rotate through. Default: all available."),
|
|
2993
|
+
labels: z.record(z.string()).optional().describe("Arbitrary metadata applied to every queued scan."),
|
|
2994
|
+
policy_set_id: z.string().uuid().optional().describe("Policy set to evaluate every scan against."),
|
|
2995
|
+
schedule_enabled: z.boolean().optional().describe("If true, the scheduler runs immediately. Default: false (manual run).")
|
|
2996
|
+
};
|
|
2997
|
+
var createCampaignTool = {
|
|
2998
|
+
name: "create_campaign",
|
|
2999
|
+
description: "Create a recurring scan campaign (template). The schedule produces N scans per run where N = number of countries times number of device profiles. Scans cost credits when they run, not when the campaign is created.",
|
|
3000
|
+
annotations: {
|
|
3001
|
+
title: "Create Campaign",
|
|
3002
|
+
readOnlyHint: false,
|
|
3003
|
+
destructiveHint: false,
|
|
3004
|
+
idempotentHint: false,
|
|
3005
|
+
openWorldHint: false
|
|
3006
|
+
},
|
|
3007
|
+
inputSchema: z.object(CreateCampaignInputShape),
|
|
3008
|
+
handler: async (input, ctx) => {
|
|
3009
|
+
const body = {
|
|
3010
|
+
name: input.name,
|
|
3011
|
+
campaign_type: input.campaign_type,
|
|
3012
|
+
country_codes: input.country_codes,
|
|
3013
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
3014
|
+
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3015
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {},
|
|
3016
|
+
...input.emulator_categories !== void 0 ? { emulator_categories: input.emulator_categories } : {},
|
|
3017
|
+
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3018
|
+
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
3019
|
+
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
|
|
3020
|
+
};
|
|
3021
|
+
const result = await ctx.api.createCampaign(body);
|
|
3022
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3023
|
+
return ok(result.value);
|
|
3024
|
+
}
|
|
3025
|
+
};
|
|
3026
|
+
var GetCampaignInputShape = {
|
|
3027
|
+
campaign_id: z.string().uuid().describe("Campaign UUID.")
|
|
3028
|
+
};
|
|
3029
|
+
var getCampaignTool = {
|
|
3030
|
+
name: "get_campaign",
|
|
3031
|
+
description: "Get one campaign by UUID: name, type, target URL/ad-tag, countries, schedule status, archive status, parent group.",
|
|
3032
|
+
annotations: {
|
|
3033
|
+
title: "Get Campaign",
|
|
3034
|
+
readOnlyHint: true,
|
|
3035
|
+
destructiveHint: false,
|
|
3036
|
+
idempotentHint: true,
|
|
3037
|
+
openWorldHint: false
|
|
3038
|
+
},
|
|
3039
|
+
inputSchema: z.object(GetCampaignInputShape),
|
|
3040
|
+
handler: async (input, ctx) => {
|
|
3041
|
+
const result = await ctx.api.getCampaign(input.campaign_id);
|
|
3042
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3043
|
+
return ok(result.value);
|
|
3044
|
+
}
|
|
3045
|
+
};
|
|
3046
|
+
var ListCampaignRunsInputShape = {
|
|
3047
|
+
campaign_id: z.string().uuid().describe("Campaign UUID."),
|
|
3048
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3049
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3050
|
+
};
|
|
3051
|
+
var listCampaignRunsTool = {
|
|
3052
|
+
name: "list_campaign_runs",
|
|
3053
|
+
description: "List every run (scheduled execution) of one campaign, paginated, with per-run counters.",
|
|
3054
|
+
annotations: {
|
|
3055
|
+
title: "List Campaign Runs",
|
|
3056
|
+
readOnlyHint: true,
|
|
3057
|
+
destructiveHint: false,
|
|
3058
|
+
idempotentHint: true,
|
|
3059
|
+
openWorldHint: false
|
|
3060
|
+
},
|
|
3061
|
+
inputSchema: z.object(ListCampaignRunsInputShape),
|
|
3062
|
+
handler: async (input, ctx) => {
|
|
3063
|
+
const result = await ctx.api.listCampaignRuns(input.campaign_id, {
|
|
3064
|
+
page: input.page,
|
|
3065
|
+
limit: input.limit
|
|
3066
|
+
});
|
|
3067
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3068
|
+
return ok(result.value);
|
|
3069
|
+
}
|
|
3070
|
+
};
|
|
3071
|
+
var ListCampaignsInputShape = {
|
|
3072
|
+
group_id: z.string().uuid().optional().describe("Optional campaign-group UUID to filter by."),
|
|
3073
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3074
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200).")
|
|
3075
|
+
};
|
|
3076
|
+
var listCampaignsTool = {
|
|
3077
|
+
name: "list_campaigns",
|
|
3078
|
+
description: "List campaigns for the caller's organization, optionally filtered by group, paginated.",
|
|
3079
|
+
annotations: {
|
|
3080
|
+
title: "List Campaigns",
|
|
3081
|
+
readOnlyHint: true,
|
|
3082
|
+
destructiveHint: false,
|
|
3083
|
+
idempotentHint: true,
|
|
3084
|
+
openWorldHint: false
|
|
3085
|
+
},
|
|
3086
|
+
inputSchema: z.object(ListCampaignsInputShape),
|
|
3087
|
+
handler: async (input, ctx) => {
|
|
3088
|
+
const filters = {
|
|
3089
|
+
page: input.page,
|
|
3090
|
+
limit: input.limit,
|
|
3091
|
+
...input.group_id !== void 0 ? { group_id: input.group_id } : {}
|
|
3092
|
+
};
|
|
3093
|
+
const result = await ctx.api.listCampaigns(filters);
|
|
3094
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3095
|
+
return ok(result.value);
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
var ListCampaignsPickerInputShape = {};
|
|
3099
|
+
var listCampaignsPickerTool = {
|
|
3100
|
+
name: "list_campaigns_picker",
|
|
3101
|
+
description: "Slim per-row campaign list for selection UIs \u2014 id, name, group_id, is_archived. Cheaper than `list_campaigns` for orgs with thousands of campaigns. Use `get_campaign(id)` after a selection to fetch full details.",
|
|
3102
|
+
annotations: {
|
|
3103
|
+
title: "List Campaigns (Picker)",
|
|
3104
|
+
readOnlyHint: true,
|
|
3105
|
+
destructiveHint: false,
|
|
3106
|
+
idempotentHint: true,
|
|
3107
|
+
openWorldHint: false
|
|
3108
|
+
},
|
|
3109
|
+
inputSchema: z.object(ListCampaignsPickerInputShape),
|
|
3110
|
+
handler: async (_input, ctx) => {
|
|
3111
|
+
const result = await ctx.api.listCampaignsPicker();
|
|
3112
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3113
|
+
return ok(result.value);
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
var RunCampaignInputShape = {
|
|
3117
|
+
campaign_id: z.string().uuid().describe("Campaign UUID to run now.")
|
|
3118
|
+
};
|
|
3119
|
+
var runCampaignTool = {
|
|
3120
|
+
name: "run_campaign",
|
|
3121
|
+
description: "Trigger an immediate, ad-hoc run of a campaign. Costs N credits where N = number of countries \xD7 number of emulators in the campaign config. Returns the new run with progress counters (total / completed / failed / partial / cancelled); track further progress via `get_run`.",
|
|
3122
|
+
annotations: {
|
|
3123
|
+
title: "Run Campaign Now",
|
|
3124
|
+
readOnlyHint: false,
|
|
3125
|
+
destructiveHint: false,
|
|
3126
|
+
idempotentHint: false,
|
|
3127
|
+
openWorldHint: false
|
|
3128
|
+
},
|
|
3129
|
+
inputSchema: z.object(RunCampaignInputShape),
|
|
3130
|
+
handler: async (input, ctx) => {
|
|
3131
|
+
const result = await ctx.api.runCampaign(input.campaign_id);
|
|
3132
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3133
|
+
return ok(result.value);
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
var UnarchiveCampaignInputShape = {
|
|
3137
|
+
campaign_id: z.string().uuid().describe("Campaign UUID.")
|
|
3138
|
+
};
|
|
3139
|
+
var unarchiveCampaignTool = {
|
|
3140
|
+
name: "unarchive_campaign",
|
|
3141
|
+
description: "Restore an archived campaign. Inverse of `archive_campaign`. The campaign re-appears in default lists; if `schedule_enabled` was true, the scheduler resumes producing runs.",
|
|
3142
|
+
annotations: {
|
|
3143
|
+
title: "Unarchive Campaign",
|
|
3144
|
+
readOnlyHint: false,
|
|
3145
|
+
destructiveHint: false,
|
|
3146
|
+
idempotentHint: true,
|
|
3147
|
+
openWorldHint: false
|
|
3148
|
+
},
|
|
3149
|
+
inputSchema: z.object(UnarchiveCampaignInputShape),
|
|
3150
|
+
handler: async (input, ctx) => {
|
|
3151
|
+
const result = await ctx.api.unarchiveCampaign(input.campaign_id);
|
|
3152
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3153
|
+
return ok(result.value);
|
|
3154
|
+
}
|
|
3155
|
+
};
|
|
3156
|
+
var UpdateCampaignInputShape = {
|
|
3157
|
+
campaign_id: z.string().uuid().describe("Campaign UUID to update."),
|
|
3158
|
+
name: z.string().min(1).max(200).optional().describe("New display name."),
|
|
3159
|
+
country_codes: z.array(z.string().length(2)).optional().describe("Replace the country list."),
|
|
3160
|
+
labels: z.record(z.string()).optional().describe("Replace the label map."),
|
|
3161
|
+
policy_set_id: z.string().uuid().nullable().optional().describe("New policy set UUID; pass null to clear."),
|
|
3162
|
+
schedule_enabled: z.boolean().optional().describe("Pause / resume the scheduler.")
|
|
3163
|
+
};
|
|
3164
|
+
var updateCampaignTool = {
|
|
3165
|
+
name: "update_campaign",
|
|
3166
|
+
description: "Update one or more fields of a campaign. Fields not supplied are left unchanged. `policy_set_id` accepts null to clear the binding.",
|
|
3167
|
+
annotations: {
|
|
3168
|
+
title: "Update Campaign",
|
|
3169
|
+
readOnlyHint: false,
|
|
3170
|
+
destructiveHint: false,
|
|
3171
|
+
idempotentHint: true,
|
|
3172
|
+
openWorldHint: false
|
|
3173
|
+
},
|
|
3174
|
+
inputSchema: z.object(UpdateCampaignInputShape),
|
|
3175
|
+
handler: async (input, ctx) => {
|
|
3176
|
+
const body = {
|
|
3177
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
3178
|
+
...input.country_codes !== void 0 ? { country_codes: input.country_codes } : {},
|
|
3179
|
+
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3180
|
+
...input.policy_set_id !== void 0 ? { policy_set_id: input.policy_set_id } : {},
|
|
3181
|
+
...input.schedule_enabled !== void 0 ? { schedule_enabled: input.schedule_enabled } : {}
|
|
3182
|
+
};
|
|
3183
|
+
const result = await ctx.api.updateCampaign(input.campaign_id, body);
|
|
3184
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3185
|
+
return ok(result.value);
|
|
3186
|
+
}
|
|
3187
|
+
};
|
|
3188
|
+
var CreateCustomRuleInputShape = {
|
|
3189
|
+
name: z.string().min(1).max(200).describe("Display name."),
|
|
3190
|
+
tag_slug: z.string().max(100).optional().describe("Tag slug to assign on match. Empty = create-only (advanced)."),
|
|
3191
|
+
rule_type: z.string().max(50).describe("Rule engine: regex | substring | iab_category | etc. (API validates)."),
|
|
3192
|
+
config: z.record(z.unknown()).describe("Rule-type-specific configuration object. Shape depends on rule_type."),
|
|
3193
|
+
target: z.string().max(30).optional().describe(
|
|
3194
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
|
|
3195
|
+
)
|
|
3196
|
+
};
|
|
3197
|
+
var createCustomRuleTool = {
|
|
3198
|
+
name: "create_custom_rule",
|
|
3199
|
+
description: "Define a custom tag-detection rule (regex / substring / category). Matches will tag every future scan; existing scans are untouched until you call `recheck_scans`.",
|
|
3200
|
+
annotations: {
|
|
3201
|
+
title: "Create Custom Rule",
|
|
3202
|
+
readOnlyHint: false,
|
|
3203
|
+
destructiveHint: false,
|
|
3204
|
+
idempotentHint: false,
|
|
3205
|
+
openWorldHint: false
|
|
3206
|
+
},
|
|
3207
|
+
inputSchema: z.object(CreateCustomRuleInputShape),
|
|
3208
|
+
handler: async (input, ctx) => {
|
|
3209
|
+
const body = {
|
|
3210
|
+
name: input.name,
|
|
3211
|
+
rule_type: input.rule_type,
|
|
3212
|
+
config: input.config
|
|
3213
|
+
};
|
|
3214
|
+
if (input.tag_slug !== void 0) body.tag_slug = input.tag_slug;
|
|
3215
|
+
if (input.target !== void 0) body.target = input.target;
|
|
3216
|
+
const result = await ctx.api.createCustomRule(body);
|
|
3217
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3218
|
+
return ok(result.value);
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3221
|
+
var DeleteCustomRuleInputShape = {
|
|
3222
|
+
rule_id: z.string().uuid().describe("Rule UUID to delete.")
|
|
3223
|
+
};
|
|
3224
|
+
var deleteCustomRuleTool = {
|
|
3225
|
+
name: "delete_custom_rule",
|
|
3226
|
+
description: "Permanently delete a custom rule. Already-applied tags on past scans are preserved; the rule simply stops running on future scans.",
|
|
3227
|
+
annotations: {
|
|
3228
|
+
title: "Delete Custom Rule",
|
|
3229
|
+
readOnlyHint: false,
|
|
3230
|
+
destructiveHint: true,
|
|
3231
|
+
idempotentHint: true,
|
|
3232
|
+
openWorldHint: false
|
|
3233
|
+
},
|
|
3234
|
+
inputSchema: z.object(DeleteCustomRuleInputShape),
|
|
3235
|
+
handler: async (input, ctx) => {
|
|
3236
|
+
const result = await ctx.api.deleteCustomRule(input.rule_id);
|
|
3237
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3238
|
+
return ok({ deleted: true });
|
|
3239
|
+
}
|
|
3240
|
+
};
|
|
3241
|
+
var GetCustomRuleInputShape = { rule_id: z.string().uuid().describe("Rule UUID.") };
|
|
3242
|
+
var getCustomRuleTool = {
|
|
3243
|
+
name: "get_custom_rule",
|
|
3244
|
+
description: "Get one custom rule by UUID with name, tag-slug, type, config object, target, active flag.",
|
|
3245
|
+
annotations: {
|
|
3246
|
+
title: "Get Custom Rule",
|
|
3247
|
+
readOnlyHint: true,
|
|
3248
|
+
destructiveHint: false,
|
|
3249
|
+
idempotentHint: true,
|
|
3250
|
+
openWorldHint: false
|
|
3251
|
+
},
|
|
3252
|
+
inputSchema: z.object(GetCustomRuleInputShape),
|
|
3253
|
+
handler: async (input, ctx) => {
|
|
3254
|
+
const result = await ctx.api.getCustomRule(input.rule_id);
|
|
3255
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3256
|
+
return ok(result.value);
|
|
3257
|
+
}
|
|
3258
|
+
};
|
|
3259
|
+
var ListCustomRulesInputShape = {
|
|
3260
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3261
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3262
|
+
};
|
|
3263
|
+
var listCustomRulesTool = {
|
|
3264
|
+
name: "list_custom_rules",
|
|
3265
|
+
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.",
|
|
3266
|
+
annotations: {
|
|
3267
|
+
title: "List Custom Rules",
|
|
3268
|
+
readOnlyHint: true,
|
|
3269
|
+
destructiveHint: false,
|
|
3270
|
+
idempotentHint: true,
|
|
3271
|
+
openWorldHint: false
|
|
3272
|
+
},
|
|
3273
|
+
inputSchema: z.object(ListCustomRulesInputShape),
|
|
3274
|
+
handler: async (input, ctx) => {
|
|
3275
|
+
const result = await ctx.api.listCustomRules({ page: input.page, limit: input.limit });
|
|
3276
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3277
|
+
return ok(result.value);
|
|
3278
|
+
}
|
|
3279
|
+
};
|
|
3280
|
+
var TestCustomRuleInputShape = {
|
|
3281
|
+
rule_type: z.string().max(50).describe("Rule engine type (regex, substring, ...)."),
|
|
3282
|
+
config: z.record(z.unknown()).describe("Rule-type-specific config to test."),
|
|
3283
|
+
target: z.string().max(30).describe(
|
|
3284
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
3285
|
+
),
|
|
3286
|
+
scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
|
|
3287
|
+
};
|
|
3288
|
+
var testCustomRuleTool = {
|
|
3289
|
+
name: "test_custom_rule",
|
|
3290
|
+
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`.",
|
|
3291
|
+
annotations: {
|
|
3292
|
+
title: "Test Custom Rule",
|
|
3293
|
+
readOnlyHint: true,
|
|
3294
|
+
destructiveHint: false,
|
|
3295
|
+
idempotentHint: true,
|
|
3296
|
+
openWorldHint: false
|
|
3297
|
+
},
|
|
3298
|
+
inputSchema: z.object(TestCustomRuleInputShape),
|
|
3299
|
+
handler: async (input, ctx) => {
|
|
3300
|
+
const result = await ctx.api.testCustomRule({
|
|
3301
|
+
rule_type: input.rule_type,
|
|
3302
|
+
config: input.config,
|
|
3303
|
+
target: input.target,
|
|
3304
|
+
scan_id: input.scan_id
|
|
3305
|
+
});
|
|
3306
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3307
|
+
return ok(result.value);
|
|
3308
|
+
}
|
|
3309
|
+
};
|
|
3310
|
+
var UpdateCustomRuleInputShape = {
|
|
3311
|
+
rule_id: z.string().uuid().describe("Rule UUID to update."),
|
|
3312
|
+
name: z.string().min(1).max(200).optional().describe("New display name."),
|
|
3313
|
+
tag_slug: z.string().max(100).optional().describe("New tag slug to assign on match."),
|
|
3314
|
+
config: z.record(z.unknown()).optional().describe("New rule-type-specific config object."),
|
|
3315
|
+
target: z.string().max(30).optional().describe(
|
|
3316
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
3317
|
+
),
|
|
3318
|
+
is_active: z.boolean().optional().describe("Enable/disable the rule.")
|
|
3319
|
+
};
|
|
3320
|
+
var updateCustomRuleTool = {
|
|
3321
|
+
name: "update_custom_rule",
|
|
3322
|
+
description: "Update a custom tag-detection rule. Only supplied fields are sent. 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.)",
|
|
3323
|
+
annotations: {
|
|
3324
|
+
title: "Update Custom Rule",
|
|
3325
|
+
readOnlyHint: false,
|
|
3326
|
+
destructiveHint: false,
|
|
3327
|
+
idempotentHint: true,
|
|
3328
|
+
openWorldHint: false
|
|
3329
|
+
},
|
|
3330
|
+
inputSchema: z.object(UpdateCustomRuleInputShape),
|
|
3331
|
+
handler: async (input, ctx) => {
|
|
3332
|
+
const body = {};
|
|
3333
|
+
if (input.name !== void 0) body.name = input.name;
|
|
3334
|
+
if (input.tag_slug !== void 0) body.tag_slug = input.tag_slug;
|
|
3335
|
+
if (input.config !== void 0) body.config = input.config;
|
|
3336
|
+
if (input.target !== void 0) body.target = input.target;
|
|
3337
|
+
if (input.is_active !== void 0) body.is_active = input.is_active;
|
|
3338
|
+
const result = await ctx.api.updateCustomRule(input.rule_id, body);
|
|
3339
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3340
|
+
return ok(result.value);
|
|
3341
|
+
}
|
|
3342
|
+
};
|
|
3343
|
+
var ListEmulatorsInputShape = {};
|
|
3344
|
+
var listEmulatorsTool = {
|
|
3345
|
+
name: "list_emulators",
|
|
3346
|
+
description: "List every device/OS emulator profile available for scans (id, display name, category, browser). Use the `id` as `emulator_id` in `create_scan` / `create_campaign`.",
|
|
3347
|
+
annotations: {
|
|
3348
|
+
title: "List Emulators",
|
|
3349
|
+
readOnlyHint: true,
|
|
3350
|
+
destructiveHint: false,
|
|
3351
|
+
idempotentHint: true,
|
|
3352
|
+
openWorldHint: false
|
|
3353
|
+
},
|
|
3354
|
+
inputSchema: z.object(ListEmulatorsInputShape),
|
|
3355
|
+
handler: async (_input, ctx) => {
|
|
3356
|
+
const result = await ctx.api.listEmulators();
|
|
3357
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3358
|
+
return ok({ items: result.value, total: result.value.length });
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
var ListGeosInputShape = {};
|
|
3362
|
+
var listGeosTool = {
|
|
3363
|
+
name: "list_geos",
|
|
3364
|
+
description: "List every country the Kaminari Ad platform can scan ads from, with ISO 3166-1 alpha-2 code, name, continent, and emoji.",
|
|
3365
|
+
annotations: {
|
|
3366
|
+
title: "List Geos",
|
|
3367
|
+
readOnlyHint: true,
|
|
3368
|
+
destructiveHint: false,
|
|
3369
|
+
idempotentHint: true,
|
|
3370
|
+
openWorldHint: false
|
|
3371
|
+
},
|
|
3372
|
+
inputSchema: z.object(ListGeosInputShape),
|
|
3373
|
+
handler: async (_input, ctx) => {
|
|
3374
|
+
const result = await ctx.api.listGeos();
|
|
3375
|
+
if (result.isErr()) {
|
|
3376
|
+
return err(mapApiError(result.error));
|
|
3377
|
+
}
|
|
3378
|
+
return ok({ items: result.value, total: result.value.length });
|
|
3379
|
+
}
|
|
3380
|
+
};
|
|
3381
|
+
var ListInvoicesInputShape = {
|
|
3382
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3383
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3384
|
+
};
|
|
3385
|
+
var listInvoicesTool = {
|
|
3386
|
+
name: "list_invoices",
|
|
3387
|
+
description: "List invoices issued to the organization with number, status, total in micros, currency, due/paid dates. Use to find an invoice id; PDFs are available at https://app.kaminari.ad/api/v1/invoices/{id}/pdf (not exposed via MCP \u2014 agents return the URL to the user).",
|
|
3388
|
+
annotations: {
|
|
3389
|
+
title: "List Invoices",
|
|
3390
|
+
readOnlyHint: true,
|
|
3391
|
+
destructiveHint: false,
|
|
3392
|
+
idempotentHint: true,
|
|
3393
|
+
openWorldHint: false
|
|
3394
|
+
},
|
|
3395
|
+
inputSchema: z.object(ListInvoicesInputShape),
|
|
3396
|
+
handler: async (input, ctx) => {
|
|
3397
|
+
const result = await ctx.api.listInvoices({ page: input.page, limit: input.limit });
|
|
3398
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3399
|
+
return ok(result.value);
|
|
3400
|
+
}
|
|
3401
|
+
};
|
|
3402
|
+
var PolicyEntryShape = z.object({
|
|
3403
|
+
tag_slug: z.string().min(1).max(100).describe("Tag slug that triggers a violation."),
|
|
3404
|
+
country_codes: z.array(z.string().length(2)).max(50).describe("Restrict the violation to these countries. Empty array = all countries.")
|
|
3405
|
+
});
|
|
3406
|
+
var CreatePolicySetInputShape = {
|
|
3407
|
+
name: z.string().min(1).max(200).describe("Display name."),
|
|
3408
|
+
description: z.string().max(2e3).describe("Free-form description (use empty string for none)."),
|
|
3409
|
+
entries: z.array(PolicyEntryShape).min(1).max(500).describe("At least one entry. Each entry pairs a tag-slug with country codes.")
|
|
3410
|
+
};
|
|
3411
|
+
var createPolicySetTool = {
|
|
3412
|
+
name: "create_policy_set",
|
|
3413
|
+
description: "Create a new policy set (named collection of tag + country-list entries). Once created, you can bind campaigns to it via `update_campaign`.",
|
|
3414
|
+
annotations: {
|
|
3415
|
+
title: "Create Policy Set",
|
|
3416
|
+
readOnlyHint: false,
|
|
3417
|
+
destructiveHint: false,
|
|
3418
|
+
idempotentHint: false,
|
|
3419
|
+
openWorldHint: false
|
|
3420
|
+
},
|
|
3421
|
+
inputSchema: z.object(CreatePolicySetInputShape),
|
|
3422
|
+
handler: async (input, ctx) => {
|
|
3423
|
+
const result = await ctx.api.createPolicySet({
|
|
3424
|
+
name: input.name,
|
|
3425
|
+
description: input.description,
|
|
3426
|
+
entries: input.entries.map((e) => ({ tag_slug: e.tag_slug, country_codes: e.country_codes }))
|
|
3427
|
+
});
|
|
3428
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3429
|
+
return ok(result.value);
|
|
3430
|
+
}
|
|
3431
|
+
};
|
|
3432
|
+
var DeletePolicySetInputShape = {
|
|
3433
|
+
policy_set_id: z.string().uuid().describe("Policy set UUID.")
|
|
3434
|
+
};
|
|
3435
|
+
var deletePolicySetTool = {
|
|
3436
|
+
name: "delete_policy_set",
|
|
3437
|
+
description: "Permanently delete a policy set. IMPORTANT: API returns HTTP 400 if any active campaign is still bound to this set. To unbind first, call `list_campaigns` with a `policy_set_id` filter (when available) or scan your campaigns for matches, then `update_campaign` for each match setting `policy_set_id=null`, then retry delete. Alerts created under this set persist (their `policy_set_id` becomes `null`).",
|
|
3438
|
+
annotations: {
|
|
3439
|
+
title: "Delete Policy Set",
|
|
3440
|
+
readOnlyHint: false,
|
|
3441
|
+
destructiveHint: true,
|
|
3442
|
+
idempotentHint: true,
|
|
3443
|
+
openWorldHint: false
|
|
3444
|
+
},
|
|
3445
|
+
inputSchema: z.object(DeletePolicySetInputShape),
|
|
3446
|
+
handler: async (input, ctx) => {
|
|
3447
|
+
const result = await ctx.api.deletePolicySet(input.policy_set_id);
|
|
3448
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3449
|
+
return ok({ deleted: true });
|
|
3450
|
+
}
|
|
3451
|
+
};
|
|
3452
|
+
var GetPolicySetInputShape = {
|
|
3453
|
+
policy_set_id: z.string().uuid().describe("Policy set UUID.")
|
|
3454
|
+
};
|
|
3455
|
+
var getPolicySetTool = {
|
|
3456
|
+
name: "get_policy_set",
|
|
3457
|
+
description: "Get one policy set by UUID with its complete list of entries (tag-slug + applicable country codes).",
|
|
3458
|
+
annotations: {
|
|
3459
|
+
title: "Get Policy Set",
|
|
3460
|
+
readOnlyHint: true,
|
|
3461
|
+
destructiveHint: false,
|
|
3462
|
+
idempotentHint: true,
|
|
3463
|
+
openWorldHint: false
|
|
3464
|
+
},
|
|
3465
|
+
inputSchema: z.object(GetPolicySetInputShape),
|
|
3466
|
+
handler: async (input, ctx) => {
|
|
3467
|
+
const result = await ctx.api.getPolicySet(input.policy_set_id);
|
|
3468
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3469
|
+
return ok(result.value);
|
|
3470
|
+
}
|
|
3471
|
+
};
|
|
3472
|
+
var ListPolicySetsInputShape = {
|
|
3473
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3474
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3475
|
+
};
|
|
3476
|
+
var listPolicySetsTool = {
|
|
3477
|
+
name: "list_policy_sets",
|
|
3478
|
+
description: "Paginated list of policy sets: named collections of (tag, country-list) entries that define what counts as a violation. Campaigns bind to one policy set. Returns `{items, total, page, limit}`. List items omit `entries` for payload size \u2014 fetch a single set via `get_policy_set` when you need them.",
|
|
3479
|
+
annotations: {
|
|
3480
|
+
title: "List Policy Sets",
|
|
3481
|
+
readOnlyHint: true,
|
|
3482
|
+
destructiveHint: false,
|
|
3483
|
+
idempotentHint: true,
|
|
3484
|
+
openWorldHint: false
|
|
3485
|
+
},
|
|
3486
|
+
inputSchema: z.object(ListPolicySetsInputShape),
|
|
3487
|
+
handler: async (input, ctx) => {
|
|
3488
|
+
const result = await ctx.api.listPolicySets({ page: input.page, limit: input.limit });
|
|
3489
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3490
|
+
return ok(result.value);
|
|
3491
|
+
}
|
|
3492
|
+
};
|
|
3493
|
+
var RequestPolicySetApprovalInputShape = {
|
|
3494
|
+
policy_set_id: z.string().uuid().describe("Policy set UUID.")
|
|
3495
|
+
};
|
|
3496
|
+
var requestPolicySetApprovalTool = {
|
|
3497
|
+
name: "request_policy_set_approval",
|
|
3498
|
+
description: "Submit a private policy set for Kaminari Ad team review so it can be marked PUBLIC and used by other organizations. The set must be complete and well-formed. Returns immediately; approval status is reflected on the policy set entity once the review completes.",
|
|
3499
|
+
annotations: {
|
|
3500
|
+
title: "Request Policy Set Approval",
|
|
3501
|
+
readOnlyHint: false,
|
|
3502
|
+
destructiveHint: false,
|
|
3503
|
+
idempotentHint: false,
|
|
3504
|
+
openWorldHint: false
|
|
3505
|
+
},
|
|
3506
|
+
inputSchema: z.object(RequestPolicySetApprovalInputShape),
|
|
3507
|
+
handler: async (input, ctx) => {
|
|
3508
|
+
const result = await ctx.api.requestPolicySetApproval(input.policy_set_id);
|
|
3509
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3510
|
+
return ok({ requested: true });
|
|
3511
|
+
}
|
|
3512
|
+
};
|
|
3513
|
+
var PolicyEntryShape2 = z.object({
|
|
3514
|
+
tag_slug: z.string().min(1).max(100),
|
|
3515
|
+
country_codes: z.array(z.string().length(2)).max(50)
|
|
3516
|
+
});
|
|
3517
|
+
var UpdatePolicySetInputShape = {
|
|
3518
|
+
policy_set_id: z.string().uuid().describe("Policy set UUID."),
|
|
3519
|
+
name: z.string().min(1).max(200).describe("New name (always required by the API on update)."),
|
|
3520
|
+
description: z.string().max(2e3).describe("New description (empty string allowed)."),
|
|
3521
|
+
entries: z.array(PolicyEntryShape2).min(1).max(500).describe("REPLACEMENT entries list \u2014 REPLACES the current list, not a merge.")
|
|
3522
|
+
};
|
|
3523
|
+
var updatePolicySetTool = {
|
|
3524
|
+
name: "update_policy_set",
|
|
3525
|
+
description: "REPLACE a policy set's name, description, and entry list. The API requires all three fields on every update \u2014 read the current set with `get_policy_set` first if you only want to change one thing.",
|
|
3526
|
+
annotations: {
|
|
3527
|
+
title: "Update Policy Set",
|
|
3528
|
+
readOnlyHint: false,
|
|
3529
|
+
destructiveHint: false,
|
|
3530
|
+
idempotentHint: true,
|
|
3531
|
+
openWorldHint: false
|
|
3532
|
+
},
|
|
3533
|
+
inputSchema: z.object(UpdatePolicySetInputShape),
|
|
3534
|
+
handler: async (input, ctx) => {
|
|
3535
|
+
const result = await ctx.api.updatePolicySet(input.policy_set_id, {
|
|
3536
|
+
name: input.name,
|
|
3537
|
+
description: input.description,
|
|
3538
|
+
entries: input.entries.map((e) => ({ tag_slug: e.tag_slug, country_codes: e.country_codes }))
|
|
3539
|
+
});
|
|
3540
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3541
|
+
return ok(result.value);
|
|
3542
|
+
}
|
|
3543
|
+
};
|
|
3544
|
+
var CancelRunInputShape = { run_id: z.string().uuid().describe("Run UUID.") };
|
|
3545
|
+
var cancelRunTool = {
|
|
3546
|
+
name: "cancel_run",
|
|
3547
|
+
description: "Cancel every pending scan within one run. Running scans complete; pending ones get refunded. Returns the count of cancelled scans.",
|
|
3548
|
+
annotations: {
|
|
3549
|
+
title: "Cancel Run",
|
|
3550
|
+
readOnlyHint: false,
|
|
3551
|
+
destructiveHint: false,
|
|
3552
|
+
idempotentHint: true,
|
|
3553
|
+
openWorldHint: false
|
|
3554
|
+
},
|
|
3555
|
+
inputSchema: z.object(CancelRunInputShape),
|
|
3556
|
+
handler: async (input, ctx) => {
|
|
3557
|
+
const result = await ctx.api.cancelRun(input.run_id);
|
|
3558
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3559
|
+
return ok(result.value);
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
var GetRunInputShape = { run_id: z.string().uuid().describe("Run UUID.") };
|
|
3563
|
+
var getRunTool = {
|
|
3564
|
+
name: "get_run",
|
|
3565
|
+
description: "Get one run by UUID with totals (queued, completed, failed, partial, cancelled), parent campaign, label, source.",
|
|
3566
|
+
annotations: {
|
|
3567
|
+
title: "Get Run",
|
|
3568
|
+
readOnlyHint: true,
|
|
3569
|
+
destructiveHint: false,
|
|
3570
|
+
idempotentHint: true,
|
|
3571
|
+
openWorldHint: false
|
|
3572
|
+
},
|
|
3573
|
+
inputSchema: z.object(GetRunInputShape),
|
|
3574
|
+
handler: async (input, ctx) => {
|
|
3575
|
+
const result = await ctx.api.getRun(input.run_id);
|
|
3576
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3577
|
+
return ok(result.value);
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
var ListRunScansInputShape = {
|
|
3581
|
+
run_id: z.string().uuid().describe("Run UUID."),
|
|
3582
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3583
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3584
|
+
};
|
|
3585
|
+
var listRunScansTool = {
|
|
3586
|
+
name: "list_run_scans",
|
|
3587
|
+
description: "List the tile-scan items produced by one run (status, country, offer URL, screenshot, elapsed ms, error). For full scan details (input URL, labels, classification, redirect chain) fetch a specific scan via `get_scan`.",
|
|
3588
|
+
annotations: {
|
|
3589
|
+
title: "List Run Scans",
|
|
3590
|
+
readOnlyHint: true,
|
|
3591
|
+
destructiveHint: false,
|
|
3592
|
+
idempotentHint: true,
|
|
3593
|
+
openWorldHint: false
|
|
3594
|
+
},
|
|
3595
|
+
inputSchema: z.object(ListRunScansInputShape),
|
|
3596
|
+
handler: async (input, ctx) => {
|
|
3597
|
+
const result = await ctx.api.listRunScans(input.run_id, {
|
|
3598
|
+
page: input.page,
|
|
3599
|
+
limit: input.limit
|
|
3600
|
+
});
|
|
3601
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3602
|
+
return ok(result.value);
|
|
3603
|
+
}
|
|
3604
|
+
};
|
|
3605
|
+
var CancelScanInputShape = {
|
|
3606
|
+
scan_id: z.string().uuid().describe("UUID of the pending scan to cancel.")
|
|
3607
|
+
};
|
|
3608
|
+
var cancelScanTool = {
|
|
3609
|
+
name: "cancel_scan",
|
|
3610
|
+
description: "Cancel one pending scan by UUID. Already-running or completed scans are no-ops. Cancellation refunds the scan credit.",
|
|
3611
|
+
annotations: {
|
|
3612
|
+
title: "Cancel Scan",
|
|
3613
|
+
readOnlyHint: false,
|
|
3614
|
+
destructiveHint: false,
|
|
3615
|
+
idempotentHint: true,
|
|
3616
|
+
openWorldHint: false
|
|
3617
|
+
},
|
|
3618
|
+
inputSchema: z.object(CancelScanInputShape),
|
|
3619
|
+
handler: async (input, ctx) => {
|
|
3620
|
+
const result = await ctx.api.cancelScan(input.scan_id);
|
|
3621
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3622
|
+
return ok(result.value);
|
|
3623
|
+
}
|
|
3624
|
+
};
|
|
3625
|
+
var CreateBulkScansInputShape = {
|
|
3626
|
+
url: z.string().url().optional().describe("Direct URL. EITHER `url` OR `ad_tag` is required."),
|
|
3627
|
+
ad_tag: z.string().optional().describe("Raw ad-tag HTML/JS. EITHER `url` OR `ad_tag` is required."),
|
|
3628
|
+
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."),
|
|
3629
|
+
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; same for every country in the batch."),
|
|
3630
|
+
labels: z.record(z.string()).optional().describe("Arbitrary metadata copied onto every created scan.")
|
|
3631
|
+
};
|
|
3632
|
+
var createBulkScansTool = {
|
|
3633
|
+
name: "create_bulk_scans",
|
|
3634
|
+
description: "Queue one new scan per country in a single call (e.g. test the same URL from US + DE + JP). COSTS N CREDITS where N = number of countries. Returns the list of created scans.",
|
|
3635
|
+
annotations: {
|
|
3636
|
+
title: "Create Bulk Scans",
|
|
3637
|
+
readOnlyHint: false,
|
|
3638
|
+
destructiveHint: false,
|
|
3639
|
+
idempotentHint: false,
|
|
3640
|
+
openWorldHint: false
|
|
3641
|
+
},
|
|
3642
|
+
inputSchema: z.object(CreateBulkScansInputShape),
|
|
3643
|
+
handler: async (input, ctx) => {
|
|
3644
|
+
const body = {
|
|
3645
|
+
country_codes: input.country_codes,
|
|
3646
|
+
emulator_id: input.emulator_id,
|
|
3647
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
3648
|
+
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3649
|
+
...input.labels !== void 0 ? { labels: input.labels } : {}
|
|
3650
|
+
};
|
|
3651
|
+
const result = await ctx.api.createBulkScans(body);
|
|
3652
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3653
|
+
return ok({ items: result.value, total: result.value.length });
|
|
3654
|
+
}
|
|
3655
|
+
};
|
|
3656
|
+
var CreateScanInputShape = {
|
|
3657
|
+
url: z.string().url().optional().describe("Direct URL of the ad / landing page. EITHER `url` OR `ad_tag` is required."),
|
|
3658
|
+
ad_tag: z.string().optional().describe("Raw HTML/JS ad tag (script, iframe, image). EITHER `url` OR `ad_tag` is required."),
|
|
3659
|
+
country_code: z.string().length(2).describe("ISO 3166-1 alpha-2 country code, e.g. US, DE, JP. Determines proxy geo."),
|
|
3660
|
+
emulator_id: z.string().min(1).max(100).describe("Device/OS profile slug; use `list_emulators` to discover valid values."),
|
|
3661
|
+
labels: z.record(z.string()).optional().describe("Arbitrary string -> string metadata attached to the scan."),
|
|
3662
|
+
campaign_id: z.string().uuid().optional().describe("Optional campaign UUID to attribute the scan to."),
|
|
3663
|
+
run_id: z.string().uuid().optional().describe("Optional run UUID inside the campaign.")
|
|
3664
|
+
};
|
|
3665
|
+
var createScanTool = {
|
|
3666
|
+
name: "create_scan",
|
|
3667
|
+
description: "Queue a single new scan for a URL or ad-tag against one country. COSTS CREDITS and bills the caller's organization. Returns the newly-created scan record.",
|
|
3668
|
+
annotations: {
|
|
3669
|
+
title: "Create Scan",
|
|
3670
|
+
readOnlyHint: false,
|
|
3671
|
+
destructiveHint: false,
|
|
3672
|
+
idempotentHint: false,
|
|
3673
|
+
openWorldHint: false
|
|
3674
|
+
},
|
|
3675
|
+
inputSchema: z.object(CreateScanInputShape),
|
|
3676
|
+
handler: async (input, ctx) => {
|
|
3677
|
+
const body = {
|
|
3678
|
+
country_code: input.country_code,
|
|
3679
|
+
emulator_id: input.emulator_id,
|
|
3680
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
3681
|
+
...input.ad_tag !== void 0 ? { ad_tag: input.ad_tag } : {},
|
|
3682
|
+
...input.labels !== void 0 ? { labels: input.labels } : {},
|
|
3683
|
+
...input.campaign_id !== void 0 ? { campaign_id: input.campaign_id } : {},
|
|
3684
|
+
...input.run_id !== void 0 ? { run_id: input.run_id } : {}
|
|
3685
|
+
};
|
|
3686
|
+
const result = await ctx.api.createScan(body);
|
|
3687
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3688
|
+
return ok(result.value);
|
|
3689
|
+
}
|
|
3690
|
+
};
|
|
3691
|
+
var GetScanInputShape = {
|
|
3692
|
+
scan_id: z.string().uuid().describe("The scan's UUID (returned by `list_scans` or `create_scan`).")
|
|
3693
|
+
};
|
|
3694
|
+
var getScanTool = {
|
|
3695
|
+
name: "get_scan",
|
|
3696
|
+
description: "Get full detail for one scan by UUID: status, offer URL, screenshot URL, timing, labels, and the parent campaign if any.",
|
|
3697
|
+
annotations: {
|
|
3698
|
+
title: "Get Scan",
|
|
3699
|
+
readOnlyHint: true,
|
|
3700
|
+
destructiveHint: false,
|
|
3701
|
+
idempotentHint: true,
|
|
3702
|
+
openWorldHint: false
|
|
3703
|
+
},
|
|
3704
|
+
inputSchema: z.object(GetScanInputShape),
|
|
3705
|
+
handler: async (input, ctx) => {
|
|
3706
|
+
const result = await ctx.api.getScan(input.scan_id);
|
|
3707
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3708
|
+
return ok(result.value);
|
|
3709
|
+
}
|
|
3710
|
+
};
|
|
3711
|
+
var ListScansInputShape = {
|
|
3712
|
+
status: z.string().optional().describe(
|
|
3713
|
+
"Comma-separated statuses to filter by. One of: pending, running, done, failed, cancelled."
|
|
3714
|
+
),
|
|
3715
|
+
country_code: z.string().optional().describe("Comma-separated ISO 3166-1 alpha-2 country codes, e.g. US,DE,JP."),
|
|
3716
|
+
url: z.string().optional().describe("Substring match against the scanned URL."),
|
|
3717
|
+
scan_id: z.string().optional().describe("Comma-separated scan UUIDs to fetch a specific set."),
|
|
3718
|
+
date_from: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive lower bound on scan creation."),
|
|
3719
|
+
date_to: z.string().date().optional().describe("ISO date (YYYY-MM-DD), inclusive upper bound on scan creation."),
|
|
3720
|
+
tag: z.string().optional().describe("Comma-separated tag slugs to filter by."),
|
|
3721
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page number."),
|
|
3722
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size (1-200). Default 50.")
|
|
3723
|
+
};
|
|
3724
|
+
var listScansTool = {
|
|
3725
|
+
name: "list_scans",
|
|
3726
|
+
description: "List scans for the caller's organization with optional filters (status, country, URL substring, date range, tags). Returns a paginated envelope.",
|
|
3727
|
+
annotations: {
|
|
3728
|
+
title: "List Scans",
|
|
3729
|
+
readOnlyHint: true,
|
|
3730
|
+
destructiveHint: false,
|
|
3731
|
+
idempotentHint: true,
|
|
3732
|
+
openWorldHint: false
|
|
3733
|
+
},
|
|
3734
|
+
inputSchema: z.object(ListScansInputShape),
|
|
3735
|
+
handler: async (input, ctx) => {
|
|
3736
|
+
const filters = {
|
|
3737
|
+
page: input.page,
|
|
3738
|
+
limit: input.limit,
|
|
3739
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
3740
|
+
...input.country_code !== void 0 ? { country_code: input.country_code } : {},
|
|
3741
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
3742
|
+
...input.scan_id !== void 0 ? { scan_id: input.scan_id } : {},
|
|
3743
|
+
...input.date_from !== void 0 ? { date_from: input.date_from } : {},
|
|
3744
|
+
...input.date_to !== void 0 ? { date_to: input.date_to } : {},
|
|
3745
|
+
...input.tag !== void 0 ? { tag: input.tag } : {}
|
|
3746
|
+
};
|
|
3747
|
+
const result = await ctx.api.listScans(filters);
|
|
3748
|
+
if (result.isErr()) {
|
|
3749
|
+
return err(mapApiError(result.error));
|
|
3750
|
+
}
|
|
3751
|
+
return ok(result.value);
|
|
3752
|
+
}
|
|
3753
|
+
};
|
|
3754
|
+
var RecheckScansInputShape = {
|
|
3755
|
+
scope_type: z.enum(["last_n", "hours"]).describe(
|
|
3756
|
+
"Selection mode. `last_n` = most recent N scans; `hours` = scans from the past N hours."
|
|
3757
|
+
),
|
|
3758
|
+
scope_value: z.number().int().min(1).max(5e3).describe(
|
|
3759
|
+
"Number of scans (`last_n`, max 5000) OR number of hours (`hours`, max 72). API enforces tighter caps per mode."
|
|
3760
|
+
)
|
|
3761
|
+
};
|
|
3762
|
+
var recheckScansTool = {
|
|
3763
|
+
name: "recheck_scans",
|
|
3764
|
+
description: "Re-run the checker pipeline against recent COMPLETED scans (e.g. after updating policies or custom rules). Returns the number of scans queued for re-evaluation. No new crawl fee \u2014 only the checker cost.",
|
|
3765
|
+
annotations: {
|
|
3766
|
+
title: "Recheck Scans",
|
|
3767
|
+
readOnlyHint: false,
|
|
3768
|
+
destructiveHint: false,
|
|
3769
|
+
idempotentHint: false,
|
|
3770
|
+
openWorldHint: false
|
|
3771
|
+
},
|
|
3772
|
+
inputSchema: z.object(RecheckScansInputShape),
|
|
3773
|
+
handler: async (input, ctx) => {
|
|
3774
|
+
const result = await ctx.api.recheckScans({
|
|
3775
|
+
scope_type: input.scope_type,
|
|
3776
|
+
scope_value: input.scope_value
|
|
3777
|
+
});
|
|
3778
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3779
|
+
return ok(result.value);
|
|
3780
|
+
}
|
|
3781
|
+
};
|
|
3782
|
+
var DeleteTagDefinitionInputShape = {
|
|
3783
|
+
slug: z.string().min(1).max(100).describe("Custom tag slug to delete.")
|
|
3784
|
+
};
|
|
3785
|
+
var deleteTagDefinitionTool = {
|
|
3786
|
+
name: "delete_tag_definition",
|
|
3787
|
+
description: "Delete a CUSTOM tag definition. Historical tag assignments are preserved; future scans will not receive this tag. System tags cannot be deleted.",
|
|
3788
|
+
annotations: {
|
|
3789
|
+
title: "Delete Tag Definition",
|
|
3790
|
+
readOnlyHint: false,
|
|
3791
|
+
destructiveHint: true,
|
|
3792
|
+
idempotentHint: true,
|
|
3793
|
+
openWorldHint: false
|
|
3794
|
+
},
|
|
3795
|
+
inputSchema: z.object(DeleteTagDefinitionInputShape),
|
|
3796
|
+
handler: async (input, ctx) => {
|
|
3797
|
+
const result = await ctx.api.deleteTagDefinition(input.slug);
|
|
3798
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3799
|
+
return ok({ deleted: true });
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3802
|
+
var GetTagDefinitionInputShape = {
|
|
3803
|
+
slug: z.string().min(1).max(100).describe("Tag slug (e.g. `malware`, `redirect_chain_too_long`).")
|
|
3804
|
+
};
|
|
3805
|
+
var getTagDefinitionTool = {
|
|
3806
|
+
name: "get_tag_definition",
|
|
3807
|
+
description: "Get full definition of one tag: display name, description, severity, category, source (system vs custom), public-report visibility, usage counts, plus `linked_rules` \u2014 the custom rules currently producing this tag (id, name, active flag). Fetch a specific rule's full config via `get_custom_rule`.",
|
|
3808
|
+
annotations: {
|
|
3809
|
+
title: "Get Tag Definition",
|
|
3810
|
+
readOnlyHint: true,
|
|
3811
|
+
destructiveHint: false,
|
|
3812
|
+
idempotentHint: true,
|
|
3813
|
+
openWorldHint: false
|
|
3814
|
+
},
|
|
3815
|
+
inputSchema: z.object(GetTagDefinitionInputShape),
|
|
3816
|
+
handler: async (input, ctx) => {
|
|
3817
|
+
const result = await ctx.api.getTagDefinition(input.slug);
|
|
3818
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3819
|
+
return ok(result.value);
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
var ListScanTagsInputShape = {
|
|
3823
|
+
scan_id: z.string().uuid().describe("Scan UUID.")
|
|
3824
|
+
};
|
|
3825
|
+
var listScanTagsTool = {
|
|
3826
|
+
name: "list_scan_tags",
|
|
3827
|
+
description: "List every tag (system + custom) attached to one scan by the checker pipeline, with display name, category, and severity.",
|
|
3828
|
+
annotations: {
|
|
3829
|
+
title: "List Scan Tags",
|
|
3830
|
+
readOnlyHint: true,
|
|
3831
|
+
destructiveHint: false,
|
|
3832
|
+
idempotentHint: true,
|
|
3833
|
+
openWorldHint: false
|
|
3834
|
+
},
|
|
3835
|
+
inputSchema: z.object(ListScanTagsInputShape),
|
|
3836
|
+
handler: async (input, ctx) => {
|
|
3837
|
+
const result = await ctx.api.listScanTags(input.scan_id);
|
|
3838
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3839
|
+
return ok({ items: result.value, total: result.value.length });
|
|
3840
|
+
}
|
|
3841
|
+
};
|
|
3842
|
+
var ListTagsInputShape = {};
|
|
3843
|
+
var listTagsTool = {
|
|
3844
|
+
name: "list_tags",
|
|
3845
|
+
description: "List every tag definition the platform knows (system tags + organization custom tags) with category, severity, and usage counters (scans + rules per tag).",
|
|
3846
|
+
annotations: {
|
|
3847
|
+
title: "List Tags",
|
|
3848
|
+
readOnlyHint: true,
|
|
3849
|
+
destructiveHint: false,
|
|
3850
|
+
idempotentHint: true,
|
|
3851
|
+
openWorldHint: false
|
|
3852
|
+
},
|
|
3853
|
+
inputSchema: z.object(ListTagsInputShape),
|
|
3854
|
+
handler: async (_input, ctx) => {
|
|
3855
|
+
const result = await ctx.api.listTags();
|
|
3856
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3857
|
+
return ok({ items: result.value, total: result.value.length });
|
|
3858
|
+
}
|
|
3859
|
+
};
|
|
3860
|
+
var UpdateTagDefinitionInputShape = {
|
|
3861
|
+
slug: z.string().min(1).max(100).describe("Tag slug."),
|
|
3862
|
+
display_name: z.string().min(1).max(200).optional().describe("New human-readable name."),
|
|
3863
|
+
description: z.string().max(2e3).optional().describe("New description."),
|
|
3864
|
+
severity: z.enum(["high", "medium", "low"]).optional().describe("New severity level."),
|
|
3865
|
+
show_in_public_report: z.boolean().optional().describe("Whether the tag appears in the public scan-report view.")
|
|
3866
|
+
};
|
|
3867
|
+
var updateTagDefinitionTool = {
|
|
3868
|
+
name: "update_tag_definition",
|
|
3869
|
+
description: "Update display fields of a CUSTOM tag (system tags are read-only). Only supplied fields are touched. To read the updated definition, follow up with `get_tag_definition`.",
|
|
3870
|
+
annotations: {
|
|
3871
|
+
title: "Update Tag Definition",
|
|
3872
|
+
readOnlyHint: false,
|
|
3873
|
+
destructiveHint: false,
|
|
3874
|
+
idempotentHint: true,
|
|
3875
|
+
openWorldHint: false
|
|
3876
|
+
},
|
|
3877
|
+
inputSchema: z.object(UpdateTagDefinitionInputShape),
|
|
3878
|
+
handler: async (input, ctx) => {
|
|
3879
|
+
const body = {};
|
|
3880
|
+
if (input.display_name !== void 0) body.display_name = input.display_name;
|
|
3881
|
+
if (input.description !== void 0) body.description = input.description;
|
|
3882
|
+
if (input.severity !== void 0) body.severity = input.severity;
|
|
3883
|
+
if (input.show_in_public_report !== void 0) {
|
|
3884
|
+
body.show_in_public_report = input.show_in_public_report;
|
|
3885
|
+
}
|
|
3886
|
+
const result = await ctx.api.updateTagDefinition(input.slug, body);
|
|
3887
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3888
|
+
return ok({ updated: true });
|
|
3889
|
+
}
|
|
3890
|
+
};
|
|
3891
|
+
var BulkReplayWebhookInputShape = {
|
|
3892
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
|
|
3893
|
+
from_ts: z.string().datetime().describe("ISO-8601 lower bound (inclusive) of the time window to replay."),
|
|
3894
|
+
to_ts: z.string().datetime().describe("ISO-8601 upper bound (exclusive) of the time window to replay.")
|
|
3895
|
+
};
|
|
3896
|
+
var bulkReplayWebhookTool = {
|
|
3897
|
+
name: "bulk_replay_webhook",
|
|
3898
|
+
description: "Replay every delivery attempt for this webhook endpoint that landed in [from_ts, to_ts). Returns `{ replayed, skipped }` counts. Use to recover after a downstream outage \u2014 every event in the window is re-fired.",
|
|
3899
|
+
annotations: {
|
|
3900
|
+
title: "Bulk Replay Webhook",
|
|
3901
|
+
readOnlyHint: false,
|
|
3902
|
+
destructiveHint: false,
|
|
3903
|
+
idempotentHint: false,
|
|
3904
|
+
openWorldHint: false
|
|
3905
|
+
},
|
|
3906
|
+
inputSchema: z.object(BulkReplayWebhookInputShape),
|
|
3907
|
+
handler: async (input, ctx) => {
|
|
3908
|
+
const result = await ctx.api.bulkReplayWebhook(input.webhook_id, {
|
|
3909
|
+
from_ts: input.from_ts,
|
|
3910
|
+
to_ts: input.to_ts
|
|
3911
|
+
});
|
|
3912
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3913
|
+
return ok(result.value);
|
|
3914
|
+
}
|
|
3915
|
+
};
|
|
3916
|
+
var CreateWebhookInputShape = {
|
|
3917
|
+
url: z.string().url().describe("HTTPS endpoint URL."),
|
|
3918
|
+
description: z.string().max(200).default("").describe("Free-form label shown in the dashboard."),
|
|
3919
|
+
event_types: z.array(z.string()).min(0).max(50).default([]).describe(
|
|
3920
|
+
"Event-type slugs to subscribe to (see `list_webhook_event_types`). Empty array = subscribe to ALL events."
|
|
3921
|
+
),
|
|
3922
|
+
campaign_ids: z.array(z.string().uuid()).max(50).default([]).describe("Restrict to events from these campaigns. Empty array = events from every campaign.")
|
|
3923
|
+
};
|
|
3924
|
+
var createWebhookTool = {
|
|
3925
|
+
name: "create_webhook",
|
|
3926
|
+
description: "Register a webhook endpoint for the chosen event types. Response is a `{ webhook, secret }` envelope \u2014 the HMAC-SHA256 SIGNING SECRET is returned once and the caller MUST store it to verify event signatures.",
|
|
3927
|
+
annotations: {
|
|
3928
|
+
title: "Create Webhook",
|
|
3929
|
+
readOnlyHint: false,
|
|
3930
|
+
destructiveHint: false,
|
|
3931
|
+
idempotentHint: false,
|
|
3932
|
+
openWorldHint: false
|
|
3933
|
+
},
|
|
3934
|
+
inputSchema: z.object(CreateWebhookInputShape),
|
|
3935
|
+
handler: async (input, ctx) => {
|
|
3936
|
+
const result = await ctx.api.createWebhook({
|
|
3937
|
+
url: input.url,
|
|
3938
|
+
description: input.description,
|
|
3939
|
+
event_types: input.event_types,
|
|
3940
|
+
campaign_ids: input.campaign_ids
|
|
3941
|
+
});
|
|
3942
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3943
|
+
return ok(result.value);
|
|
3944
|
+
}
|
|
3945
|
+
};
|
|
3946
|
+
var DeleteWebhookInputShape = {
|
|
3947
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID to remove.")
|
|
3948
|
+
};
|
|
3949
|
+
var deleteWebhookTool = {
|
|
3950
|
+
name: "delete_webhook",
|
|
3951
|
+
description: "Unregister a webhook endpoint. No further events are delivered; in-flight retries are dropped. Past delivery history is preserved.",
|
|
3952
|
+
annotations: {
|
|
3953
|
+
title: "Delete Webhook",
|
|
3954
|
+
readOnlyHint: false,
|
|
3955
|
+
destructiveHint: true,
|
|
3956
|
+
idempotentHint: true,
|
|
3957
|
+
openWorldHint: false
|
|
3958
|
+
},
|
|
3959
|
+
inputSchema: z.object(DeleteWebhookInputShape),
|
|
3960
|
+
handler: async (input, ctx) => {
|
|
3961
|
+
const result = await ctx.api.deleteWebhook(input.webhook_id);
|
|
3962
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3963
|
+
return ok({ deleted: true });
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
var GetWebhookInputShape = {
|
|
3967
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID.")
|
|
3968
|
+
};
|
|
3969
|
+
var getWebhookTool = {
|
|
3970
|
+
name: "get_webhook",
|
|
3971
|
+
description: "Get one webhook endpoint by UUID with URL, subscribed event types, active flag.",
|
|
3972
|
+
annotations: {
|
|
3973
|
+
title: "Get Webhook",
|
|
3974
|
+
readOnlyHint: true,
|
|
3975
|
+
destructiveHint: false,
|
|
3976
|
+
idempotentHint: true,
|
|
3977
|
+
openWorldHint: false
|
|
3978
|
+
},
|
|
3979
|
+
inputSchema: z.object(GetWebhookInputShape),
|
|
3980
|
+
handler: async (input, ctx) => {
|
|
3981
|
+
const result = await ctx.api.getWebhook(input.webhook_id);
|
|
3982
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
3983
|
+
return ok(result.value);
|
|
3984
|
+
}
|
|
3985
|
+
};
|
|
3986
|
+
var ListWebhookDeliveriesInputShape = {
|
|
3987
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
|
|
3988
|
+
page: z.number().int().min(1).max(500).default(1).describe("1-indexed page."),
|
|
3989
|
+
limit: z.number().int().min(1).max(200).default(50).describe("Page size.")
|
|
3990
|
+
};
|
|
3991
|
+
var listWebhookDeliveriesTool = {
|
|
3992
|
+
name: "list_webhook_deliveries",
|
|
3993
|
+
description: "List delivery attempts for one webhook endpoint with event type, status (pending / delivered / failed), HTTP response status if any, and attempt timestamp. Paginated.",
|
|
3994
|
+
annotations: {
|
|
3995
|
+
title: "List Webhook Deliveries",
|
|
3996
|
+
readOnlyHint: true,
|
|
3997
|
+
destructiveHint: false,
|
|
3998
|
+
idempotentHint: true,
|
|
3999
|
+
openWorldHint: false
|
|
4000
|
+
},
|
|
4001
|
+
inputSchema: z.object(ListWebhookDeliveriesInputShape),
|
|
4002
|
+
handler: async (input, ctx) => {
|
|
4003
|
+
const result = await ctx.api.listWebhookDeliveries(input.webhook_id, {
|
|
4004
|
+
page: input.page,
|
|
4005
|
+
limit: input.limit
|
|
4006
|
+
});
|
|
4007
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4008
|
+
return ok(result.value);
|
|
4009
|
+
}
|
|
4010
|
+
};
|
|
4011
|
+
var ListWebhookEventTypesInputShape = {};
|
|
4012
|
+
var listWebhookEventTypesTool = {
|
|
4013
|
+
name: "list_webhook_event_types",
|
|
4014
|
+
description: "List the catalog of event types a webhook can subscribe to (e.g. `scan.done`, `alert.opened`, `campaign.run.completed`) with each event's description and a sample payload.",
|
|
4015
|
+
annotations: {
|
|
4016
|
+
title: "List Webhook Event Types",
|
|
4017
|
+
readOnlyHint: true,
|
|
4018
|
+
destructiveHint: false,
|
|
4019
|
+
idempotentHint: true,
|
|
4020
|
+
openWorldHint: false
|
|
4021
|
+
},
|
|
4022
|
+
inputSchema: z.object(ListWebhookEventTypesInputShape),
|
|
4023
|
+
handler: async (_input, ctx) => {
|
|
4024
|
+
const result = await ctx.api.listWebhookEventTypes();
|
|
4025
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4026
|
+
return ok({ items: result.value.entries, total: result.value.entries.length });
|
|
4027
|
+
}
|
|
4028
|
+
};
|
|
4029
|
+
var ListWebhooksInputShape = {};
|
|
4030
|
+
var listWebhooksTool = {
|
|
4031
|
+
name: "list_webhooks",
|
|
4032
|
+
description: "List the organization's registered webhook endpoints with their URL, subscribed event types, and active flag.",
|
|
4033
|
+
annotations: {
|
|
4034
|
+
title: "List Webhooks",
|
|
4035
|
+
readOnlyHint: true,
|
|
4036
|
+
destructiveHint: false,
|
|
4037
|
+
idempotentHint: true,
|
|
4038
|
+
openWorldHint: false
|
|
4039
|
+
},
|
|
4040
|
+
inputSchema: z.object(ListWebhooksInputShape),
|
|
4041
|
+
handler: async (_input, ctx) => {
|
|
4042
|
+
const result = await ctx.api.listWebhooks();
|
|
4043
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4044
|
+
return ok({ items: result.value, total: result.value.length });
|
|
4045
|
+
}
|
|
4046
|
+
};
|
|
4047
|
+
var ReplayWebhookDeliveryInputShape = {
|
|
4048
|
+
attempt_id: z.string().uuid().describe("Delivery-attempt UUID (from `list_webhook_deliveries`).")
|
|
4049
|
+
};
|
|
4050
|
+
var replayWebhookDeliveryTool = {
|
|
4051
|
+
name: "replay_webhook_delivery",
|
|
4052
|
+
description: "Queue a re-attempt for one specific webhook-delivery attempt by id. Useful when an endpoint was temporarily down. Result of the replay shows up as a new entry in `list_webhook_deliveries`.",
|
|
4053
|
+
annotations: {
|
|
4054
|
+
title: "Replay Webhook Delivery",
|
|
4055
|
+
readOnlyHint: false,
|
|
4056
|
+
destructiveHint: false,
|
|
4057
|
+
idempotentHint: false,
|
|
4058
|
+
openWorldHint: false
|
|
4059
|
+
},
|
|
4060
|
+
inputSchema: z.object(ReplayWebhookDeliveryInputShape),
|
|
4061
|
+
handler: async (input, ctx) => {
|
|
4062
|
+
const result = await ctx.api.replayWebhookDelivery(input.attempt_id);
|
|
4063
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4064
|
+
return ok({ queued: true });
|
|
4065
|
+
}
|
|
4066
|
+
};
|
|
4067
|
+
var RotateWebhookSecretInputShape = {
|
|
4068
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID.")
|
|
4069
|
+
};
|
|
4070
|
+
var rotateWebhookSecretTool = {
|
|
4071
|
+
name: "rotate_webhook_secret",
|
|
4072
|
+
description: "Generate a new signing secret for a webhook. The new secret is returned IN FULL once \u2014 tell the user to store it. Subsequent deliveries are signed with the new secret; the old one stops working immediately.",
|
|
4073
|
+
annotations: {
|
|
4074
|
+
title: "Rotate Webhook Secret",
|
|
4075
|
+
readOnlyHint: false,
|
|
4076
|
+
destructiveHint: true,
|
|
4077
|
+
idempotentHint: false,
|
|
4078
|
+
openWorldHint: false
|
|
4079
|
+
},
|
|
4080
|
+
inputSchema: z.object(RotateWebhookSecretInputShape),
|
|
4081
|
+
handler: async (input, ctx) => {
|
|
4082
|
+
const result = await ctx.api.rotateWebhookSecret(input.webhook_id);
|
|
4083
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4084
|
+
return ok(result.value);
|
|
4085
|
+
}
|
|
4086
|
+
};
|
|
4087
|
+
var TestWebhookInputShape = {
|
|
4088
|
+
webhook_id: z.string().uuid().describe("Webhook endpoint UUID."),
|
|
4089
|
+
event_type: z.string().min(1).max(100).describe(
|
|
4090
|
+
"Event type slug whose sample payload to send (see `list_webhook_event_types` for the catalog)."
|
|
4091
|
+
)
|
|
4092
|
+
};
|
|
4093
|
+
var testWebhookTool = {
|
|
4094
|
+
name: "test_webhook",
|
|
4095
|
+
description: "Dispatch a synthetic event with the sample payload for the given `event_type` to the webhook endpoint and return the receiver's response synchronously. Includes HTTP status, elapsed time, and a snippet of the response body so the operator can diagnose receiver bugs.",
|
|
4096
|
+
annotations: {
|
|
4097
|
+
title: "Test Webhook",
|
|
4098
|
+
readOnlyHint: false,
|
|
4099
|
+
destructiveHint: false,
|
|
4100
|
+
idempotentHint: false,
|
|
4101
|
+
openWorldHint: true
|
|
4102
|
+
},
|
|
4103
|
+
inputSchema: z.object(TestWebhookInputShape),
|
|
4104
|
+
handler: async (input, ctx) => {
|
|
4105
|
+
const result = await ctx.api.testWebhook(input.webhook_id, { event_type: input.event_type });
|
|
4106
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4107
|
+
return ok(result.value);
|
|
4108
|
+
}
|
|
4109
|
+
};
|
|
4110
|
+
var UpdateWebhookInputShape = {
|
|
4111
|
+
webhook_id: z.string().uuid().describe("Webhook UUID."),
|
|
4112
|
+
url: z.string().url().optional().describe("New endpoint URL."),
|
|
4113
|
+
event_types: z.array(z.string()).max(50).optional().describe("Replace the subscribed-event-types list."),
|
|
4114
|
+
is_active: z.boolean().optional().describe("Enable / disable delivery.")
|
|
4115
|
+
};
|
|
4116
|
+
var updateWebhookTool = {
|
|
4117
|
+
name: "update_webhook",
|
|
4118
|
+
description: "Update a webhook endpoint's URL, event-type subscriptions, and/or active flag. Signing secret is NOT rotated by this call \u2014 use `rotate_webhook_secret` for that.",
|
|
4119
|
+
annotations: {
|
|
4120
|
+
title: "Update Webhook",
|
|
4121
|
+
readOnlyHint: false,
|
|
4122
|
+
destructiveHint: false,
|
|
4123
|
+
idempotentHint: true,
|
|
4124
|
+
openWorldHint: false
|
|
4125
|
+
},
|
|
4126
|
+
inputSchema: z.object(UpdateWebhookInputShape),
|
|
4127
|
+
handler: async (input, ctx) => {
|
|
4128
|
+
const body = {
|
|
4129
|
+
...input.url !== void 0 ? { url: input.url } : {},
|
|
4130
|
+
...input.event_types !== void 0 ? { event_types: input.event_types } : {},
|
|
4131
|
+
...input.is_active !== void 0 ? { is_active: input.is_active } : {}
|
|
4132
|
+
};
|
|
4133
|
+
const result = await ctx.api.updateWebhook(input.webhook_id, body);
|
|
4134
|
+
if (result.isErr()) return err(mapApiError(result.error));
|
|
4135
|
+
return ok(result.value);
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
4138
|
+
|
|
4139
|
+
// src/application/tool-registry.ts
|
|
4140
|
+
function registerAllTools(register) {
|
|
4141
|
+
register(getAccountTool);
|
|
4142
|
+
register(updateOrgTool);
|
|
4143
|
+
register(listOrgUsersTool);
|
|
4144
|
+
register(inviteUserTool);
|
|
4145
|
+
register(updateUserRoleTool);
|
|
4146
|
+
register(removeUserTool);
|
|
4147
|
+
register(transferOwnershipTool);
|
|
4148
|
+
register(listOrgRolesTool);
|
|
4149
|
+
register(listApiKeysTool);
|
|
4150
|
+
register(createApiKeyTool);
|
|
4151
|
+
register(revokeApiKeyTool);
|
|
4152
|
+
register(listGeosTool);
|
|
4153
|
+
register(listEmulatorsTool);
|
|
4154
|
+
register(getScanTool);
|
|
4155
|
+
register(listScansTool);
|
|
4156
|
+
register(cancelScanTool);
|
|
4157
|
+
register(createBulkScansTool);
|
|
4158
|
+
register(createScanTool);
|
|
4159
|
+
register(recheckScansTool);
|
|
4160
|
+
register(getCampaignTool);
|
|
4161
|
+
register(listCampaignsTool);
|
|
4162
|
+
register(archiveCampaignTool);
|
|
4163
|
+
register(unarchiveCampaignTool);
|
|
4164
|
+
register(cancelCampaignTool);
|
|
4165
|
+
register(runCampaignTool);
|
|
4166
|
+
register(createCampaignTool);
|
|
4167
|
+
register(updateCampaignTool);
|
|
4168
|
+
register(listCampaignRunsTool);
|
|
4169
|
+
register(listCampaignsPickerTool);
|
|
4170
|
+
register(getRunTool);
|
|
4171
|
+
register(listRunScansTool);
|
|
4172
|
+
register(cancelRunTool);
|
|
4173
|
+
register(getCampaignGroupTool);
|
|
4174
|
+
register(listCampaignGroupsTool);
|
|
4175
|
+
register(createCampaignGroupTool);
|
|
4176
|
+
register(updateCampaignGroupTool);
|
|
4177
|
+
register(runCampaignGroupTool);
|
|
4178
|
+
register(cancelCampaignGroupTool);
|
|
4179
|
+
register(archiveCampaignGroupTool);
|
|
4180
|
+
register(unarchiveCampaignGroupTool);
|
|
4181
|
+
register(pauseCampaignGroupScheduleTool);
|
|
4182
|
+
register(resumeCampaignGroupScheduleTool);
|
|
4183
|
+
register(listTagsTool);
|
|
4184
|
+
register(getTagDefinitionTool);
|
|
4185
|
+
register(updateTagDefinitionTool);
|
|
4186
|
+
register(deleteTagDefinitionTool);
|
|
4187
|
+
register(listScanTagsTool);
|
|
4188
|
+
register(listCustomRulesTool);
|
|
4189
|
+
register(getCustomRuleTool);
|
|
4190
|
+
register(createCustomRuleTool);
|
|
4191
|
+
register(updateCustomRuleTool);
|
|
4192
|
+
register(deleteCustomRuleTool);
|
|
4193
|
+
register(testCustomRuleTool);
|
|
4194
|
+
register(listPolicySetsTool);
|
|
4195
|
+
register(getPolicySetTool);
|
|
4196
|
+
register(createPolicySetTool);
|
|
4197
|
+
register(updatePolicySetTool);
|
|
4198
|
+
register(deletePolicySetTool);
|
|
4199
|
+
register(requestPolicySetApprovalTool);
|
|
4200
|
+
register(listAlertsTool);
|
|
4201
|
+
register(updateAlertStatusTool);
|
|
4202
|
+
register(getAlertStatsTool);
|
|
4203
|
+
register(listWebhooksTool);
|
|
4204
|
+
register(getWebhookTool);
|
|
4205
|
+
register(createWebhookTool);
|
|
4206
|
+
register(updateWebhookTool);
|
|
4207
|
+
register(deleteWebhookTool);
|
|
4208
|
+
register(listWebhookEventTypesTool);
|
|
4209
|
+
register(listWebhookDeliveriesTool);
|
|
4210
|
+
register(testWebhookTool);
|
|
4211
|
+
register(rotateWebhookSecretTool);
|
|
4212
|
+
register(replayWebhookDeliveryTool);
|
|
4213
|
+
register(bulkReplayWebhookTool);
|
|
4214
|
+
register(getBillingSummaryTool);
|
|
4215
|
+
register(listUsageTool);
|
|
4216
|
+
register(getUsageSummaryTool);
|
|
4217
|
+
register(listBalanceHistoryTool);
|
|
4218
|
+
register(listInvoicesTool);
|
|
4219
|
+
register(listAlertDestinationsTool);
|
|
4220
|
+
register(deleteAlertDestinationTool);
|
|
4221
|
+
register(setAlertDestinationVersionTool);
|
|
4222
|
+
register(getCampaignAlertOverridesTool);
|
|
4223
|
+
register(setCampaignAlertOverridesTool);
|
|
4224
|
+
}
|
|
4225
|
+
|
|
4226
|
+
// src/presentation/shared/wire-tools.ts
|
|
4227
|
+
function wireToolsIntoMcpServer(server, ctxProvider) {
|
|
4228
|
+
registerAllTools((tool) => {
|
|
4229
|
+
server.registerTool(
|
|
4230
|
+
tool.name,
|
|
4231
|
+
{
|
|
4232
|
+
title: tool.annotations.title,
|
|
4233
|
+
description: tool.description,
|
|
4234
|
+
inputSchema: tool.inputSchema,
|
|
4235
|
+
annotations: {
|
|
4236
|
+
title: tool.annotations.title,
|
|
4237
|
+
readOnlyHint: tool.annotations.readOnlyHint,
|
|
4238
|
+
destructiveHint: tool.annotations.destructiveHint,
|
|
4239
|
+
idempotentHint: tool.annotations.idempotentHint,
|
|
4240
|
+
openWorldHint: tool.annotations.openWorldHint
|
|
4241
|
+
}
|
|
4242
|
+
},
|
|
4243
|
+
(async (rawArgs, _extra) => {
|
|
4244
|
+
const ctx = ctxProvider();
|
|
4245
|
+
const parsed = tool.inputSchema.parse(rawArgs);
|
|
4246
|
+
const result = await tool.handler(parsed, ctx);
|
|
4247
|
+
if (result.isErr()) {
|
|
4248
|
+
return toolErrorToMcpResult(result.error);
|
|
4249
|
+
}
|
|
4250
|
+
return toolOkToMcpResult(result.value);
|
|
4251
|
+
})
|
|
4252
|
+
);
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
function toolOkToMcpResult(value) {
|
|
4256
|
+
const isPlainObject = value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4257
|
+
return {
|
|
4258
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
4259
|
+
...isPlainObject ? { structuredContent: value } : {}
|
|
4260
|
+
};
|
|
4261
|
+
}
|
|
4262
|
+
function toolErrorToMcpResult(error) {
|
|
4263
|
+
return {
|
|
4264
|
+
isError: true,
|
|
4265
|
+
content: [{ type: "text", text: formatToolError(error) }]
|
|
4266
|
+
};
|
|
4267
|
+
}
|
|
4268
|
+
function formatToolError(error) {
|
|
4269
|
+
switch (error.kind) {
|
|
4270
|
+
case "unauthorized":
|
|
4271
|
+
return `Unauthorized: ${error.message}`;
|
|
4272
|
+
case "forbidden":
|
|
4273
|
+
return `Forbidden${error.code === void 0 ? "" : ` (${error.code})`}: ${error.message}`;
|
|
4274
|
+
case "not-found":
|
|
4275
|
+
return `Not found: ${error.message}`;
|
|
4276
|
+
case "rate-limited":
|
|
4277
|
+
return `Rate limited: ${error.message}${error.retryAfterMs === void 0 ? "" : ` (retry after ${String(error.retryAfterMs)} ms)`}`;
|
|
4278
|
+
case "invalid-input":
|
|
4279
|
+
return `Invalid input${error.code === void 0 ? "" : ` (${error.code})`}: ${error.message}`;
|
|
4280
|
+
case "upstream":
|
|
4281
|
+
return `Upstream error: ${error.message}`;
|
|
4282
|
+
case "internal":
|
|
4283
|
+
return `Internal error: ${error.message}`;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
export { BearerToken, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
4288
|
+
//# sourceMappingURL=chunk-4MNLSWSZ.js.map
|
|
4289
|
+
//# sourceMappingURL=chunk-4MNLSWSZ.js.map
|