@mentio-dev/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3823 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/index.ts
9
+ import { Command, Option } from "commander";
10
+ import { writeFileSync as writeFileSync2 } from "fs";
11
+
12
+ // package.json
13
+ var package_default = {
14
+ name: "@mentio-dev/cli",
15
+ version: "0.1.0",
16
+ description: "Command-line client for the Mentio API: one command per endpoint, plus watch and MCP helpers.",
17
+ license: "MIT",
18
+ homepage: "https://docs.mentio.dev/cli",
19
+ repository: {
20
+ type: "git",
21
+ url: "git+https://github.com/PauGuirao/mentions.git",
22
+ directory: "packages/cli"
23
+ },
24
+ bugs: {
25
+ url: "https://github.com/PauGuirao/mentions/issues"
26
+ },
27
+ keywords: [
28
+ "mentio",
29
+ "social listening",
30
+ "brand monitoring",
31
+ "cli",
32
+ "mcp"
33
+ ],
34
+ type: "module",
35
+ bin: {
36
+ mentio: "./dist/index.js"
37
+ },
38
+ publishConfig: {
39
+ access: "public"
40
+ },
41
+ files: [
42
+ "dist",
43
+ "README.md"
44
+ ],
45
+ engines: {
46
+ node: ">=22"
47
+ },
48
+ scripts: {
49
+ generate: "tsx scripts/generate-operations.ts",
50
+ build: "tsup",
51
+ dev: "tsx src/index.ts",
52
+ typecheck: "tsc --noEmit",
53
+ test: "vitest run",
54
+ prepublishOnly: "pnpm build"
55
+ },
56
+ dependencies: {
57
+ commander: "^15.0.0"
58
+ },
59
+ devDependencies: {
60
+ "@mentio-dev/sdk": "workspace:*",
61
+ "@types/node": "^22.15.0",
62
+ tsup: "^8.5.1",
63
+ tsx: "^4.23.1",
64
+ typescript: "^5.7.0",
65
+ vitest: "^3.0.0"
66
+ }
67
+ };
68
+
69
+ // src/config.ts
70
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
71
+ import { homedir } from "os";
72
+ import { join } from "path";
73
+ var DEFAULT_API_URL = "https://api.mentio.dev";
74
+ function configDir(env = process.env) {
75
+ return env.MENTIO_CONFIG_DIR ?? join(homedir(), ".mentio");
76
+ }
77
+ function configPath(env = process.env) {
78
+ return join(configDir(env), "config.json");
79
+ }
80
+ function readConfig(env = process.env) {
81
+ const path = configPath(env);
82
+ if (!existsSync(path)) return {};
83
+ try {
84
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
85
+ if (parsed === null || typeof parsed !== "object") return {};
86
+ const record = parsed;
87
+ return {
88
+ apiKey: typeof record.apiKey === "string" ? record.apiKey : void 0,
89
+ apiUrl: typeof record.apiUrl === "string" ? record.apiUrl : void 0
90
+ };
91
+ } catch {
92
+ return {};
93
+ }
94
+ }
95
+ function writeConfig(patch, env = process.env) {
96
+ const current = readConfig(env);
97
+ const next = { ...current };
98
+ if (patch.apiKey === null) delete next.apiKey;
99
+ else if (patch.apiKey !== void 0) next.apiKey = patch.apiKey;
100
+ if (patch.apiUrl === null) delete next.apiUrl;
101
+ else if (patch.apiUrl !== void 0) next.apiUrl = patch.apiUrl;
102
+ const dir = configDir(env);
103
+ mkdirSync(dir, { recursive: true, mode: 448 });
104
+ const path = configPath(env);
105
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}
106
+ `, { mode: 384 });
107
+ chmodSync(path, 384);
108
+ return path;
109
+ }
110
+ function resolveSettings(flags, env = process.env) {
111
+ const file = readConfig(env);
112
+ const apiUrl = (flags.apiUrl ?? env.MENTIO_API_URL ?? file.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, "");
113
+ if (flags.apiKey) return { apiKey: flags.apiKey, apiUrl, source: "flag" };
114
+ if (env.MENTIO_API_KEY) return { apiKey: env.MENTIO_API_KEY, apiUrl, source: "env" };
115
+ if (file.apiKey) return { apiKey: file.apiKey, apiUrl, source: "file" };
116
+ return { apiKey: void 0, apiUrl, source: "none" };
117
+ }
118
+ function keyPrefix(key) {
119
+ const match = /^([a-z]+_[a-z]+_[a-z0-9]{4})/i.exec(key);
120
+ return match?.[1] ?? key.slice(0, 12);
121
+ }
122
+
123
+ // src/flags.ts
124
+ var UsageError = class extends Error {
125
+ constructor(message) {
126
+ super(message);
127
+ this.name = "UsageError";
128
+ }
129
+ };
130
+ function coerceScalar(field, raw, type) {
131
+ switch (type) {
132
+ case "integer":
133
+ case "number": {
134
+ const n = Number(raw);
135
+ if (raw.trim() === "" || !Number.isFinite(n)) throw new UsageError(`--${field.name} expects a number, got "${raw}"`);
136
+ if (type === "integer" && !Number.isInteger(n)) throw new UsageError(`--${field.name} expects a whole number, got "${raw}"`);
137
+ return n;
138
+ }
139
+ case "boolean":
140
+ if (raw === "true") return true;
141
+ if (raw === "false") return false;
142
+ throw new UsageError(`--${field.name} expects true or false, got "${raw}"`);
143
+ case "object": {
144
+ try {
145
+ const parsed = JSON.parse(raw);
146
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("not an object");
147
+ return parsed;
148
+ } catch {
149
+ throw new UsageError(`--${field.name} expects a JSON object, got "${raw}"`);
150
+ }
151
+ }
152
+ case "array":
153
+ case "string":
154
+ default:
155
+ return raw;
156
+ }
157
+ }
158
+ function coerce(field, raw) {
159
+ if (field.nullable && raw === "null") return null;
160
+ if (field.type === "array") {
161
+ if (field.in === "query") return raw;
162
+ const trimmed = raw.trim();
163
+ if (trimmed.startsWith("[")) {
164
+ try {
165
+ return JSON.parse(trimmed);
166
+ } catch {
167
+ throw new UsageError(`--${field.name} expects a comma-separated list or a JSON array`);
168
+ }
169
+ }
170
+ const items = trimmed === "" ? [] : trimmed.split(",").map((v) => v.trim());
171
+ return items.map((item) => coerceScalar(field, item, field.items ?? "string"));
172
+ }
173
+ return coerceScalar(field, raw, field.type);
174
+ }
175
+ function flagHelp(field) {
176
+ const parts = [];
177
+ if (field.description) parts.push(field.description.replace(/\s+/g, " ").trim());
178
+ const hints = [];
179
+ if (field.type === "array") hints.push(field.enum ? `comma-separated: ${field.enum.join("|")}` : "comma-separated");
180
+ else if (field.type === "object") hints.push("JSON");
181
+ else if (field.type !== "string" && !field.enum) hints.push(field.type);
182
+ if (field.nullable) hints.push("null clears");
183
+ if (hints.length > 0) parts.push(`(${hints.join(", ")})`);
184
+ return parts.join(" ");
185
+ }
186
+ function buildRequest(op, positional, flags, jsonBody) {
187
+ const path = {};
188
+ const pathParams = op.params.filter((p) => p.in === "path");
189
+ pathParams.forEach((param, index) => {
190
+ const value = positional[index];
191
+ if (value === void 0 || value === "") throw new UsageError(`missing <${param.name}>`);
192
+ path[param.name] = value;
193
+ });
194
+ const query = {};
195
+ for (const param of op.params.filter((p) => p.in === "query")) {
196
+ const raw = flags[param.name];
197
+ if (raw === void 0) continue;
198
+ const value = coerce(param, String(raw));
199
+ if (value === null || typeof value === "object") continue;
200
+ query[param.name] = value;
201
+ }
202
+ let body;
203
+ if (op.body) {
204
+ body = {};
205
+ if (jsonBody !== void 0) {
206
+ let parsed;
207
+ try {
208
+ parsed = JSON.parse(jsonBody);
209
+ } catch {
210
+ throw new UsageError("--json is not valid JSON");
211
+ }
212
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new UsageError("--json must be a JSON object");
213
+ body = parsed;
214
+ }
215
+ for (const field of op.body.fields) {
216
+ const raw = flags[field.name];
217
+ if (raw === void 0) continue;
218
+ body[field.name] = coerce(field, String(raw));
219
+ }
220
+ for (const field of op.body.fields) {
221
+ if (field.required && body[field.name] === void 0) throw new UsageError(`--${field.name} is required`);
222
+ }
223
+ }
224
+ return { path, query, body };
225
+ }
226
+
227
+ // src/mcp.ts
228
+ var DEFAULT_MCP_URL = "https://mcp.mentio.dev/mcp";
229
+ function mcpConfig(kind, url, apiKey) {
230
+ const auth = `Bearer ${apiKey}`;
231
+ switch (kind) {
232
+ case "claude":
233
+ return `claude mcp add --transport http mentio ${url} --header "Authorization: ${auth}"`;
234
+ case "vscode":
235
+ return JSON.stringify({ servers: { mentio: { type: "http", url, headers: { Authorization: auth } } } }, null, 2);
236
+ case "cursor":
237
+ return JSON.stringify({ mcpServers: { mentio: { url, headers: { Authorization: auth } } } }, null, 2);
238
+ case "generic":
239
+ return JSON.stringify({ mcpServers: { mentio: { type: "http", url, headers: { Authorization: auth } } } }, null, 2);
240
+ }
241
+ }
242
+
243
+ // src/naming.ts
244
+ function commandName(op) {
245
+ const segments = op.path.replace(/^\/v1\//, "").split("/");
246
+ const noun = segments[0] ?? "";
247
+ if (noun === "health") return "system:health";
248
+ const rest = segments.slice(1);
249
+ const hasId = rest.some((s) => s.startsWith("{"));
250
+ const action = rest.find((s) => !s.startsWith("{"));
251
+ const method = op.method.toLowerCase();
252
+ let verb;
253
+ if (action !== void 0) {
254
+ verb = action === "export.csv" ? "export" : action;
255
+ } else if (method === "get" && !hasId) {
256
+ verb = op.operationId.startsWith("search") ? "search" : noun === "company" ? "get" : "list";
257
+ } else if (method === "post" && !hasId) {
258
+ verb = "create";
259
+ } else if (method === "get") {
260
+ verb = "get";
261
+ } else if (method === "patch") {
262
+ verb = "update";
263
+ } else if (method === "delete") {
264
+ verb = op.operationId.startsWith("revoke") ? "revoke" : "delete";
265
+ } else {
266
+ verb = method;
267
+ }
268
+ return `${noun}:${verb}`;
269
+ }
270
+
271
+ // src/generated/operations.ts
272
+ var OPERATIONS = [
273
+ {
274
+ "operationId": "getHealth",
275
+ "method": "GET",
276
+ "path": "/v1/health",
277
+ "summary": "getHealth",
278
+ "tag": "System",
279
+ "params": [],
280
+ "body": null,
281
+ "response": "json"
282
+ },
283
+ {
284
+ "operationId": "createKeyword",
285
+ "method": "POST",
286
+ "path": "/v1/keywords",
287
+ "summary": "Track a keyword",
288
+ "description": "Start tracking a word or phrase. Matching, classification and delivery begin on the next poll. Free workspaces track 2 keywords; a subscription raises that to 500.",
289
+ "tag": "Keywords",
290
+ "params": [],
291
+ "body": {
292
+ "fields": [
293
+ {
294
+ "name": "term",
295
+ "type": "string",
296
+ "description": "The word or phrase to track, matched case-insensitively as a phrase.",
297
+ "required": true,
298
+ "nullable": false
299
+ },
300
+ {
301
+ "name": "kind",
302
+ "type": "string",
303
+ "description": "brand: your own names. competitor: theirs. topic: the space. Drives share of voice and segments.",
304
+ "enum": [
305
+ "brand",
306
+ "competitor",
307
+ "topic"
308
+ ],
309
+ "required": false,
310
+ "nullable": false
311
+ },
312
+ {
313
+ "name": "platforms",
314
+ "type": "array",
315
+ "description": "Platforms to track it on; omit or null for every platform.",
316
+ "enum": [
317
+ "bluesky",
318
+ "hackernews",
319
+ "github",
320
+ "stackoverflow",
321
+ "devto",
322
+ "reddit",
323
+ "x",
324
+ "youtube",
325
+ "news",
326
+ "linkedin"
327
+ ],
328
+ "required": false,
329
+ "nullable": true,
330
+ "items": "string"
331
+ }
332
+ ]
333
+ },
334
+ "response": "json"
335
+ },
336
+ {
337
+ "operationId": "listKeywords",
338
+ "method": "GET",
339
+ "path": "/v1/keywords",
340
+ "summary": "List keywords",
341
+ "description": "Every keyword of the workspace, newest first, with its match stats and poll health.",
342
+ "tag": "Keywords",
343
+ "params": [],
344
+ "body": null,
345
+ "response": "json"
346
+ },
347
+ {
348
+ "operationId": "getKeyword",
349
+ "method": "GET",
350
+ "path": "/v1/keywords/{id}",
351
+ "summary": "Get a keyword",
352
+ "tag": "Keywords",
353
+ "params": [
354
+ {
355
+ "name": "id",
356
+ "in": "path",
357
+ "type": "string",
358
+ "description": "Keyword id (kw_...).",
359
+ "required": true,
360
+ "nullable": false
361
+ }
362
+ ],
363
+ "body": null,
364
+ "response": "json"
365
+ },
366
+ {
367
+ "operationId": "updateKeyword",
368
+ "method": "PATCH",
369
+ "path": "/v1/keywords/{id}",
370
+ "summary": "Update a keyword",
371
+ "description": "Mute or unmute it, or change the platforms it is tracked on.",
372
+ "tag": "Keywords",
373
+ "params": [
374
+ {
375
+ "name": "id",
376
+ "in": "path",
377
+ "type": "string",
378
+ "description": "Keyword id (kw_...).",
379
+ "required": true,
380
+ "nullable": false
381
+ }
382
+ ],
383
+ "body": {
384
+ "description": "Omitted fields are untouched.",
385
+ "fields": [
386
+ {
387
+ "name": "muted",
388
+ "type": "boolean",
389
+ "description": "A muted keyword stops polling and matching; its mentions stay.",
390
+ "required": false,
391
+ "nullable": false
392
+ },
393
+ {
394
+ "name": "platforms",
395
+ "type": "array",
396
+ "description": "Replaces the platform list; null means every platform.",
397
+ "enum": [
398
+ "bluesky",
399
+ "hackernews",
400
+ "github",
401
+ "stackoverflow",
402
+ "devto",
403
+ "reddit",
404
+ "x",
405
+ "youtube",
406
+ "news",
407
+ "linkedin"
408
+ ],
409
+ "required": false,
410
+ "nullable": true,
411
+ "items": "string"
412
+ }
413
+ ]
414
+ },
415
+ "response": "json"
416
+ },
417
+ {
418
+ "operationId": "deleteKeyword",
419
+ "method": "DELETE",
420
+ "path": "/v1/keywords/{id}",
421
+ "summary": "Delete a keyword",
422
+ "description": "Removes the keyword and its matches. Posts also matched by another keyword stay.",
423
+ "tag": "Keywords",
424
+ "params": [
425
+ {
426
+ "name": "id",
427
+ "in": "path",
428
+ "type": "string",
429
+ "description": "Keyword id (kw_...).",
430
+ "required": true,
431
+ "nullable": false
432
+ }
433
+ ],
434
+ "body": null,
435
+ "response": "none"
436
+ },
437
+ {
438
+ "operationId": "updateMention",
439
+ "method": "PATCH",
440
+ "path": "/v1/mentions/{id}",
441
+ "summary": "Update a mention",
442
+ "description": "The one write on a mention. Set status to ignored or done to handle it (open puts it back), assign it to a workspace member, snooze it out of the feed, or leave an internal note. Null clears a field; omitted fields are untouched. Delivery and billing never change.",
443
+ "tag": "Mentions",
444
+ "params": [
445
+ {
446
+ "name": "id",
447
+ "in": "path",
448
+ "type": "string",
449
+ "description": "Mention id (mm_...).",
450
+ "required": true,
451
+ "nullable": false
452
+ }
453
+ ],
454
+ "body": {
455
+ "description": "Every field is optional; omitted fields are untouched.",
456
+ "fields": [
457
+ {
458
+ "name": "status",
459
+ "type": "string",
460
+ "description": "ignored or done to handle it; open to put it back.",
461
+ "enum": [
462
+ "open",
463
+ "ignored",
464
+ "done"
465
+ ],
466
+ "required": false,
467
+ "nullable": false
468
+ },
469
+ {
470
+ "name": "assigneeId",
471
+ "type": "string",
472
+ "description": "A workspace member (user id), or null to unassign.",
473
+ "required": false,
474
+ "nullable": true
475
+ },
476
+ {
477
+ "name": "snoozedUntil",
478
+ "type": "integer",
479
+ "description": "ISO 8601 (or epoch ms) until which the mention leaves the feed; null wakes it.",
480
+ "required": false,
481
+ "nullable": true
482
+ },
483
+ {
484
+ "name": "note",
485
+ "type": "string",
486
+ "description": "Internal note; null or empty clears it.",
487
+ "required": false,
488
+ "nullable": true
489
+ }
490
+ ]
491
+ },
492
+ "response": "json"
493
+ },
494
+ {
495
+ "operationId": "getMention",
496
+ "method": "GET",
497
+ "path": "/v1/mentions/{id}",
498
+ "summary": "Get a mention",
499
+ "description": "One mention by id, as it appears in the list: the post, its author with reach and your tags, the classification, the priority score and the triage fields. Ids belong to your organization; any other id is a 404.",
500
+ "tag": "Mentions",
501
+ "params": [
502
+ {
503
+ "name": "id",
504
+ "in": "path",
505
+ "type": "string",
506
+ "description": "Mention id (mm_...).",
507
+ "required": true,
508
+ "nullable": false
509
+ }
510
+ ],
511
+ "body": null,
512
+ "response": "json"
513
+ },
514
+ {
515
+ "operationId": "searchMentions",
516
+ "method": "GET",
517
+ "path": "/v1/mentions",
518
+ "summary": "List mentions",
519
+ "description": "Mentions matched to your keywords, filtered and paginated. Default order is newest match first; sort=priority ranks by attention score. Page with nextCursor, passing the same filters and sort. A mention is one post matched to one keyword.",
520
+ "tag": "Mentions",
521
+ "params": [
522
+ {
523
+ "name": "keywordId",
524
+ "in": "query",
525
+ "type": "string",
526
+ "description": "Only matches of this keyword.",
527
+ "required": false,
528
+ "nullable": false
529
+ },
530
+ {
531
+ "name": "platform",
532
+ "in": "query",
533
+ "type": "string",
534
+ "description": "Only posts from this platform.",
535
+ "enum": [
536
+ "bluesky",
537
+ "hackernews",
538
+ "github",
539
+ "stackoverflow",
540
+ "devto",
541
+ "reddit",
542
+ "x",
543
+ "youtube",
544
+ "news",
545
+ "linkedin"
546
+ ],
547
+ "required": false,
548
+ "nullable": false
549
+ },
550
+ {
551
+ "name": "status",
552
+ "in": "query",
553
+ "type": "string",
554
+ "description": "Only mentions in this status. Omit for every status.",
555
+ "enum": [
556
+ "open",
557
+ "ignored",
558
+ "done"
559
+ ],
560
+ "required": false,
561
+ "nullable": false
562
+ },
563
+ {
564
+ "name": "relevant",
565
+ "in": "query",
566
+ "type": "boolean",
567
+ "description": "true: only mentions the classifier scored relevant; false: only the rest (unclassified included).",
568
+ "required": false,
569
+ "nullable": false
570
+ },
571
+ {
572
+ "name": "sentiment",
573
+ "in": "query",
574
+ "type": "string",
575
+ "description": "Only this sentiment.",
576
+ "enum": [
577
+ "positive",
578
+ "neutral",
579
+ "negative"
580
+ ],
581
+ "required": false,
582
+ "nullable": false
583
+ },
584
+ {
585
+ "name": "intent",
586
+ "in": "query",
587
+ "type": "string",
588
+ "description": "Only mentions carrying this intent (buy_intent, question, complaint, praise, comparison).",
589
+ "required": false,
590
+ "nullable": false
591
+ },
592
+ {
593
+ "name": "personId",
594
+ "in": "query",
595
+ "type": "string",
596
+ "description": "Only this person (an id from /v1/people), merged accounts included. Implies includeMuted.",
597
+ "required": false,
598
+ "nullable": false
599
+ },
600
+ {
601
+ "name": "includeMuted",
602
+ "in": "query",
603
+ "type": "boolean",
604
+ "description": "true: include mentions by people you muted, hidden by default.",
605
+ "required": false,
606
+ "nullable": false
607
+ },
608
+ {
609
+ "name": "assigneeId",
610
+ "in": "query",
611
+ "type": "string",
612
+ "description": "Only mentions assigned to this workspace member (user id).",
613
+ "required": false,
614
+ "nullable": false
615
+ },
616
+ {
617
+ "name": "snoozed",
618
+ "in": "query",
619
+ "type": "boolean",
620
+ "description": "true: only mentions currently snoozed. Otherwise snoozed mentions stay out until they wake.",
621
+ "required": false,
622
+ "nullable": false
623
+ },
624
+ {
625
+ "name": "excludeAuthors",
626
+ "in": "query",
627
+ "type": "array",
628
+ "description": "Hide these authors: display names, handles or profile URLs. Repeatable, or one comma-separated value.",
629
+ "required": false,
630
+ "nullable": false
631
+ },
632
+ {
633
+ "name": "minRelevance",
634
+ "in": "query",
635
+ "type": "integer",
636
+ "description": "Only mentions scored at least this; unclassified ones are excluded.",
637
+ "required": false,
638
+ "nullable": false
639
+ },
640
+ {
641
+ "name": "minFollowers",
642
+ "in": "query",
643
+ "type": "integer",
644
+ "description": "Only authors with at least this many followers. Unknown reach never passes.",
645
+ "required": false,
646
+ "nullable": false
647
+ },
648
+ {
649
+ "name": "tags",
650
+ "in": "query",
651
+ "type": "array",
652
+ "description": "Only authors your workspace tagged with any of these (exact, case-sensitive). Repeatable, or comma-separated.",
653
+ "required": false,
654
+ "nullable": false
655
+ },
656
+ {
657
+ "name": "q",
658
+ "in": "query",
659
+ "type": "string",
660
+ "description": "Substring search in the post text.",
661
+ "required": false,
662
+ "nullable": false
663
+ },
664
+ {
665
+ "name": "since",
666
+ "in": "query",
667
+ "type": "integer",
668
+ "description": "Only posts published at or after this instant (ISO 8601, or epoch ms).",
669
+ "required": false,
670
+ "nullable": false
671
+ },
672
+ {
673
+ "name": "until",
674
+ "in": "query",
675
+ "type": "integer",
676
+ "description": "Only posts published at or before this instant (ISO 8601, or epoch ms).",
677
+ "required": false,
678
+ "nullable": false
679
+ },
680
+ {
681
+ "name": "sort",
682
+ "in": "query",
683
+ "type": "string",
684
+ "description": "newest: by match time, newest first. priority: by attention score, highest first. Cursors are specific to a sort.",
685
+ "enum": [
686
+ "newest",
687
+ "priority"
688
+ ],
689
+ "required": false,
690
+ "nullable": false
691
+ },
692
+ {
693
+ "name": "cursor",
694
+ "in": "query",
695
+ "type": "string",
696
+ "description": "nextCursor from the previous page; pass the same filters and sort.",
697
+ "required": false,
698
+ "nullable": false
699
+ },
700
+ {
701
+ "name": "limit",
702
+ "in": "query",
703
+ "type": "integer",
704
+ "description": "Page size, 1 to 100.",
705
+ "required": false,
706
+ "nullable": false
707
+ }
708
+ ],
709
+ "body": null,
710
+ "response": "json"
711
+ },
712
+ {
713
+ "operationId": "exportMentionsCsv",
714
+ "method": "GET",
715
+ "path": "/v1/mentions/export.csv",
716
+ "summary": "Export mentions as CSV",
717
+ "description": "The same mentions GET /v1/mentions would list for these filters, as CSV, newest published first: id, published_at, platform, keyword, author, author_url, author_followers, relevance, sentiment, intents (pipe-separated), status, relevant, delivered, url, text (first 1,000 characters). Capped at 10,000 rows; the X-Mentions-Truncated header says when the cap cut the list.",
718
+ "tag": "Mentions",
719
+ "params": [
720
+ {
721
+ "name": "keywordId",
722
+ "in": "query",
723
+ "type": "string",
724
+ "description": "Only matches of this keyword.",
725
+ "required": false,
726
+ "nullable": false
727
+ },
728
+ {
729
+ "name": "platform",
730
+ "in": "query",
731
+ "type": "string",
732
+ "description": "Only posts from this platform.",
733
+ "enum": [
734
+ "bluesky",
735
+ "hackernews",
736
+ "github",
737
+ "stackoverflow",
738
+ "devto",
739
+ "reddit",
740
+ "x",
741
+ "youtube",
742
+ "news",
743
+ "linkedin"
744
+ ],
745
+ "required": false,
746
+ "nullable": false
747
+ },
748
+ {
749
+ "name": "status",
750
+ "in": "query",
751
+ "type": "string",
752
+ "description": "Only mentions in this status. Omit for every status.",
753
+ "enum": [
754
+ "open",
755
+ "ignored",
756
+ "done"
757
+ ],
758
+ "required": false,
759
+ "nullable": false
760
+ },
761
+ {
762
+ "name": "relevant",
763
+ "in": "query",
764
+ "type": "boolean",
765
+ "description": "true: only mentions the classifier scored relevant; false: only the rest (unclassified included).",
766
+ "required": false,
767
+ "nullable": false
768
+ },
769
+ {
770
+ "name": "sentiment",
771
+ "in": "query",
772
+ "type": "string",
773
+ "description": "Only this sentiment.",
774
+ "enum": [
775
+ "positive",
776
+ "neutral",
777
+ "negative"
778
+ ],
779
+ "required": false,
780
+ "nullable": false
781
+ },
782
+ {
783
+ "name": "intent",
784
+ "in": "query",
785
+ "type": "string",
786
+ "description": "Only mentions carrying this intent (buy_intent, question, complaint, praise, comparison).",
787
+ "required": false,
788
+ "nullable": false
789
+ },
790
+ {
791
+ "name": "personId",
792
+ "in": "query",
793
+ "type": "string",
794
+ "description": "Only this person (an id from /v1/people), merged accounts included. Implies includeMuted.",
795
+ "required": false,
796
+ "nullable": false
797
+ },
798
+ {
799
+ "name": "includeMuted",
800
+ "in": "query",
801
+ "type": "boolean",
802
+ "description": "true: include mentions by people you muted, hidden by default.",
803
+ "required": false,
804
+ "nullable": false
805
+ },
806
+ {
807
+ "name": "assigneeId",
808
+ "in": "query",
809
+ "type": "string",
810
+ "description": "Only mentions assigned to this workspace member (user id).",
811
+ "required": false,
812
+ "nullable": false
813
+ },
814
+ {
815
+ "name": "snoozed",
816
+ "in": "query",
817
+ "type": "boolean",
818
+ "description": "true: only mentions currently snoozed. Otherwise snoozed mentions stay out until they wake.",
819
+ "required": false,
820
+ "nullable": false
821
+ },
822
+ {
823
+ "name": "excludeAuthors",
824
+ "in": "query",
825
+ "type": "array",
826
+ "description": "Hide these authors: display names, handles or profile URLs. Repeatable, or one comma-separated value.",
827
+ "required": false,
828
+ "nullable": false
829
+ },
830
+ {
831
+ "name": "minRelevance",
832
+ "in": "query",
833
+ "type": "integer",
834
+ "description": "Only mentions scored at least this; unclassified ones are excluded.",
835
+ "required": false,
836
+ "nullable": false
837
+ },
838
+ {
839
+ "name": "minFollowers",
840
+ "in": "query",
841
+ "type": "integer",
842
+ "description": "Only authors with at least this many followers. Unknown reach never passes.",
843
+ "required": false,
844
+ "nullable": false
845
+ },
846
+ {
847
+ "name": "tags",
848
+ "in": "query",
849
+ "type": "array",
850
+ "description": "Only authors your workspace tagged with any of these (exact, case-sensitive). Repeatable, or comma-separated.",
851
+ "required": false,
852
+ "nullable": false
853
+ },
854
+ {
855
+ "name": "q",
856
+ "in": "query",
857
+ "type": "string",
858
+ "description": "Substring search in the post text.",
859
+ "required": false,
860
+ "nullable": false
861
+ },
862
+ {
863
+ "name": "since",
864
+ "in": "query",
865
+ "type": "integer",
866
+ "description": "Only posts published at or after this instant (ISO 8601, or epoch ms).",
867
+ "required": false,
868
+ "nullable": false
869
+ },
870
+ {
871
+ "name": "until",
872
+ "in": "query",
873
+ "type": "integer",
874
+ "description": "Only posts published at or before this instant (ISO 8601, or epoch ms).",
875
+ "required": false,
876
+ "nullable": false
877
+ }
878
+ ],
879
+ "body": null,
880
+ "response": "csv"
881
+ },
882
+ {
883
+ "operationId": "exportPeopleCsv",
884
+ "method": "GET",
885
+ "path": "/v1/people/export.csv",
886
+ "summary": "Export people as CSV",
887
+ "description": "The same list as GET /v1/people (segmentId included) as CSV, one row per person with their contact columns: handle, followers, email, website, company, location, tags. Capped at 5,000 people.",
888
+ "tag": "People",
889
+ "params": [
890
+ {
891
+ "name": "platform",
892
+ "in": "query",
893
+ "type": "string",
894
+ "description": "People with an account on this platform.",
895
+ "enum": [
896
+ "bluesky",
897
+ "hackernews",
898
+ "github",
899
+ "stackoverflow",
900
+ "devto",
901
+ "reddit",
902
+ "x",
903
+ "youtube",
904
+ "news",
905
+ "linkedin"
906
+ ],
907
+ "required": false,
908
+ "nullable": false
909
+ },
910
+ {
911
+ "name": "q",
912
+ "in": "query",
913
+ "type": "string",
914
+ "description": "Matches the display name or the profile handle or URL, case-insensitively.",
915
+ "required": false,
916
+ "nullable": false
917
+ },
918
+ {
919
+ "name": "tag",
920
+ "in": "query",
921
+ "type": "string",
922
+ "description": "Only people carrying this tag (exact, case-sensitive).",
923
+ "required": false,
924
+ "nullable": false
925
+ },
926
+ {
927
+ "name": "muted",
928
+ "in": "query",
929
+ "type": "boolean",
930
+ "description": "true: only muted people; false: only unmuted; omitted: everyone.",
931
+ "required": false,
932
+ "nullable": false
933
+ },
934
+ {
935
+ "name": "since",
936
+ "in": "query",
937
+ "type": "integer",
938
+ "description": "Only people whose first matched mention is at or after this instant (ISO 8601, or epoch ms).",
939
+ "required": false,
940
+ "nullable": false
941
+ },
942
+ {
943
+ "name": "segmentId",
944
+ "in": "query",
945
+ "type": "string",
946
+ "description": "A saved segment applied on top of every other filter here. Unknown id: 404.",
947
+ "required": false,
948
+ "nullable": false
949
+ },
950
+ {
951
+ "name": "platforms",
952
+ "in": "query",
953
+ "type": "array",
954
+ "description": "People with an account on any of these platforms. Repeatable, or comma-separated.",
955
+ "enum": [
956
+ "bluesky",
957
+ "hackernews",
958
+ "github",
959
+ "stackoverflow",
960
+ "devto",
961
+ "reddit",
962
+ "x",
963
+ "youtube",
964
+ "news",
965
+ "linkedin"
966
+ ],
967
+ "required": false,
968
+ "nullable": false
969
+ },
970
+ {
971
+ "name": "tags",
972
+ "in": "query",
973
+ "type": "array",
974
+ "description": "People carrying any of these tags. Repeatable, or comma-separated.",
975
+ "required": false,
976
+ "nullable": false
977
+ },
978
+ {
979
+ "name": "minFollowers",
980
+ "in": "query",
981
+ "type": "integer",
982
+ "description": "At least this many followers. Unknown reach never matches.",
983
+ "required": false,
984
+ "nullable": false
985
+ },
986
+ {
987
+ "name": "maxFollowers",
988
+ "in": "query",
989
+ "type": "integer",
990
+ "description": "At most this many followers.",
991
+ "required": false,
992
+ "nullable": false
993
+ },
994
+ {
995
+ "name": "minMentions",
996
+ "in": "query",
997
+ "type": "integer",
998
+ "description": "At least this many matched mentions.",
999
+ "required": false,
1000
+ "nullable": false
1001
+ },
1002
+ {
1003
+ "name": "minNegative",
1004
+ "in": "query",
1005
+ "type": "integer",
1006
+ "description": "At least this many negative mentions.",
1007
+ "required": false,
1008
+ "nullable": false
1009
+ },
1010
+ {
1011
+ "name": "intents",
1012
+ "in": "query",
1013
+ "type": "array",
1014
+ "description": "At least one mention carrying any of these intents.",
1015
+ "required": false,
1016
+ "nullable": false
1017
+ },
1018
+ {
1019
+ "name": "keywordKinds",
1020
+ "in": "query",
1021
+ "type": "array",
1022
+ "description": "Mentioned a keyword of any of these kinds.",
1023
+ "enum": [
1024
+ "brand",
1025
+ "competitor",
1026
+ "topic"
1027
+ ],
1028
+ "required": false,
1029
+ "nullable": false
1030
+ },
1031
+ {
1032
+ "name": "neverKeywordKinds",
1033
+ "in": "query",
1034
+ "type": "array",
1035
+ "description": "Never mentioned a keyword of these kinds.",
1036
+ "enum": [
1037
+ "brand",
1038
+ "competitor",
1039
+ "topic"
1040
+ ],
1041
+ "required": false,
1042
+ "nullable": false
1043
+ },
1044
+ {
1045
+ "name": "newSinceDays",
1046
+ "in": "query",
1047
+ "type": "integer",
1048
+ "description": "First seen within this many days.",
1049
+ "required": false,
1050
+ "nullable": false
1051
+ },
1052
+ {
1053
+ "name": "sort",
1054
+ "in": "query",
1055
+ "type": "string",
1056
+ "description": "mentions: most matches first. recent: last seen first. reach: most followers first, unknown last. new: first seen most recently first.",
1057
+ "enum": [
1058
+ "mentions",
1059
+ "recent",
1060
+ "reach",
1061
+ "new"
1062
+ ],
1063
+ "required": false,
1064
+ "nullable": false
1065
+ }
1066
+ ],
1067
+ "body": null,
1068
+ "response": "csv"
1069
+ },
1070
+ {
1071
+ "operationId": "listPeople",
1072
+ "method": "GET",
1073
+ "path": "/v1/people",
1074
+ "summary": "List people",
1075
+ "description": "The people behind your mentions: one row per person, with their accounts, reach, public profile, per-workspace stats and your annotations. Filter by platform, tag, follower range, mention counts, intents seen, keyword kinds mentioned or never mentioned, or a saved segment. Offset-paginated with a total.",
1076
+ "tag": "People",
1077
+ "params": [
1078
+ {
1079
+ "name": "platform",
1080
+ "in": "query",
1081
+ "type": "string",
1082
+ "description": "People with an account on this platform.",
1083
+ "enum": [
1084
+ "bluesky",
1085
+ "hackernews",
1086
+ "github",
1087
+ "stackoverflow",
1088
+ "devto",
1089
+ "reddit",
1090
+ "x",
1091
+ "youtube",
1092
+ "news",
1093
+ "linkedin"
1094
+ ],
1095
+ "required": false,
1096
+ "nullable": false
1097
+ },
1098
+ {
1099
+ "name": "q",
1100
+ "in": "query",
1101
+ "type": "string",
1102
+ "description": "Matches the display name or the profile handle or URL, case-insensitively.",
1103
+ "required": false,
1104
+ "nullable": false
1105
+ },
1106
+ {
1107
+ "name": "tag",
1108
+ "in": "query",
1109
+ "type": "string",
1110
+ "description": "Only people carrying this tag (exact, case-sensitive).",
1111
+ "required": false,
1112
+ "nullable": false
1113
+ },
1114
+ {
1115
+ "name": "muted",
1116
+ "in": "query",
1117
+ "type": "boolean",
1118
+ "description": "true: only muted people; false: only unmuted; omitted: everyone.",
1119
+ "required": false,
1120
+ "nullable": false
1121
+ },
1122
+ {
1123
+ "name": "since",
1124
+ "in": "query",
1125
+ "type": "integer",
1126
+ "description": "Only people whose first matched mention is at or after this instant (ISO 8601, or epoch ms).",
1127
+ "required": false,
1128
+ "nullable": false
1129
+ },
1130
+ {
1131
+ "name": "segmentId",
1132
+ "in": "query",
1133
+ "type": "string",
1134
+ "description": "A saved segment applied on top of every other filter here. Unknown id: 404.",
1135
+ "required": false,
1136
+ "nullable": false
1137
+ },
1138
+ {
1139
+ "name": "platforms",
1140
+ "in": "query",
1141
+ "type": "array",
1142
+ "description": "People with an account on any of these platforms. Repeatable, or comma-separated.",
1143
+ "enum": [
1144
+ "bluesky",
1145
+ "hackernews",
1146
+ "github",
1147
+ "stackoverflow",
1148
+ "devto",
1149
+ "reddit",
1150
+ "x",
1151
+ "youtube",
1152
+ "news",
1153
+ "linkedin"
1154
+ ],
1155
+ "required": false,
1156
+ "nullable": false
1157
+ },
1158
+ {
1159
+ "name": "tags",
1160
+ "in": "query",
1161
+ "type": "array",
1162
+ "description": "People carrying any of these tags. Repeatable, or comma-separated.",
1163
+ "required": false,
1164
+ "nullable": false
1165
+ },
1166
+ {
1167
+ "name": "minFollowers",
1168
+ "in": "query",
1169
+ "type": "integer",
1170
+ "description": "At least this many followers. Unknown reach never matches.",
1171
+ "required": false,
1172
+ "nullable": false
1173
+ },
1174
+ {
1175
+ "name": "maxFollowers",
1176
+ "in": "query",
1177
+ "type": "integer",
1178
+ "description": "At most this many followers.",
1179
+ "required": false,
1180
+ "nullable": false
1181
+ },
1182
+ {
1183
+ "name": "minMentions",
1184
+ "in": "query",
1185
+ "type": "integer",
1186
+ "description": "At least this many matched mentions.",
1187
+ "required": false,
1188
+ "nullable": false
1189
+ },
1190
+ {
1191
+ "name": "minNegative",
1192
+ "in": "query",
1193
+ "type": "integer",
1194
+ "description": "At least this many negative mentions.",
1195
+ "required": false,
1196
+ "nullable": false
1197
+ },
1198
+ {
1199
+ "name": "intents",
1200
+ "in": "query",
1201
+ "type": "array",
1202
+ "description": "At least one mention carrying any of these intents.",
1203
+ "required": false,
1204
+ "nullable": false
1205
+ },
1206
+ {
1207
+ "name": "keywordKinds",
1208
+ "in": "query",
1209
+ "type": "array",
1210
+ "description": "Mentioned a keyword of any of these kinds.",
1211
+ "enum": [
1212
+ "brand",
1213
+ "competitor",
1214
+ "topic"
1215
+ ],
1216
+ "required": false,
1217
+ "nullable": false
1218
+ },
1219
+ {
1220
+ "name": "neverKeywordKinds",
1221
+ "in": "query",
1222
+ "type": "array",
1223
+ "description": "Never mentioned a keyword of these kinds.",
1224
+ "enum": [
1225
+ "brand",
1226
+ "competitor",
1227
+ "topic"
1228
+ ],
1229
+ "required": false,
1230
+ "nullable": false
1231
+ },
1232
+ {
1233
+ "name": "newSinceDays",
1234
+ "in": "query",
1235
+ "type": "integer",
1236
+ "description": "First seen within this many days.",
1237
+ "required": false,
1238
+ "nullable": false
1239
+ },
1240
+ {
1241
+ "name": "sort",
1242
+ "in": "query",
1243
+ "type": "string",
1244
+ "description": "mentions: most matches first. recent: last seen first. reach: most followers first, unknown last. new: first seen most recently first.",
1245
+ "enum": [
1246
+ "mentions",
1247
+ "recent",
1248
+ "reach",
1249
+ "new"
1250
+ ],
1251
+ "required": false,
1252
+ "nullable": false
1253
+ },
1254
+ {
1255
+ "name": "limit",
1256
+ "in": "query",
1257
+ "type": "integer",
1258
+ "description": "Page size, 1 to 100.",
1259
+ "required": false,
1260
+ "nullable": false
1261
+ },
1262
+ {
1263
+ "name": "offset",
1264
+ "in": "query",
1265
+ "type": "integer",
1266
+ "description": "Skip this many people. Offset paging: a grouped read over hundreds of people, not a stream.",
1267
+ "required": false,
1268
+ "nullable": false
1269
+ }
1270
+ ],
1271
+ "body": null,
1272
+ "response": "json"
1273
+ },
1274
+ {
1275
+ "operationId": "getPerson",
1276
+ "method": "GET",
1277
+ "path": "/v1/people/{id}",
1278
+ "summary": "Get a person",
1279
+ "description": "One person as your workspace sees them. An account merged into someone resolves to that person.",
1280
+ "tag": "People",
1281
+ "params": [
1282
+ {
1283
+ "name": "id",
1284
+ "in": "path",
1285
+ "type": "string",
1286
+ "description": "Person id (aut_...).",
1287
+ "required": true,
1288
+ "nullable": false
1289
+ }
1290
+ ],
1291
+ "body": null,
1292
+ "response": "json"
1293
+ },
1294
+ {
1295
+ "operationId": "updatePerson",
1296
+ "method": "PATCH",
1297
+ "path": "/v1/people/{id}",
1298
+ "summary": "Update your annotations on a person",
1299
+ "description": "Tags, notes and mute, for your workspace only. Mute hides their posts from your feed and every channel; ingest and billing never change.",
1300
+ "tag": "People",
1301
+ "params": [
1302
+ {
1303
+ "name": "id",
1304
+ "in": "path",
1305
+ "type": "string",
1306
+ "description": "Person id (aut_...).",
1307
+ "required": true,
1308
+ "nullable": false
1309
+ }
1310
+ ],
1311
+ "body": {
1312
+ "description": "Omitted fields are untouched.",
1313
+ "fields": [
1314
+ {
1315
+ "name": "tags",
1316
+ "type": "array",
1317
+ "description": "Replaces the whole list.",
1318
+ "required": false,
1319
+ "nullable": false,
1320
+ "items": "string"
1321
+ },
1322
+ {
1323
+ "name": "notes",
1324
+ "type": "string",
1325
+ "required": false,
1326
+ "nullable": false
1327
+ },
1328
+ {
1329
+ "name": "muted",
1330
+ "type": "boolean",
1331
+ "required": false,
1332
+ "nullable": false
1333
+ }
1334
+ ]
1335
+ },
1336
+ "response": "json"
1337
+ },
1338
+ {
1339
+ "operationId": "mergePeople",
1340
+ "method": "POST",
1341
+ "path": "/v1/people/{id}/merge",
1342
+ "summary": "Merge an account into a person",
1343
+ "description": "Declare that this account and another person are the same human, for your workspace only. Their mentions, tags and notes combine under the person named by `into`.",
1344
+ "tag": "People",
1345
+ "params": [
1346
+ {
1347
+ "name": "id",
1348
+ "in": "path",
1349
+ "type": "string",
1350
+ "description": "Person id (aut_...).",
1351
+ "required": true,
1352
+ "nullable": false
1353
+ }
1354
+ ],
1355
+ "body": {
1356
+ "fields": [
1357
+ {
1358
+ "name": "into",
1359
+ "type": "string",
1360
+ "description": "The person to fold this account into (their id).",
1361
+ "required": true,
1362
+ "nullable": false
1363
+ }
1364
+ ]
1365
+ },
1366
+ "response": "json"
1367
+ },
1368
+ {
1369
+ "operationId": "splitPerson",
1370
+ "method": "POST",
1371
+ "path": "/v1/people/{id}/split",
1372
+ "summary": "Undo a merge",
1373
+ "description": "The account becomes its own person again.",
1374
+ "tag": "People",
1375
+ "params": [
1376
+ {
1377
+ "name": "id",
1378
+ "in": "path",
1379
+ "type": "string",
1380
+ "description": "Person id (aut_...).",
1381
+ "required": true,
1382
+ "nullable": false
1383
+ }
1384
+ ],
1385
+ "body": null,
1386
+ "response": "json"
1387
+ },
1388
+ {
1389
+ "operationId": "listSegments",
1390
+ "method": "GET",
1391
+ "path": "/v1/segments",
1392
+ "summary": "List segments",
1393
+ "description": "Your saved segments, each with the number of people in it right now (segments are evaluated on every read, never materialized), plus presets you can save as a starting point. Pass a segment id to GET /v1/people to list its members.",
1394
+ "tag": "Segments",
1395
+ "params": [],
1396
+ "body": null,
1397
+ "response": "json"
1398
+ },
1399
+ {
1400
+ "operationId": "createSegment",
1401
+ "method": "POST",
1402
+ "path": "/v1/segments",
1403
+ "summary": "Create a segment",
1404
+ "tag": "Segments",
1405
+ "params": [],
1406
+ "body": {
1407
+ "fields": [
1408
+ {
1409
+ "name": "name",
1410
+ "type": "string",
1411
+ "required": true,
1412
+ "nullable": false
1413
+ },
1414
+ {
1415
+ "name": "description",
1416
+ "type": "string",
1417
+ "required": false,
1418
+ "nullable": false
1419
+ },
1420
+ {
1421
+ "name": "filter",
1422
+ "type": "object",
1423
+ "required": false,
1424
+ "nullable": false
1425
+ }
1426
+ ]
1427
+ },
1428
+ "response": "json"
1429
+ },
1430
+ {
1431
+ "operationId": "getSegment",
1432
+ "method": "GET",
1433
+ "path": "/v1/segments/{id}",
1434
+ "summary": "Get a segment",
1435
+ "tag": "Segments",
1436
+ "params": [
1437
+ {
1438
+ "name": "id",
1439
+ "in": "path",
1440
+ "type": "string",
1441
+ "description": "Segment id (seg_...).",
1442
+ "required": true,
1443
+ "nullable": false
1444
+ }
1445
+ ],
1446
+ "body": null,
1447
+ "response": "json"
1448
+ },
1449
+ {
1450
+ "operationId": "updateSegment",
1451
+ "method": "PATCH",
1452
+ "path": "/v1/segments/{id}",
1453
+ "summary": "Update a segment",
1454
+ "tag": "Segments",
1455
+ "params": [
1456
+ {
1457
+ "name": "id",
1458
+ "in": "path",
1459
+ "type": "string",
1460
+ "description": "Segment id (seg_...).",
1461
+ "required": true,
1462
+ "nullable": false
1463
+ }
1464
+ ],
1465
+ "body": {
1466
+ "description": "Omitted fields are untouched.",
1467
+ "fields": [
1468
+ {
1469
+ "name": "name",
1470
+ "type": "string",
1471
+ "required": false,
1472
+ "nullable": false
1473
+ },
1474
+ {
1475
+ "name": "description",
1476
+ "type": "string",
1477
+ "required": false,
1478
+ "nullable": false
1479
+ },
1480
+ {
1481
+ "name": "filter",
1482
+ "type": "object",
1483
+ "description": "Replaces the whole filter.",
1484
+ "required": false,
1485
+ "nullable": false
1486
+ }
1487
+ ]
1488
+ },
1489
+ "response": "json"
1490
+ },
1491
+ {
1492
+ "operationId": "deleteSegment",
1493
+ "method": "DELETE",
1494
+ "path": "/v1/segments/{id}",
1495
+ "summary": "Delete a segment",
1496
+ "description": "Nobody in it is affected.",
1497
+ "tag": "Segments",
1498
+ "params": [
1499
+ {
1500
+ "name": "id",
1501
+ "in": "path",
1502
+ "type": "string",
1503
+ "description": "Segment id (seg_...).",
1504
+ "required": true,
1505
+ "nullable": false
1506
+ }
1507
+ ],
1508
+ "body": null,
1509
+ "response": "none"
1510
+ },
1511
+ {
1512
+ "operationId": "getCompany",
1513
+ "method": "GET",
1514
+ "path": "/v1/company",
1515
+ "summary": "Get the company profile",
1516
+ "description": "What the classifier knows about you: name, description, use cases, your own accounts, and the composed context it reads.",
1517
+ "tag": "Company",
1518
+ "params": [],
1519
+ "body": null,
1520
+ "response": "json"
1521
+ },
1522
+ {
1523
+ "operationId": "updateCompany",
1524
+ "method": "PATCH",
1525
+ "path": "/v1/company",
1526
+ "summary": "Update the company profile",
1527
+ "description": "Changing profile fields recomposes the classifier context; setting `context` directly overrides it until the next profile edit. Relevance scores for new mentions follow at once.",
1528
+ "tag": "Company",
1529
+ "params": [],
1530
+ "body": {
1531
+ "description": "Omitted fields are untouched.",
1532
+ "fields": [
1533
+ {
1534
+ "name": "name",
1535
+ "type": "string",
1536
+ "required": false,
1537
+ "nullable": false
1538
+ },
1539
+ {
1540
+ "name": "description",
1541
+ "type": "string",
1542
+ "required": false,
1543
+ "nullable": false
1544
+ },
1545
+ {
1546
+ "name": "useCases",
1547
+ "type": "array",
1548
+ "description": "Replaces the whole list.",
1549
+ "required": false,
1550
+ "nullable": false,
1551
+ "items": "string"
1552
+ },
1553
+ {
1554
+ "name": "accounts",
1555
+ "type": "object",
1556
+ "required": false,
1557
+ "nullable": false
1558
+ },
1559
+ {
1560
+ "name": "context",
1561
+ "type": "string",
1562
+ "description": "Overrides the composed context until the next profile edit.",
1563
+ "required": false,
1564
+ "nullable": false
1565
+ }
1566
+ ]
1567
+ },
1568
+ "response": "json"
1569
+ },
1570
+ {
1571
+ "operationId": "createApiKey",
1572
+ "method": "POST",
1573
+ "path": "/v1/api-keys",
1574
+ "summary": "Create an API key",
1575
+ "description": "Mint a key for this workspace. The key itself is returned once; only its hash is stored.",
1576
+ "tag": "API keys",
1577
+ "params": [],
1578
+ "body": {
1579
+ "fields": [
1580
+ {
1581
+ "name": "name",
1582
+ "type": "string",
1583
+ "description": 'A label for the key; "default" when omitted.',
1584
+ "required": false,
1585
+ "nullable": false
1586
+ },
1587
+ {
1588
+ "name": "scope",
1589
+ "type": "string",
1590
+ "description": "read: GET only. write: everything.",
1591
+ "enum": [
1592
+ "read",
1593
+ "write"
1594
+ ],
1595
+ "required": false,
1596
+ "nullable": false
1597
+ }
1598
+ ]
1599
+ },
1600
+ "response": "json"
1601
+ },
1602
+ {
1603
+ "operationId": "listApiKeys",
1604
+ "method": "GET",
1605
+ "path": "/v1/api-keys",
1606
+ "summary": "List API keys",
1607
+ "tag": "API keys",
1608
+ "params": [],
1609
+ "body": null,
1610
+ "response": "json"
1611
+ },
1612
+ {
1613
+ "operationId": "revokeApiKey",
1614
+ "method": "DELETE",
1615
+ "path": "/v1/api-keys/{id}",
1616
+ "summary": "Revoke an API key",
1617
+ "description": "Takes effect at once on the API and within a few minutes on cached verifications.",
1618
+ "tag": "API keys",
1619
+ "params": [
1620
+ {
1621
+ "name": "id",
1622
+ "in": "path",
1623
+ "type": "string",
1624
+ "description": "API key id (key_...).",
1625
+ "required": true,
1626
+ "nullable": false
1627
+ }
1628
+ ],
1629
+ "body": null,
1630
+ "response": "none"
1631
+ },
1632
+ {
1633
+ "operationId": "getAlert",
1634
+ "method": "GET",
1635
+ "path": "/v1/alerts/{id}",
1636
+ "summary": "Get an alert",
1637
+ "tag": "Alerts",
1638
+ "params": [
1639
+ {
1640
+ "name": "id",
1641
+ "in": "path",
1642
+ "type": "string",
1643
+ "description": "Alert id (feed_...).",
1644
+ "required": true,
1645
+ "nullable": false
1646
+ }
1647
+ ],
1648
+ "body": null,
1649
+ "response": "json"
1650
+ },
1651
+ {
1652
+ "operationId": "updateAlert",
1653
+ "method": "PATCH",
1654
+ "path": "/v1/alerts/{id}",
1655
+ "summary": "Update an alert",
1656
+ "tag": "Alerts",
1657
+ "params": [
1658
+ {
1659
+ "name": "id",
1660
+ "in": "path",
1661
+ "type": "string",
1662
+ "description": "Alert id (feed_...).",
1663
+ "required": true,
1664
+ "nullable": false
1665
+ }
1666
+ ],
1667
+ "body": {
1668
+ "description": "Omitted fields are untouched.",
1669
+ "fields": [
1670
+ {
1671
+ "name": "name",
1672
+ "type": "string",
1673
+ "required": false,
1674
+ "nullable": false
1675
+ },
1676
+ {
1677
+ "name": "enabled",
1678
+ "type": "boolean",
1679
+ "required": false,
1680
+ "nullable": false
1681
+ },
1682
+ {
1683
+ "name": "mode",
1684
+ "type": "string",
1685
+ "enum": [
1686
+ "instant",
1687
+ "daily"
1688
+ ],
1689
+ "required": false,
1690
+ "nullable": false
1691
+ },
1692
+ {
1693
+ "name": "filter",
1694
+ "type": "object",
1695
+ "description": "Replaces the whole filter.",
1696
+ "required": false,
1697
+ "nullable": false
1698
+ },
1699
+ {
1700
+ "name": "schedule",
1701
+ "type": "object",
1702
+ "required": false,
1703
+ "nullable": true
1704
+ },
1705
+ {
1706
+ "name": "event",
1707
+ "type": "string",
1708
+ "required": false,
1709
+ "nullable": true
1710
+ },
1711
+ {
1712
+ "name": "channelIds",
1713
+ "type": "array",
1714
+ "description": "Replaces the whole list.",
1715
+ "required": false,
1716
+ "nullable": false,
1717
+ "items": "string"
1718
+ }
1719
+ ]
1720
+ },
1721
+ "response": "json"
1722
+ },
1723
+ {
1724
+ "operationId": "deleteAlert",
1725
+ "method": "DELETE",
1726
+ "path": "/v1/alerts/{id}",
1727
+ "summary": "Delete an alert",
1728
+ "tag": "Alerts",
1729
+ "params": [
1730
+ {
1731
+ "name": "id",
1732
+ "in": "path",
1733
+ "type": "string",
1734
+ "description": "Alert id (feed_...).",
1735
+ "required": true,
1736
+ "nullable": false
1737
+ }
1738
+ ],
1739
+ "body": null,
1740
+ "response": "none"
1741
+ },
1742
+ {
1743
+ "operationId": "listAlerts",
1744
+ "method": "GET",
1745
+ "path": "/v1/alerts",
1746
+ "summary": "List alerts",
1747
+ "tag": "Alerts",
1748
+ "params": [],
1749
+ "body": null,
1750
+ "response": "json"
1751
+ },
1752
+ {
1753
+ "operationId": "createAlert",
1754
+ "method": "POST",
1755
+ "path": "/v1/alerts",
1756
+ "summary": "Create an alert",
1757
+ "tag": "Alerts",
1758
+ "params": [],
1759
+ "body": {
1760
+ "fields": [
1761
+ {
1762
+ "name": "name",
1763
+ "type": "string",
1764
+ "required": true,
1765
+ "nullable": false
1766
+ },
1767
+ {
1768
+ "name": "enabled",
1769
+ "type": "boolean",
1770
+ "required": false,
1771
+ "nullable": false
1772
+ },
1773
+ {
1774
+ "name": "mode",
1775
+ "type": "string",
1776
+ "enum": [
1777
+ "instant",
1778
+ "daily"
1779
+ ],
1780
+ "required": false,
1781
+ "nullable": false
1782
+ },
1783
+ {
1784
+ "name": "filter",
1785
+ "type": "object",
1786
+ "required": false,
1787
+ "nullable": false
1788
+ },
1789
+ {
1790
+ "name": "schedule",
1791
+ "type": "object",
1792
+ "description": "Required for daily alerts.",
1793
+ "required": false,
1794
+ "nullable": false
1795
+ },
1796
+ {
1797
+ "name": "event",
1798
+ "type": "string",
1799
+ "description": "Custom event name for webhook payloads; null for the mode default.",
1800
+ "required": false,
1801
+ "nullable": true
1802
+ },
1803
+ {
1804
+ "name": "channelIds",
1805
+ "type": "array",
1806
+ "description": "Channel ids from GET /v1/channels.",
1807
+ "required": false,
1808
+ "nullable": false,
1809
+ "items": "string"
1810
+ }
1811
+ ]
1812
+ },
1813
+ "response": "json"
1814
+ },
1815
+ {
1816
+ "operationId": "testAlert",
1817
+ "method": "POST",
1818
+ "path": "/v1/alerts/{id}/test",
1819
+ "summary": "Send a test through an alert's channels",
1820
+ "tag": "Alerts",
1821
+ "params": [
1822
+ {
1823
+ "name": "id",
1824
+ "in": "path",
1825
+ "type": "string",
1826
+ "description": "Alert id (feed_...).",
1827
+ "required": true,
1828
+ "nullable": false
1829
+ }
1830
+ ],
1831
+ "body": null,
1832
+ "response": "json"
1833
+ },
1834
+ {
1835
+ "operationId": "runAlertDigest",
1836
+ "method": "POST",
1837
+ "path": "/v1/alerts/{id}/run",
1838
+ "summary": "Send a digest now",
1839
+ "tag": "Alerts",
1840
+ "params": [
1841
+ {
1842
+ "name": "id",
1843
+ "in": "path",
1844
+ "type": "string",
1845
+ "description": "Alert id (feed_...).",
1846
+ "required": true,
1847
+ "nullable": false
1848
+ }
1849
+ ],
1850
+ "body": null,
1851
+ "response": "json"
1852
+ },
1853
+ {
1854
+ "operationId": "getAnalyticsSummary",
1855
+ "method": "GET",
1856
+ "path": "/v1/analytics/summary",
1857
+ "summary": "Headline counts for a window",
1858
+ "description": "Matched and relevant mentions, distinct posts and people, sentiment, buying intent and questions, estimated reach, and where the matches stand in triage. The window is `range` (7d, 30d, 90d, 365d, ending today) or `from` and `to`, cut into days in `timezone` (UTC by default); `keywordIds` and `platforms` narrow it; `compare=true` adds the period of the same length right before it. Time axis is the publish date.",
1859
+ "tag": "Analytics",
1860
+ "params": [
1861
+ {
1862
+ "name": "range",
1863
+ "in": "query",
1864
+ "type": "string",
1865
+ "description": "Preset window ending today. Ignored when from or to is given. Default 30d.",
1866
+ "enum": [
1867
+ "7d",
1868
+ "30d",
1869
+ "90d",
1870
+ "365d"
1871
+ ],
1872
+ "required": false,
1873
+ "nullable": false
1874
+ },
1875
+ {
1876
+ "name": "from",
1877
+ "in": "query",
1878
+ "type": "string",
1879
+ "description": "First day, YYYY-MM-DD, inclusive, in `timezone`.",
1880
+ "required": false,
1881
+ "nullable": false
1882
+ },
1883
+ {
1884
+ "name": "to",
1885
+ "in": "query",
1886
+ "type": "string",
1887
+ "description": "Last day, YYYY-MM-DD, inclusive, in `timezone`. Default today.",
1888
+ "required": false,
1889
+ "nullable": false
1890
+ },
1891
+ {
1892
+ "name": "keywordIds",
1893
+ "in": "query",
1894
+ "type": "string",
1895
+ "description": "Comma-separated keyword ids; omit for every keyword.",
1896
+ "required": false,
1897
+ "nullable": false
1898
+ },
1899
+ {
1900
+ "name": "platforms",
1901
+ "in": "query",
1902
+ "type": "string",
1903
+ "description": "Comma-separated platforms (bluesky, hackernews, github, ...); omit for every platform.",
1904
+ "required": false,
1905
+ "nullable": false
1906
+ },
1907
+ {
1908
+ "name": "compare",
1909
+ "in": "query",
1910
+ "type": "string",
1911
+ "description": "true adds the period of the same length right before the window as `previous`.",
1912
+ "enum": [
1913
+ "true",
1914
+ "false"
1915
+ ],
1916
+ "required": false,
1917
+ "nullable": false
1918
+ },
1919
+ {
1920
+ "name": "timezone",
1921
+ "in": "query",
1922
+ "type": "string",
1923
+ "description": "IANA zone the days are cut in (Europe/Madrid). Default UTC. One offset, the zone's at the end of the window, applies to the whole window.",
1924
+ "required": false,
1925
+ "nullable": false
1926
+ }
1927
+ ],
1928
+ "body": null,
1929
+ "response": "json"
1930
+ },
1931
+ {
1932
+ "operationId": "getAnalyticsSeries",
1933
+ "method": "GET",
1934
+ "path": "/v1/analytics/series",
1935
+ "summary": "Mentions over time",
1936
+ "description": "Matched, relevant and sentiment counts per day or week across the window, as one total series or split per platform or per keyword with `by`. The window is `range` (7d, 30d, 90d, 365d, ending today) or `from` and `to`, cut into days in `timezone` (UTC by default); `keywordIds` and `platforms` narrow it; `compare=true` adds the period of the same length right before it. Time axis is the publish date.",
1937
+ "tag": "Analytics",
1938
+ "params": [
1939
+ {
1940
+ "name": "range",
1941
+ "in": "query",
1942
+ "type": "string",
1943
+ "description": "Preset window ending today. Ignored when from or to is given. Default 30d.",
1944
+ "enum": [
1945
+ "7d",
1946
+ "30d",
1947
+ "90d",
1948
+ "365d"
1949
+ ],
1950
+ "required": false,
1951
+ "nullable": false
1952
+ },
1953
+ {
1954
+ "name": "from",
1955
+ "in": "query",
1956
+ "type": "string",
1957
+ "description": "First day, YYYY-MM-DD, inclusive, in `timezone`.",
1958
+ "required": false,
1959
+ "nullable": false
1960
+ },
1961
+ {
1962
+ "name": "to",
1963
+ "in": "query",
1964
+ "type": "string",
1965
+ "description": "Last day, YYYY-MM-DD, inclusive, in `timezone`. Default today.",
1966
+ "required": false,
1967
+ "nullable": false
1968
+ },
1969
+ {
1970
+ "name": "keywordIds",
1971
+ "in": "query",
1972
+ "type": "string",
1973
+ "description": "Comma-separated keyword ids; omit for every keyword.",
1974
+ "required": false,
1975
+ "nullable": false
1976
+ },
1977
+ {
1978
+ "name": "platforms",
1979
+ "in": "query",
1980
+ "type": "string",
1981
+ "description": "Comma-separated platforms (bluesky, hackernews, github, ...); omit for every platform.",
1982
+ "required": false,
1983
+ "nullable": false
1984
+ },
1985
+ {
1986
+ "name": "compare",
1987
+ "in": "query",
1988
+ "type": "string",
1989
+ "description": "true adds the period of the same length right before the window as `previous`.",
1990
+ "enum": [
1991
+ "true",
1992
+ "false"
1993
+ ],
1994
+ "required": false,
1995
+ "nullable": false
1996
+ },
1997
+ {
1998
+ "name": "timezone",
1999
+ "in": "query",
2000
+ "type": "string",
2001
+ "description": "IANA zone the days are cut in (Europe/Madrid). Default UTC. One offset, the zone's at the end of the window, applies to the whole window.",
2002
+ "required": false,
2003
+ "nullable": false
2004
+ },
2005
+ {
2006
+ "name": "bucket",
2007
+ "in": "query",
2008
+ "type": "string",
2009
+ "description": "Point granularity. Default: day up to 90 days, week beyond. Weeks start on Monday.",
2010
+ "enum": [
2011
+ "day",
2012
+ "week"
2013
+ ],
2014
+ "required": false,
2015
+ "nullable": false
2016
+ },
2017
+ {
2018
+ "name": "by",
2019
+ "in": "query",
2020
+ "type": "string",
2021
+ "description": 'Split into one series per platform or per keyword (the top 20 by matched, the rest folded into "other"). Omit for one total series.',
2022
+ "enum": [
2023
+ "platform",
2024
+ "keyword"
2025
+ ],
2026
+ "required": false,
2027
+ "nullable": false
2028
+ }
2029
+ ],
2030
+ "body": null,
2031
+ "response": "json"
2032
+ },
2033
+ {
2034
+ "operationId": "getAnalyticsBreakdown",
2035
+ "method": "GET",
2036
+ "path": "/v1/analytics/breakdown",
2037
+ "summary": "Mentions grouped by one dimension",
2038
+ "description": "One table of matched, relevant and sentiment counts grouped by `by`: platform, keyword, sentiment, intent, status, hour (weekday and hour of day) or person. The window is `range` (7d, 30d, 90d, 365d, ending today) or `from` and `to`, cut into days in `timezone` (UTC by default); `keywordIds` and `platforms` narrow it; `compare=true` adds the period of the same length right before it. Time axis is the publish date.",
2039
+ "tag": "Analytics",
2040
+ "params": [
2041
+ {
2042
+ "name": "range",
2043
+ "in": "query",
2044
+ "type": "string",
2045
+ "description": "Preset window ending today. Ignored when from or to is given. Default 30d.",
2046
+ "enum": [
2047
+ "7d",
2048
+ "30d",
2049
+ "90d",
2050
+ "365d"
2051
+ ],
2052
+ "required": false,
2053
+ "nullable": false
2054
+ },
2055
+ {
2056
+ "name": "from",
2057
+ "in": "query",
2058
+ "type": "string",
2059
+ "description": "First day, YYYY-MM-DD, inclusive, in `timezone`.",
2060
+ "required": false,
2061
+ "nullable": false
2062
+ },
2063
+ {
2064
+ "name": "to",
2065
+ "in": "query",
2066
+ "type": "string",
2067
+ "description": "Last day, YYYY-MM-DD, inclusive, in `timezone`. Default today.",
2068
+ "required": false,
2069
+ "nullable": false
2070
+ },
2071
+ {
2072
+ "name": "keywordIds",
2073
+ "in": "query",
2074
+ "type": "string",
2075
+ "description": "Comma-separated keyword ids; omit for every keyword.",
2076
+ "required": false,
2077
+ "nullable": false
2078
+ },
2079
+ {
2080
+ "name": "platforms",
2081
+ "in": "query",
2082
+ "type": "string",
2083
+ "description": "Comma-separated platforms (bluesky, hackernews, github, ...); omit for every platform.",
2084
+ "required": false,
2085
+ "nullable": false
2086
+ },
2087
+ {
2088
+ "name": "compare",
2089
+ "in": "query",
2090
+ "type": "string",
2091
+ "description": "true adds the period of the same length right before the window as `previous`.",
2092
+ "enum": [
2093
+ "true",
2094
+ "false"
2095
+ ],
2096
+ "required": false,
2097
+ "nullable": false
2098
+ },
2099
+ {
2100
+ "name": "timezone",
2101
+ "in": "query",
2102
+ "type": "string",
2103
+ "description": "IANA zone the days are cut in (Europe/Madrid). Default UTC. One offset, the zone's at the end of the window, applies to the whole window.",
2104
+ "required": false,
2105
+ "nullable": false
2106
+ },
2107
+ {
2108
+ "name": "by",
2109
+ "in": "query",
2110
+ "type": "string",
2111
+ "description": "The dimension to group by: platform, keyword, sentiment (unclassified included), intent (a mention can carry several), status (open, ignored, done), hour (weekday and hour of day in `timezone`), person (who posted; anonymous posts are left out).",
2112
+ "enum": [
2113
+ "platform",
2114
+ "keyword",
2115
+ "sentiment",
2116
+ "intent",
2117
+ "status",
2118
+ "hour",
2119
+ "person"
2120
+ ],
2121
+ "required": true,
2122
+ "nullable": false
2123
+ }
2124
+ ],
2125
+ "body": null,
2126
+ "response": "json"
2127
+ },
2128
+ {
2129
+ "operationId": "getShareOfVoice",
2130
+ "method": "GET",
2131
+ "path": "/v1/analytics/share-of-voice",
2132
+ "summary": "Brand against competitors",
2133
+ "description": "Every keyword matched in the window with its counts and its share of brand plus competitor matches; topic keywords are counted but stay out of the split. The window is `range` (7d, 30d, 90d, 365d, ending today) or `from` and `to`, cut into days in `timezone` (UTC by default); `keywordIds` and `platforms` narrow it; `compare=true` adds the period of the same length right before it. Time axis is the publish date.",
2134
+ "tag": "Analytics",
2135
+ "params": [
2136
+ {
2137
+ "name": "range",
2138
+ "in": "query",
2139
+ "type": "string",
2140
+ "description": "Preset window ending today. Ignored when from or to is given. Default 30d.",
2141
+ "enum": [
2142
+ "7d",
2143
+ "30d",
2144
+ "90d",
2145
+ "365d"
2146
+ ],
2147
+ "required": false,
2148
+ "nullable": false
2149
+ },
2150
+ {
2151
+ "name": "from",
2152
+ "in": "query",
2153
+ "type": "string",
2154
+ "description": "First day, YYYY-MM-DD, inclusive, in `timezone`.",
2155
+ "required": false,
2156
+ "nullable": false
2157
+ },
2158
+ {
2159
+ "name": "to",
2160
+ "in": "query",
2161
+ "type": "string",
2162
+ "description": "Last day, YYYY-MM-DD, inclusive, in `timezone`. Default today.",
2163
+ "required": false,
2164
+ "nullable": false
2165
+ },
2166
+ {
2167
+ "name": "keywordIds",
2168
+ "in": "query",
2169
+ "type": "string",
2170
+ "description": "Comma-separated keyword ids; omit for every keyword.",
2171
+ "required": false,
2172
+ "nullable": false
2173
+ },
2174
+ {
2175
+ "name": "platforms",
2176
+ "in": "query",
2177
+ "type": "string",
2178
+ "description": "Comma-separated platforms (bluesky, hackernews, github, ...); omit for every platform.",
2179
+ "required": false,
2180
+ "nullable": false
2181
+ },
2182
+ {
2183
+ "name": "compare",
2184
+ "in": "query",
2185
+ "type": "string",
2186
+ "description": "true adds the period of the same length right before the window as `previous`.",
2187
+ "enum": [
2188
+ "true",
2189
+ "false"
2190
+ ],
2191
+ "required": false,
2192
+ "nullable": false
2193
+ },
2194
+ {
2195
+ "name": "timezone",
2196
+ "in": "query",
2197
+ "type": "string",
2198
+ "description": "IANA zone the days are cut in (Europe/Madrid). Default UTC. One offset, the zone's at the end of the window, applies to the whole window.",
2199
+ "required": false,
2200
+ "nullable": false
2201
+ }
2202
+ ],
2203
+ "body": null,
2204
+ "response": "json"
2205
+ },
2206
+ {
2207
+ "operationId": "getChannel",
2208
+ "method": "GET",
2209
+ "path": "/v1/channels/{id}",
2210
+ "summary": "Get a channel",
2211
+ "tag": "Alerts",
2212
+ "params": [
2213
+ {
2214
+ "name": "id",
2215
+ "in": "path",
2216
+ "type": "string",
2217
+ "description": "Channel id (dest_...).",
2218
+ "required": true,
2219
+ "nullable": false
2220
+ }
2221
+ ],
2222
+ "body": null,
2223
+ "response": "json"
2224
+ },
2225
+ {
2226
+ "operationId": "updateChannel",
2227
+ "method": "PATCH",
2228
+ "path": "/v1/channels/{id}",
2229
+ "summary": "Update a channel",
2230
+ "tag": "Alerts",
2231
+ "params": [
2232
+ {
2233
+ "name": "id",
2234
+ "in": "path",
2235
+ "type": "string",
2236
+ "description": "Channel id (dest_...).",
2237
+ "required": true,
2238
+ "nullable": false
2239
+ }
2240
+ ],
2241
+ "body": {
2242
+ "description": "Omitted fields are untouched.",
2243
+ "fields": [
2244
+ {
2245
+ "name": "label",
2246
+ "type": "string",
2247
+ "required": false,
2248
+ "nullable": false
2249
+ },
2250
+ {
2251
+ "name": "url",
2252
+ "type": "string",
2253
+ "description": "Webhooks only.",
2254
+ "required": false,
2255
+ "nullable": false
2256
+ },
2257
+ {
2258
+ "name": "headers",
2259
+ "type": "object",
2260
+ "description": "Webhooks only; replaces the whole set.",
2261
+ "required": false,
2262
+ "nullable": false
2263
+ }
2264
+ ]
2265
+ },
2266
+ "response": "json"
2267
+ },
2268
+ {
2269
+ "operationId": "deleteChannel",
2270
+ "method": "DELETE",
2271
+ "path": "/v1/channels/{id}",
2272
+ "summary": "Delete a channel",
2273
+ "tag": "Alerts",
2274
+ "params": [
2275
+ {
2276
+ "name": "id",
2277
+ "in": "path",
2278
+ "type": "string",
2279
+ "description": "Channel id (dest_...).",
2280
+ "required": true,
2281
+ "nullable": false
2282
+ }
2283
+ ],
2284
+ "body": null,
2285
+ "response": "none"
2286
+ },
2287
+ {
2288
+ "operationId": "testChannel",
2289
+ "method": "POST",
2290
+ "path": "/v1/channels/{id}/test",
2291
+ "summary": "Send a test to a channel",
2292
+ "tag": "Alerts",
2293
+ "params": [
2294
+ {
2295
+ "name": "id",
2296
+ "in": "path",
2297
+ "type": "string",
2298
+ "description": "Channel id (dest_...).",
2299
+ "required": true,
2300
+ "nullable": false
2301
+ }
2302
+ ],
2303
+ "body": null,
2304
+ "response": "json"
2305
+ },
2306
+ {
2307
+ "operationId": "rotateWebhookSecret",
2308
+ "method": "POST",
2309
+ "path": "/v1/channels/{id}/rotate-secret",
2310
+ "summary": "Rotate a webhook secret",
2311
+ "tag": "Alerts",
2312
+ "params": [
2313
+ {
2314
+ "name": "id",
2315
+ "in": "path",
2316
+ "type": "string",
2317
+ "description": "Channel id (dest_...).",
2318
+ "required": true,
2319
+ "nullable": false
2320
+ }
2321
+ ],
2322
+ "body": null,
2323
+ "response": "json"
2324
+ },
2325
+ {
2326
+ "operationId": "listChannelDeliveries",
2327
+ "method": "GET",
2328
+ "path": "/v1/channels/{id}/deliveries",
2329
+ "summary": "List deliveries to a channel",
2330
+ "tag": "Alerts",
2331
+ "params": [
2332
+ {
2333
+ "name": "id",
2334
+ "in": "path",
2335
+ "type": "string",
2336
+ "description": "Channel id (dest_...).",
2337
+ "required": true,
2338
+ "nullable": false
2339
+ },
2340
+ {
2341
+ "name": "limit",
2342
+ "in": "query",
2343
+ "type": "integer",
2344
+ "required": false,
2345
+ "nullable": false
2346
+ }
2347
+ ],
2348
+ "body": null,
2349
+ "response": "json"
2350
+ },
2351
+ {
2352
+ "operationId": "listChannels",
2353
+ "method": "GET",
2354
+ "path": "/v1/channels",
2355
+ "summary": "List channels",
2356
+ "tag": "Alerts",
2357
+ "params": [],
2358
+ "body": null,
2359
+ "response": "json"
2360
+ },
2361
+ {
2362
+ "operationId": "createChannel",
2363
+ "method": "POST",
2364
+ "path": "/v1/channels",
2365
+ "summary": "Create a channel",
2366
+ "tag": "Alerts",
2367
+ "params": [],
2368
+ "body": {
2369
+ "fields": [
2370
+ {
2371
+ "name": "kind",
2372
+ "type": "string",
2373
+ "enum": [
2374
+ "slack",
2375
+ "email",
2376
+ "webhook"
2377
+ ],
2378
+ "required": false,
2379
+ "nullable": false
2380
+ },
2381
+ {
2382
+ "name": "channelId",
2383
+ "type": "string",
2384
+ "description": "A Slack channel id from the connected workspace.",
2385
+ "required": false,
2386
+ "nullable": false
2387
+ },
2388
+ {
2389
+ "name": "channelName",
2390
+ "type": "string",
2391
+ "required": false,
2392
+ "nullable": false
2393
+ },
2394
+ {
2395
+ "name": "emails",
2396
+ "type": "array",
2397
+ "description": "Each address gets a confirmation link; workspace members are confirmed on sight.",
2398
+ "required": false,
2399
+ "nullable": false,
2400
+ "items": "string"
2401
+ },
2402
+ {
2403
+ "name": "url",
2404
+ "type": "string",
2405
+ "required": false,
2406
+ "nullable": false
2407
+ },
2408
+ {
2409
+ "name": "label",
2410
+ "type": "string",
2411
+ "required": false,
2412
+ "nullable": false
2413
+ },
2414
+ {
2415
+ "name": "headers",
2416
+ "type": "object",
2417
+ "required": false,
2418
+ "nullable": false
2419
+ }
2420
+ ]
2421
+ },
2422
+ "response": "json"
2423
+ }
2424
+ ];
2425
+
2426
+ // src/output.ts
2427
+ var isScalar = (v) => v === null || ["string", "number", "boolean"].includes(typeof v);
2428
+ var cell = (v) => {
2429
+ if (v === null) return "";
2430
+ if (typeof v === "string") return v.length > 48 ? `${v.slice(0, 47)}\u2026` : v.replace(/\s+/g, " ");
2431
+ return String(v);
2432
+ };
2433
+ function renderTable(rows) {
2434
+ const first = rows[0];
2435
+ if (!first) return "(no rows)";
2436
+ const columns = Object.keys(first).filter((k) => isScalar(first[k])).slice(0, 8);
2437
+ if (columns.length === 0) return JSON.stringify(rows, null, 2);
2438
+ const lines = rows.map((row) => columns.map((c) => cell(isScalar(row[c]) ? row[c] : JSON.stringify(row[c]))));
2439
+ const widths = columns.map((c, i) => Math.max(c.length, ...lines.map((l) => l[i]?.length ?? 0)));
2440
+ const pad = (s, w) => s.padEnd(w);
2441
+ const header = columns.map((c, i) => pad(c, widths[i] ?? c.length)).join(" ");
2442
+ const rule = widths.map((w) => "-".repeat(w)).join(" ");
2443
+ const body = lines.map((l) => l.map((s, i) => pad(s, widths[i] ?? s.length)).join(" "));
2444
+ return [header, rule, ...body].join("\n");
2445
+ }
2446
+ function formatOutput(value, options) {
2447
+ if (options.table) {
2448
+ const rows = Array.isArray(value) ? value : value !== null && typeof value === "object" && Array.isArray(value.data) ? value.data : null;
2449
+ if (rows && rows.every((r) => r !== null && typeof r === "object")) {
2450
+ return renderTable(rows);
2451
+ }
2452
+ }
2453
+ return options.pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
2454
+ }
2455
+
2456
+ // ../sdk/src/generated/core/bodySerializer.gen.ts
2457
+ var jsonBodySerializer = {
2458
+ bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
2459
+ };
2460
+
2461
+ // ../sdk/src/generated/core/params.gen.ts
2462
+ var extraPrefixesMap = {
2463
+ $body_: "body",
2464
+ $headers_: "headers",
2465
+ $path_: "path",
2466
+ $query_: "query"
2467
+ };
2468
+ var extraPrefixes = Object.entries(extraPrefixesMap);
2469
+
2470
+ // ../sdk/src/generated/core/serverSentEvents.gen.ts
2471
+ function createSseClient({
2472
+ onRequest,
2473
+ onSseError,
2474
+ onSseEvent,
2475
+ responseTransformer,
2476
+ responseValidator,
2477
+ sseDefaultRetryDelay,
2478
+ sseMaxRetryAttempts,
2479
+ sseMaxRetryDelay,
2480
+ sseSleepFn,
2481
+ url,
2482
+ ...options
2483
+ }) {
2484
+ let lastEventId;
2485
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2486
+ const createStream = async function* () {
2487
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
2488
+ let attempt = 0;
2489
+ const signal = options.signal ?? new AbortController().signal;
2490
+ while (true) {
2491
+ if (signal.aborted) break;
2492
+ attempt++;
2493
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
2494
+ if (lastEventId !== void 0) {
2495
+ headers.set("Last-Event-ID", lastEventId);
2496
+ }
2497
+ try {
2498
+ const requestInit = {
2499
+ redirect: "follow",
2500
+ ...options,
2501
+ body: options.serializedBody,
2502
+ headers,
2503
+ signal
2504
+ };
2505
+ let request = new Request(url, requestInit);
2506
+ if (onRequest) {
2507
+ request = await onRequest(url, requestInit);
2508
+ }
2509
+ const _fetch = options.fetch ?? globalThis.fetch;
2510
+ const response = await _fetch(request);
2511
+ if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
2512
+ if (!response.body) throw new Error("No body in SSE response");
2513
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
2514
+ let buffer = "";
2515
+ const abortHandler = () => {
2516
+ try {
2517
+ reader.cancel();
2518
+ } catch {
2519
+ }
2520
+ };
2521
+ signal.addEventListener("abort", abortHandler);
2522
+ try {
2523
+ while (true) {
2524
+ const { done, value } = await reader.read();
2525
+ if (done) break;
2526
+ buffer += value;
2527
+ buffer = buffer.replace(/\r\n?/g, "\n");
2528
+ const chunks = buffer.split("\n\n");
2529
+ buffer = chunks.pop() ?? "";
2530
+ for (const chunk of chunks) {
2531
+ const lines = chunk.split("\n");
2532
+ const dataLines = [];
2533
+ let eventName;
2534
+ for (const line of lines) {
2535
+ if (line.startsWith("data:")) {
2536
+ dataLines.push(line.replace(/^data:\s*/, ""));
2537
+ } else if (line.startsWith("event:")) {
2538
+ eventName = line.replace(/^event:\s*/, "");
2539
+ } else if (line.startsWith("id:")) {
2540
+ lastEventId = line.replace(/^id:\s*/, "");
2541
+ } else if (line.startsWith("retry:")) {
2542
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
2543
+ if (!Number.isNaN(parsed)) {
2544
+ retryDelay = parsed;
2545
+ }
2546
+ }
2547
+ }
2548
+ let data;
2549
+ let parsedJson = false;
2550
+ if (dataLines.length) {
2551
+ const rawData = dataLines.join("\n");
2552
+ try {
2553
+ data = JSON.parse(rawData);
2554
+ parsedJson = true;
2555
+ } catch {
2556
+ data = rawData;
2557
+ }
2558
+ }
2559
+ if (parsedJson) {
2560
+ if (responseValidator) {
2561
+ await responseValidator(data);
2562
+ }
2563
+ if (responseTransformer) {
2564
+ data = await responseTransformer(data);
2565
+ }
2566
+ }
2567
+ onSseEvent?.({
2568
+ data,
2569
+ event: eventName,
2570
+ id: lastEventId,
2571
+ retry: retryDelay
2572
+ });
2573
+ if (dataLines.length) {
2574
+ yield data;
2575
+ }
2576
+ }
2577
+ }
2578
+ } finally {
2579
+ signal.removeEventListener("abort", abortHandler);
2580
+ reader.releaseLock();
2581
+ }
2582
+ break;
2583
+ } catch (error) {
2584
+ onSseError?.(error);
2585
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
2586
+ break;
2587
+ }
2588
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
2589
+ await sleep(backoff);
2590
+ }
2591
+ }
2592
+ };
2593
+ const stream = createStream();
2594
+ return { stream };
2595
+ }
2596
+
2597
+ // ../sdk/src/generated/core/pathSerializer.gen.ts
2598
+ var separatorArrayExplode = (style) => {
2599
+ switch (style) {
2600
+ case "label":
2601
+ return ".";
2602
+ case "matrix":
2603
+ return ";";
2604
+ case "simple":
2605
+ return ",";
2606
+ default:
2607
+ return "&";
2608
+ }
2609
+ };
2610
+ var separatorArrayNoExplode = (style) => {
2611
+ switch (style) {
2612
+ case "form":
2613
+ return ",";
2614
+ case "pipeDelimited":
2615
+ return "|";
2616
+ case "spaceDelimited":
2617
+ return "%20";
2618
+ default:
2619
+ return ",";
2620
+ }
2621
+ };
2622
+ var separatorObjectExplode = (style) => {
2623
+ switch (style) {
2624
+ case "label":
2625
+ return ".";
2626
+ case "matrix":
2627
+ return ";";
2628
+ case "simple":
2629
+ return ",";
2630
+ default:
2631
+ return "&";
2632
+ }
2633
+ };
2634
+ var serializeArrayParam = ({
2635
+ allowReserved,
2636
+ explode,
2637
+ name,
2638
+ style,
2639
+ value
2640
+ }) => {
2641
+ if (!explode) {
2642
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
2643
+ switch (style) {
2644
+ case "label":
2645
+ return `.${joinedValues2}`;
2646
+ case "matrix":
2647
+ return `;${name}=${joinedValues2}`;
2648
+ case "simple":
2649
+ return joinedValues2;
2650
+ default:
2651
+ return `${name}=${joinedValues2}`;
2652
+ }
2653
+ }
2654
+ const separator = separatorArrayExplode(style);
2655
+ const joinedValues = value.map((v) => {
2656
+ if (style === "label" || style === "simple") {
2657
+ return allowReserved ? v : encodeURIComponent(v);
2658
+ }
2659
+ return serializePrimitiveParam({
2660
+ allowReserved,
2661
+ name,
2662
+ value: v
2663
+ });
2664
+ }).join(separator);
2665
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
2666
+ };
2667
+ var serializePrimitiveParam = ({
2668
+ allowReserved,
2669
+ name,
2670
+ value
2671
+ }) => {
2672
+ if (value === void 0 || value === null) {
2673
+ return "";
2674
+ }
2675
+ if (typeof value === "object") {
2676
+ throw new Error(
2677
+ "Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
2678
+ );
2679
+ }
2680
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
2681
+ };
2682
+ var serializeObjectParam = ({
2683
+ allowReserved,
2684
+ explode,
2685
+ name,
2686
+ style,
2687
+ value,
2688
+ valueOnly
2689
+ }) => {
2690
+ if (value instanceof Date) {
2691
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
2692
+ }
2693
+ if (style !== "deepObject" && !explode) {
2694
+ let values = [];
2695
+ Object.entries(value).forEach(([key, v]) => {
2696
+ values = [...values, key, allowReserved ? v : encodeURIComponent(v)];
2697
+ });
2698
+ const joinedValues2 = values.join(",");
2699
+ switch (style) {
2700
+ case "form":
2701
+ return `${name}=${joinedValues2}`;
2702
+ case "label":
2703
+ return `.${joinedValues2}`;
2704
+ case "matrix":
2705
+ return `;${name}=${joinedValues2}`;
2706
+ default:
2707
+ return joinedValues2;
2708
+ }
2709
+ }
2710
+ const separator = separatorObjectExplode(style);
2711
+ const joinedValues = Object.entries(value).map(
2712
+ ([key, v]) => serializePrimitiveParam({
2713
+ allowReserved,
2714
+ name: style === "deepObject" ? `${name}[${key}]` : key,
2715
+ value: v
2716
+ })
2717
+ ).join(separator);
2718
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
2719
+ };
2720
+
2721
+ // ../sdk/src/generated/core/utils.gen.ts
2722
+ var PATH_PARAM_RE = /\{[^{}]+\}/g;
2723
+ var defaultPathSerializer = ({ path, url: _url }) => {
2724
+ let url = _url;
2725
+ const matches = _url.match(PATH_PARAM_RE);
2726
+ if (matches) {
2727
+ for (const match of matches) {
2728
+ let explode = false;
2729
+ let name = match.substring(1, match.length - 1);
2730
+ let style = "simple";
2731
+ if (name.endsWith("*")) {
2732
+ explode = true;
2733
+ name = name.substring(0, name.length - 1);
2734
+ }
2735
+ if (name.startsWith(".")) {
2736
+ name = name.substring(1);
2737
+ style = "label";
2738
+ } else if (name.startsWith(";")) {
2739
+ name = name.substring(1);
2740
+ style = "matrix";
2741
+ }
2742
+ const value = path[name];
2743
+ if (value === void 0 || value === null) {
2744
+ continue;
2745
+ }
2746
+ if (Array.isArray(value)) {
2747
+ url = url.replace(match, serializeArrayParam({ explode, name, style, value }));
2748
+ continue;
2749
+ }
2750
+ if (typeof value === "object") {
2751
+ url = url.replace(
2752
+ match,
2753
+ serializeObjectParam({
2754
+ explode,
2755
+ name,
2756
+ style,
2757
+ value,
2758
+ valueOnly: true
2759
+ })
2760
+ );
2761
+ continue;
2762
+ }
2763
+ if (style === "matrix") {
2764
+ url = url.replace(
2765
+ match,
2766
+ `;${serializePrimitiveParam({
2767
+ name,
2768
+ value
2769
+ })}`
2770
+ );
2771
+ continue;
2772
+ }
2773
+ const replaceValue = encodeURIComponent(
2774
+ style === "label" ? `.${value}` : value
2775
+ );
2776
+ url = url.replace(match, replaceValue);
2777
+ }
2778
+ }
2779
+ return url;
2780
+ };
2781
+ var getUrl = ({
2782
+ baseUrl,
2783
+ path,
2784
+ query,
2785
+ querySerializer,
2786
+ url: _url
2787
+ }) => {
2788
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
2789
+ let url = (baseUrl ?? "") + pathUrl;
2790
+ if (path) {
2791
+ url = defaultPathSerializer({ path, url });
2792
+ }
2793
+ let search = query ? querySerializer(query) : "";
2794
+ if (search.startsWith("?")) {
2795
+ search = search.substring(1);
2796
+ }
2797
+ if (search) {
2798
+ url += `?${search}`;
2799
+ }
2800
+ return url;
2801
+ };
2802
+ function getValidRequestBody(options) {
2803
+ const hasBody = options.body !== void 0;
2804
+ const isSerializedBody = hasBody && options.bodySerializer;
2805
+ if (isSerializedBody) {
2806
+ if ("serializedBody" in options) {
2807
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
2808
+ return hasSerializedBody ? options.serializedBody : null;
2809
+ }
2810
+ return options.body !== "" ? options.body : null;
2811
+ }
2812
+ if (hasBody) {
2813
+ return options.body;
2814
+ }
2815
+ return void 0;
2816
+ }
2817
+
2818
+ // ../sdk/src/generated/core/auth.gen.ts
2819
+ var getAuthToken = async (auth, callback) => {
2820
+ const token = typeof callback === "function" ? await callback(auth) : callback;
2821
+ if (!token) {
2822
+ return;
2823
+ }
2824
+ if (auth.scheme === "bearer") {
2825
+ return `Bearer ${token}`;
2826
+ }
2827
+ if (auth.scheme === "basic") {
2828
+ return `Basic ${btoa(token)}`;
2829
+ }
2830
+ return token;
2831
+ };
2832
+
2833
+ // ../sdk/src/generated/client/utils.gen.ts
2834
+ var createQuerySerializer = ({
2835
+ parameters = {},
2836
+ ...args
2837
+ } = {}) => {
2838
+ const querySerializer = (queryParams) => {
2839
+ const search = [];
2840
+ if (queryParams && typeof queryParams === "object") {
2841
+ for (const name in queryParams) {
2842
+ const value = queryParams[name];
2843
+ if (value === void 0 || value === null) {
2844
+ continue;
2845
+ }
2846
+ const options = parameters[name] || args;
2847
+ if (Array.isArray(value)) {
2848
+ const serializedArray = serializeArrayParam({
2849
+ allowReserved: options.allowReserved,
2850
+ explode: true,
2851
+ name,
2852
+ style: "form",
2853
+ value,
2854
+ ...options.array
2855
+ });
2856
+ if (serializedArray) search.push(serializedArray);
2857
+ } else if (typeof value === "object") {
2858
+ const serializedObject = serializeObjectParam({
2859
+ allowReserved: options.allowReserved,
2860
+ explode: true,
2861
+ name,
2862
+ style: "deepObject",
2863
+ value,
2864
+ ...options.object
2865
+ });
2866
+ if (serializedObject) search.push(serializedObject);
2867
+ } else {
2868
+ const serializedPrimitive = serializePrimitiveParam({
2869
+ allowReserved: options.allowReserved,
2870
+ name,
2871
+ value
2872
+ });
2873
+ if (serializedPrimitive) search.push(serializedPrimitive);
2874
+ }
2875
+ }
2876
+ }
2877
+ return search.join("&");
2878
+ };
2879
+ return querySerializer;
2880
+ };
2881
+ var getParseAs = (contentType) => {
2882
+ if (!contentType) {
2883
+ return "stream";
2884
+ }
2885
+ const cleanContent = contentType.split(";")[0]?.trim();
2886
+ if (!cleanContent) {
2887
+ return;
2888
+ }
2889
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
2890
+ return "json";
2891
+ }
2892
+ if (cleanContent === "multipart/form-data") {
2893
+ return "formData";
2894
+ }
2895
+ if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
2896
+ return "blob";
2897
+ }
2898
+ if (cleanContent.startsWith("text/")) {
2899
+ return "text";
2900
+ }
2901
+ return;
2902
+ };
2903
+ var checkForExistence = (options, name) => {
2904
+ if (!name) {
2905
+ return false;
2906
+ }
2907
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
2908
+ return true;
2909
+ }
2910
+ return false;
2911
+ };
2912
+ async function setAuthParams(options) {
2913
+ for (const auth of options.security ?? []) {
2914
+ if (checkForExistence(options, auth.name)) {
2915
+ continue;
2916
+ }
2917
+ const token = await getAuthToken(auth, options.auth);
2918
+ if (!token) {
2919
+ continue;
2920
+ }
2921
+ const name = auth.name ?? "Authorization";
2922
+ switch (auth.in) {
2923
+ case "query":
2924
+ if (!options.query) {
2925
+ options.query = {};
2926
+ }
2927
+ options.query[name] = token;
2928
+ break;
2929
+ case "cookie":
2930
+ options.headers.append("Cookie", `${name}=${token}`);
2931
+ break;
2932
+ case "header":
2933
+ default:
2934
+ options.headers.set(name, token);
2935
+ break;
2936
+ }
2937
+ }
2938
+ }
2939
+ var buildUrl = (options) => getUrl({
2940
+ baseUrl: options.baseUrl,
2941
+ path: options.path,
2942
+ query: options.query,
2943
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
2944
+ url: options.url
2945
+ });
2946
+ var mergeConfigs = (a, b) => {
2947
+ const config = { ...a, ...b };
2948
+ if (config.baseUrl?.endsWith("/")) {
2949
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
2950
+ }
2951
+ config.headers = mergeHeaders(a.headers, b.headers);
2952
+ return config;
2953
+ };
2954
+ var headersEntries = (headers) => {
2955
+ const entries = [];
2956
+ headers.forEach((value, key) => {
2957
+ entries.push([key, value]);
2958
+ });
2959
+ return entries;
2960
+ };
2961
+ var mergeHeaders = (...headers) => {
2962
+ const mergedHeaders = new Headers();
2963
+ for (const header of headers) {
2964
+ if (!header) {
2965
+ continue;
2966
+ }
2967
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
2968
+ for (const [key, value] of iterator) {
2969
+ if (value === null) {
2970
+ mergedHeaders.delete(key);
2971
+ } else if (Array.isArray(value)) {
2972
+ for (const v of value) {
2973
+ mergedHeaders.append(key, v);
2974
+ }
2975
+ } else if (value !== void 0) {
2976
+ mergedHeaders.set(
2977
+ key,
2978
+ typeof value === "object" ? JSON.stringify(value) : value
2979
+ );
2980
+ }
2981
+ }
2982
+ }
2983
+ return mergedHeaders;
2984
+ };
2985
+ var Interceptors = class {
2986
+ fns = [];
2987
+ clear() {
2988
+ this.fns = [];
2989
+ }
2990
+ eject(id) {
2991
+ const index = this.getInterceptorIndex(id);
2992
+ if (this.fns[index]) {
2993
+ this.fns[index] = null;
2994
+ }
2995
+ }
2996
+ exists(id) {
2997
+ const index = this.getInterceptorIndex(id);
2998
+ return Boolean(this.fns[index]);
2999
+ }
3000
+ getInterceptorIndex(id) {
3001
+ if (typeof id === "number") {
3002
+ return this.fns[id] ? id : -1;
3003
+ }
3004
+ return this.fns.indexOf(id);
3005
+ }
3006
+ update(id, fn) {
3007
+ const index = this.getInterceptorIndex(id);
3008
+ if (this.fns[index]) {
3009
+ this.fns[index] = fn;
3010
+ return id;
3011
+ }
3012
+ return false;
3013
+ }
3014
+ use(fn) {
3015
+ this.fns.push(fn);
3016
+ return this.fns.length - 1;
3017
+ }
3018
+ };
3019
+ var createInterceptors = () => ({
3020
+ error: new Interceptors(),
3021
+ request: new Interceptors(),
3022
+ response: new Interceptors()
3023
+ });
3024
+ var defaultQuerySerializer = createQuerySerializer({
3025
+ allowReserved: false,
3026
+ array: {
3027
+ explode: true,
3028
+ style: "form"
3029
+ },
3030
+ object: {
3031
+ explode: true,
3032
+ style: "deepObject"
3033
+ }
3034
+ });
3035
+ var defaultHeaders = {
3036
+ "Content-Type": "application/json"
3037
+ };
3038
+ var createConfig = (override = {}) => ({
3039
+ ...jsonBodySerializer,
3040
+ headers: defaultHeaders,
3041
+ parseAs: "auto",
3042
+ querySerializer: defaultQuerySerializer,
3043
+ ...override
3044
+ });
3045
+
3046
+ // ../sdk/src/generated/client/client.gen.ts
3047
+ var createClient = (config = {}) => {
3048
+ let _config = mergeConfigs(createConfig(), config);
3049
+ const getConfig = () => ({ ..._config });
3050
+ const setConfig = (config2) => {
3051
+ _config = mergeConfigs(_config, config2);
3052
+ return getConfig();
3053
+ };
3054
+ const interceptors = createInterceptors();
3055
+ const beforeRequest = async (options) => {
3056
+ const opts = {
3057
+ ..._config,
3058
+ ...options,
3059
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
3060
+ headers: mergeHeaders(_config.headers, options.headers),
3061
+ serializedBody: void 0
3062
+ };
3063
+ if (opts.security) {
3064
+ await setAuthParams(opts);
3065
+ }
3066
+ if (opts.requestValidator) {
3067
+ await opts.requestValidator(opts);
3068
+ }
3069
+ if (opts.body !== void 0 && opts.bodySerializer) {
3070
+ opts.serializedBody = opts.bodySerializer(opts.body);
3071
+ }
3072
+ if (opts.body === void 0 || opts.serializedBody === "") {
3073
+ opts.headers.delete("Content-Type");
3074
+ }
3075
+ const resolvedOpts = opts;
3076
+ const url = buildUrl(resolvedOpts);
3077
+ return { opts: resolvedOpts, url };
3078
+ };
3079
+ const request = async (options) => {
3080
+ const throwOnError = options.throwOnError ?? _config.throwOnError;
3081
+ const responseStyle = options.responseStyle ?? _config.responseStyle;
3082
+ let request2;
3083
+ let response;
3084
+ try {
3085
+ const { opts, url } = await beforeRequest(options);
3086
+ const requestInit = {
3087
+ redirect: "follow",
3088
+ ...opts,
3089
+ body: getValidRequestBody(opts)
3090
+ };
3091
+ request2 = new Request(url, requestInit);
3092
+ for (const fn of interceptors.request.fns) {
3093
+ if (fn) {
3094
+ request2 = await fn(request2, opts);
3095
+ }
3096
+ }
3097
+ const _fetch = opts.fetch;
3098
+ response = await _fetch(request2);
3099
+ for (const fn of interceptors.response.fns) {
3100
+ if (fn) {
3101
+ response = await fn(response, request2, opts);
3102
+ }
3103
+ }
3104
+ const result = {
3105
+ request: request2,
3106
+ response
3107
+ };
3108
+ if (response.ok) {
3109
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
3110
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
3111
+ let emptyData;
3112
+ switch (parseAs) {
3113
+ case "arrayBuffer":
3114
+ case "blob":
3115
+ case "text":
3116
+ emptyData = await response[parseAs]();
3117
+ break;
3118
+ case "formData":
3119
+ emptyData = new FormData();
3120
+ break;
3121
+ case "stream":
3122
+ emptyData = response.body;
3123
+ break;
3124
+ case "json":
3125
+ default:
3126
+ emptyData = {};
3127
+ break;
3128
+ }
3129
+ return opts.responseStyle === "data" ? emptyData : {
3130
+ data: emptyData,
3131
+ ...result
3132
+ };
3133
+ }
3134
+ let data;
3135
+ switch (parseAs) {
3136
+ case "arrayBuffer":
3137
+ case "blob":
3138
+ case "formData":
3139
+ case "text":
3140
+ data = await response[parseAs]();
3141
+ break;
3142
+ case "json": {
3143
+ const text = await response.text();
3144
+ data = text ? JSON.parse(text) : {};
3145
+ break;
3146
+ }
3147
+ case "stream":
3148
+ return opts.responseStyle === "data" ? response.body : {
3149
+ data: response.body,
3150
+ ...result
3151
+ };
3152
+ }
3153
+ if (parseAs === "json") {
3154
+ if (opts.responseValidator) {
3155
+ await opts.responseValidator(data);
3156
+ }
3157
+ if (opts.responseTransformer) {
3158
+ data = await opts.responseTransformer(data);
3159
+ }
3160
+ }
3161
+ return opts.responseStyle === "data" ? data : {
3162
+ data,
3163
+ ...result
3164
+ };
3165
+ }
3166
+ const textError = await response.text();
3167
+ let jsonError;
3168
+ try {
3169
+ jsonError = JSON.parse(textError);
3170
+ } catch {
3171
+ }
3172
+ throw jsonError ?? textError;
3173
+ } catch (error) {
3174
+ let finalError = error;
3175
+ for (const fn of interceptors.error.fns) {
3176
+ if (fn) {
3177
+ finalError = await fn(finalError, response, request2, options);
3178
+ }
3179
+ }
3180
+ finalError = finalError || {};
3181
+ if (throwOnError) {
3182
+ throw finalError;
3183
+ }
3184
+ return responseStyle === "data" ? void 0 : {
3185
+ error: finalError,
3186
+ request: request2,
3187
+ response
3188
+ };
3189
+ }
3190
+ };
3191
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
3192
+ const makeSseFn = (method) => async (options) => {
3193
+ const { opts, url } = await beforeRequest(options);
3194
+ return createSseClient({
3195
+ ...opts,
3196
+ body: opts.body,
3197
+ method,
3198
+ onRequest: async (url2, init) => {
3199
+ let request2 = new Request(url2, init);
3200
+ for (const fn of interceptors.request.fns) {
3201
+ if (fn) {
3202
+ request2 = await fn(request2, opts);
3203
+ }
3204
+ }
3205
+ return request2;
3206
+ },
3207
+ serializedBody: getValidRequestBody(opts),
3208
+ url
3209
+ });
3210
+ };
3211
+ const _buildUrl = (options) => buildUrl({ ..._config, ...options });
3212
+ return {
3213
+ buildUrl: _buildUrl,
3214
+ connect: makeMethodFn("CONNECT"),
3215
+ delete: makeMethodFn("DELETE"),
3216
+ get: makeMethodFn("GET"),
3217
+ getConfig,
3218
+ head: makeMethodFn("HEAD"),
3219
+ interceptors,
3220
+ options: makeMethodFn("OPTIONS"),
3221
+ patch: makeMethodFn("PATCH"),
3222
+ post: makeMethodFn("POST"),
3223
+ put: makeMethodFn("PUT"),
3224
+ request,
3225
+ setConfig,
3226
+ sse: {
3227
+ connect: makeSseFn("CONNECT"),
3228
+ delete: makeSseFn("DELETE"),
3229
+ get: makeSseFn("GET"),
3230
+ head: makeSseFn("HEAD"),
3231
+ options: makeSseFn("OPTIONS"),
3232
+ patch: makeSseFn("PATCH"),
3233
+ post: makeSseFn("POST"),
3234
+ put: makeSseFn("PUT"),
3235
+ trace: makeSseFn("TRACE")
3236
+ },
3237
+ trace: makeMethodFn("TRACE")
3238
+ };
3239
+ };
3240
+
3241
+ // ../sdk/src/generated/sdk.gen.ts
3242
+ var sdk_gen_exports = {};
3243
+ __export(sdk_gen_exports, {
3244
+ createAlert: () => createAlert,
3245
+ createApiKey: () => createApiKey,
3246
+ createChannel: () => createChannel,
3247
+ createKeyword: () => createKeyword,
3248
+ createSegment: () => createSegment,
3249
+ deleteAlert: () => deleteAlert,
3250
+ deleteChannel: () => deleteChannel,
3251
+ deleteKeyword: () => deleteKeyword,
3252
+ deleteSegment: () => deleteSegment,
3253
+ exportMentionsCsv: () => exportMentionsCsv,
3254
+ exportPeopleCsv: () => exportPeopleCsv,
3255
+ getAlert: () => getAlert,
3256
+ getAnalyticsBreakdown: () => getAnalyticsBreakdown,
3257
+ getAnalyticsSeries: () => getAnalyticsSeries,
3258
+ getAnalyticsSummary: () => getAnalyticsSummary,
3259
+ getChannel: () => getChannel,
3260
+ getCompany: () => getCompany,
3261
+ getHealth: () => getHealth,
3262
+ getKeyword: () => getKeyword,
3263
+ getMention: () => getMention,
3264
+ getPerson: () => getPerson,
3265
+ getSegment: () => getSegment,
3266
+ getShareOfVoice: () => getShareOfVoice,
3267
+ listAlerts: () => listAlerts,
3268
+ listApiKeys: () => listApiKeys,
3269
+ listChannelDeliveries: () => listChannelDeliveries,
3270
+ listChannels: () => listChannels,
3271
+ listKeywords: () => listKeywords,
3272
+ listPeople: () => listPeople,
3273
+ listSegments: () => listSegments,
3274
+ mergePeople: () => mergePeople,
3275
+ revokeApiKey: () => revokeApiKey,
3276
+ rotateWebhookSecret: () => rotateWebhookSecret,
3277
+ runAlertDigest: () => runAlertDigest,
3278
+ searchMentions: () => searchMentions,
3279
+ splitPerson: () => splitPerson,
3280
+ testAlert: () => testAlert,
3281
+ testChannel: () => testChannel,
3282
+ updateAlert: () => updateAlert,
3283
+ updateChannel: () => updateChannel,
3284
+ updateCompany: () => updateCompany,
3285
+ updateKeyword: () => updateKeyword,
3286
+ updateMention: () => updateMention,
3287
+ updatePerson: () => updatePerson,
3288
+ updateSegment: () => updateSegment
3289
+ });
3290
+
3291
+ // ../sdk/src/generated/client.gen.ts
3292
+ var client = createClient(createConfig({ baseUrl: "https://api.mentio.dev" }));
3293
+
3294
+ // ../sdk/src/generated/sdk.gen.ts
3295
+ var getHealth = (options) => (options?.client ?? client).get({ url: "/v1/health", ...options });
3296
+ var listKeywords = (options) => (options?.client ?? client).get({
3297
+ security: [{ scheme: "bearer", type: "http" }],
3298
+ url: "/v1/keywords",
3299
+ ...options
3300
+ });
3301
+ var createKeyword = (options) => (options.client ?? client).post({
3302
+ security: [{ scheme: "bearer", type: "http" }],
3303
+ url: "/v1/keywords",
3304
+ ...options,
3305
+ headers: {
3306
+ "Content-Type": "application/json",
3307
+ ...options.headers
3308
+ }
3309
+ });
3310
+ var deleteKeyword = (options) => (options.client ?? client).delete({
3311
+ security: [{ scheme: "bearer", type: "http" }],
3312
+ url: "/v1/keywords/{id}",
3313
+ ...options
3314
+ });
3315
+ var getKeyword = (options) => (options.client ?? client).get({
3316
+ security: [{ scheme: "bearer", type: "http" }],
3317
+ url: "/v1/keywords/{id}",
3318
+ ...options
3319
+ });
3320
+ var updateKeyword = (options) => (options.client ?? client).patch({
3321
+ security: [{ scheme: "bearer", type: "http" }],
3322
+ url: "/v1/keywords/{id}",
3323
+ ...options,
3324
+ headers: {
3325
+ "Content-Type": "application/json",
3326
+ ...options.headers
3327
+ }
3328
+ });
3329
+ var getMention = (options) => (options.client ?? client).get({
3330
+ security: [{ scheme: "bearer", type: "http" }],
3331
+ url: "/v1/mentions/{id}",
3332
+ ...options
3333
+ });
3334
+ var updateMention = (options) => (options.client ?? client).patch({
3335
+ security: [{ scheme: "bearer", type: "http" }],
3336
+ url: "/v1/mentions/{id}",
3337
+ ...options,
3338
+ headers: {
3339
+ "Content-Type": "application/json",
3340
+ ...options.headers
3341
+ }
3342
+ });
3343
+ var searchMentions = (options) => (options?.client ?? client).get({
3344
+ security: [{ scheme: "bearer", type: "http" }],
3345
+ url: "/v1/mentions",
3346
+ ...options
3347
+ });
3348
+ var exportMentionsCsv = (options) => (options?.client ?? client).get({
3349
+ security: [{ scheme: "bearer", type: "http" }],
3350
+ url: "/v1/mentions/export.csv",
3351
+ ...options
3352
+ });
3353
+ var exportPeopleCsv = (options) => (options?.client ?? client).get({
3354
+ security: [{ scheme: "bearer", type: "http" }],
3355
+ url: "/v1/people/export.csv",
3356
+ ...options
3357
+ });
3358
+ var listPeople = (options) => (options?.client ?? client).get({
3359
+ security: [{ scheme: "bearer", type: "http" }],
3360
+ url: "/v1/people",
3361
+ ...options
3362
+ });
3363
+ var getPerson = (options) => (options.client ?? client).get({
3364
+ security: [{ scheme: "bearer", type: "http" }],
3365
+ url: "/v1/people/{id}",
3366
+ ...options
3367
+ });
3368
+ var updatePerson = (options) => (options.client ?? client).patch({
3369
+ security: [{ scheme: "bearer", type: "http" }],
3370
+ url: "/v1/people/{id}",
3371
+ ...options,
3372
+ headers: {
3373
+ "Content-Type": "application/json",
3374
+ ...options.headers
3375
+ }
3376
+ });
3377
+ var mergePeople = (options) => (options.client ?? client).post({
3378
+ security: [{ scheme: "bearer", type: "http" }],
3379
+ url: "/v1/people/{id}/merge",
3380
+ ...options,
3381
+ headers: {
3382
+ "Content-Type": "application/json",
3383
+ ...options.headers
3384
+ }
3385
+ });
3386
+ var splitPerson = (options) => (options.client ?? client).post({
3387
+ security: [{ scheme: "bearer", type: "http" }],
3388
+ url: "/v1/people/{id}/split",
3389
+ ...options
3390
+ });
3391
+ var listSegments = (options) => (options?.client ?? client).get({
3392
+ security: [{ scheme: "bearer", type: "http" }],
3393
+ url: "/v1/segments",
3394
+ ...options
3395
+ });
3396
+ var createSegment = (options) => (options.client ?? client).post({
3397
+ security: [{ scheme: "bearer", type: "http" }],
3398
+ url: "/v1/segments",
3399
+ ...options,
3400
+ headers: {
3401
+ "Content-Type": "application/json",
3402
+ ...options.headers
3403
+ }
3404
+ });
3405
+ var deleteSegment = (options) => (options.client ?? client).delete({
3406
+ security: [{ scheme: "bearer", type: "http" }],
3407
+ url: "/v1/segments/{id}",
3408
+ ...options
3409
+ });
3410
+ var getSegment = (options) => (options.client ?? client).get({
3411
+ security: [{ scheme: "bearer", type: "http" }],
3412
+ url: "/v1/segments/{id}",
3413
+ ...options
3414
+ });
3415
+ var updateSegment = (options) => (options.client ?? client).patch({
3416
+ security: [{ scheme: "bearer", type: "http" }],
3417
+ url: "/v1/segments/{id}",
3418
+ ...options,
3419
+ headers: {
3420
+ "Content-Type": "application/json",
3421
+ ...options.headers
3422
+ }
3423
+ });
3424
+ var getCompany = (options) => (options?.client ?? client).get({
3425
+ security: [{ scheme: "bearer", type: "http" }],
3426
+ url: "/v1/company",
3427
+ ...options
3428
+ });
3429
+ var updateCompany = (options) => (options.client ?? client).patch({
3430
+ security: [{ scheme: "bearer", type: "http" }],
3431
+ url: "/v1/company",
3432
+ ...options,
3433
+ headers: {
3434
+ "Content-Type": "application/json",
3435
+ ...options.headers
3436
+ }
3437
+ });
3438
+ var listApiKeys = (options) => (options?.client ?? client).get({
3439
+ security: [{ scheme: "bearer", type: "http" }],
3440
+ url: "/v1/api-keys",
3441
+ ...options
3442
+ });
3443
+ var createApiKey = (options) => (options.client ?? client).post({
3444
+ security: [{ scheme: "bearer", type: "http" }],
3445
+ url: "/v1/api-keys",
3446
+ ...options,
3447
+ headers: {
3448
+ "Content-Type": "application/json",
3449
+ ...options.headers
3450
+ }
3451
+ });
3452
+ var revokeApiKey = (options) => (options.client ?? client).delete({
3453
+ security: [{ scheme: "bearer", type: "http" }],
3454
+ url: "/v1/api-keys/{id}",
3455
+ ...options
3456
+ });
3457
+ var deleteAlert = (options) => (options.client ?? client).delete({
3458
+ security: [{ scheme: "bearer", type: "http" }],
3459
+ url: "/v1/alerts/{id}",
3460
+ ...options
3461
+ });
3462
+ var getAlert = (options) => (options.client ?? client).get({
3463
+ security: [{ scheme: "bearer", type: "http" }],
3464
+ url: "/v1/alerts/{id}",
3465
+ ...options
3466
+ });
3467
+ var updateAlert = (options) => (options.client ?? client).patch({
3468
+ security: [{ scheme: "bearer", type: "http" }],
3469
+ url: "/v1/alerts/{id}",
3470
+ ...options,
3471
+ headers: {
3472
+ "Content-Type": "application/json",
3473
+ ...options.headers
3474
+ }
3475
+ });
3476
+ var listAlerts = (options) => (options?.client ?? client).get({
3477
+ security: [{ scheme: "bearer", type: "http" }],
3478
+ url: "/v1/alerts",
3479
+ ...options
3480
+ });
3481
+ var createAlert = (options) => (options.client ?? client).post({
3482
+ security: [{ scheme: "bearer", type: "http" }],
3483
+ url: "/v1/alerts",
3484
+ ...options,
3485
+ headers: {
3486
+ "Content-Type": "application/json",
3487
+ ...options.headers
3488
+ }
3489
+ });
3490
+ var testAlert = (options) => (options.client ?? client).post({
3491
+ security: [{ scheme: "bearer", type: "http" }],
3492
+ url: "/v1/alerts/{id}/test",
3493
+ ...options
3494
+ });
3495
+ var runAlertDigest = (options) => (options.client ?? client).post({
3496
+ security: [{ scheme: "bearer", type: "http" }],
3497
+ url: "/v1/alerts/{id}/run",
3498
+ ...options
3499
+ });
3500
+ var getAnalyticsSummary = (options) => (options?.client ?? client).get({
3501
+ security: [{ scheme: "bearer", type: "http" }],
3502
+ url: "/v1/analytics/summary",
3503
+ ...options
3504
+ });
3505
+ var getAnalyticsSeries = (options) => (options?.client ?? client).get({
3506
+ security: [{ scheme: "bearer", type: "http" }],
3507
+ url: "/v1/analytics/series",
3508
+ ...options
3509
+ });
3510
+ var getAnalyticsBreakdown = (options) => (options.client ?? client).get({
3511
+ security: [{ scheme: "bearer", type: "http" }],
3512
+ url: "/v1/analytics/breakdown",
3513
+ ...options
3514
+ });
3515
+ var getShareOfVoice = (options) => (options?.client ?? client).get({
3516
+ security: [{ scheme: "bearer", type: "http" }],
3517
+ url: "/v1/analytics/share-of-voice",
3518
+ ...options
3519
+ });
3520
+ var deleteChannel = (options) => (options.client ?? client).delete({
3521
+ security: [{ scheme: "bearer", type: "http" }],
3522
+ url: "/v1/channels/{id}",
3523
+ ...options
3524
+ });
3525
+ var getChannel = (options) => (options.client ?? client).get({
3526
+ security: [{ scheme: "bearer", type: "http" }],
3527
+ url: "/v1/channels/{id}",
3528
+ ...options
3529
+ });
3530
+ var updateChannel = (options) => (options.client ?? client).patch({
3531
+ security: [{ scheme: "bearer", type: "http" }],
3532
+ url: "/v1/channels/{id}",
3533
+ ...options,
3534
+ headers: {
3535
+ "Content-Type": "application/json",
3536
+ ...options.headers
3537
+ }
3538
+ });
3539
+ var testChannel = (options) => (options.client ?? client).post({
3540
+ security: [{ scheme: "bearer", type: "http" }],
3541
+ url: "/v1/channels/{id}/test",
3542
+ ...options
3543
+ });
3544
+ var rotateWebhookSecret = (options) => (options.client ?? client).post({
3545
+ security: [{ scheme: "bearer", type: "http" }],
3546
+ url: "/v1/channels/{id}/rotate-secret",
3547
+ ...options
3548
+ });
3549
+ var listChannelDeliveries = (options) => (options.client ?? client).get({
3550
+ security: [{ scheme: "bearer", type: "http" }],
3551
+ url: "/v1/channels/{id}/deliveries",
3552
+ ...options
3553
+ });
3554
+ var listChannels = (options) => (options?.client ?? client).get({
3555
+ security: [{ scheme: "bearer", type: "http" }],
3556
+ url: "/v1/channels",
3557
+ ...options
3558
+ });
3559
+ var createChannel = (options) => (options.client ?? client).post({
3560
+ security: [{ scheme: "bearer", type: "http" }],
3561
+ url: "/v1/channels",
3562
+ ...options,
3563
+ headers: {
3564
+ "Content-Type": "application/json",
3565
+ ...options.headers
3566
+ }
3567
+ });
3568
+
3569
+ // ../sdk/src/index.ts
3570
+ var DEFAULT_BASE_URL = "https://api.mentio.dev";
3571
+ function createMentio(options) {
3572
+ const client2 = createClient(
3573
+ createConfig({
3574
+ baseUrl: (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
3575
+ auth: () => options.apiKey,
3576
+ ...options.fetch ? { fetch: options.fetch } : {},
3577
+ ...options.headers ? { headers: options.headers } : {}
3578
+ })
3579
+ );
3580
+ const bound = {};
3581
+ for (const [name, fn] of Object.entries(sdk_gen_exports)) {
3582
+ if (typeof fn !== "function") continue;
3583
+ const call = fn;
3584
+ bound[name] = (callOptions) => call({ ...callOptions, client: callOptions?.client ?? client2 });
3585
+ }
3586
+ return { ...bound, client: client2 };
3587
+ }
3588
+
3589
+ // src/run.ts
3590
+ function clientFor(settings, fetchImpl) {
3591
+ return createMentio({ apiKey: settings.apiKey ?? "", baseUrl: settings.apiUrl, ...fetchImpl ? { fetch: fetchImpl } : {} }).client;
3592
+ }
3593
+ var BEARER = [{ scheme: "bearer", type: "http" }];
3594
+ async function runOperation(client2, op, request) {
3595
+ const result = await client2.request({
3596
+ method: op.method,
3597
+ url: op.path,
3598
+ path: request.path,
3599
+ query: request.query,
3600
+ ...request.body !== void 0 ? { body: request.body } : {},
3601
+ parseAs: op.response === "csv" ? "text" : "auto",
3602
+ security: BEARER,
3603
+ throwOnError: false
3604
+ });
3605
+ const status = result.response?.status ?? 0;
3606
+ if (result.error !== void 0) return { ok: false, status, value: result.error };
3607
+ if (!result.response) return { ok: false, status, value: void 0 };
3608
+ return { ok: result.response.ok, status, value: result.data ?? void 0 };
3609
+ }
3610
+ function errorEnvelope(outcome) {
3611
+ const value = outcome.value;
3612
+ if (value !== null && typeof value === "object" && "error" in value) {
3613
+ const inner = value.error;
3614
+ if (inner !== null && typeof inner === "object" && "code" in inner && "message" in inner) {
3615
+ return value;
3616
+ }
3617
+ }
3618
+ const message = typeof value === "string" && value.trim() !== "" ? value.trim().slice(0, 300) : `Request failed with status ${outcome.status}`;
3619
+ return { error: { code: `http_${outcome.status}`, message } };
3620
+ }
3621
+
3622
+ // src/watch.ts
3623
+ var SEEN_LIMIT = 5e3;
3624
+ var PAGE = 100;
3625
+ var newSeen = () => ({ ids: /* @__PURE__ */ new Set(), order: [] });
3626
+ function takeNew(seen, newestFirst) {
3627
+ const fresh = [];
3628
+ for (const item of newestFirst) {
3629
+ if (seen.ids.has(item.id)) continue;
3630
+ fresh.push(item);
3631
+ }
3632
+ for (const item of fresh) {
3633
+ seen.ids.add(item.id);
3634
+ seen.order.push(item.id);
3635
+ }
3636
+ while (seen.order.length > SEEN_LIMIT) {
3637
+ const oldest = seen.order.shift();
3638
+ if (oldest !== void 0) seen.ids.delete(oldest);
3639
+ }
3640
+ return fresh.reverse();
3641
+ }
3642
+ var defaultSleep = (ms, signal) => new Promise((resolve) => {
3643
+ if (signal.aborted) return resolve();
3644
+ const timer = setTimeout(resolve, ms);
3645
+ signal.addEventListener("abort", () => {
3646
+ clearTimeout(timer);
3647
+ resolve();
3648
+ });
3649
+ });
3650
+ async function watchMentions(options) {
3651
+ const sleep = options.sleep ?? defaultSleep;
3652
+ const seen = newSeen();
3653
+ let first = true;
3654
+ while (!options.signal.aborted) {
3655
+ const result = await options.client.request({
3656
+ method: "GET",
3657
+ url: "/v1/mentions",
3658
+ query: { ...options.query, sort: "newest", limit: PAGE },
3659
+ security: BEARER,
3660
+ throwOnError: false
3661
+ });
3662
+ if (result.error !== void 0 || !result.response?.ok) {
3663
+ options.warn(JSON.stringify({ error: result.error ?? { code: `http_${result.response?.status ?? 0}`, message: "poll failed" } }));
3664
+ } else {
3665
+ const page = result.data?.data ?? [];
3666
+ const fresh = takeNew(seen, page);
3667
+ if (!first || options.fromStart) for (const item of fresh) options.write(JSON.stringify(item));
3668
+ }
3669
+ first = false;
3670
+ await sleep(options.intervalMs, options.signal);
3671
+ }
3672
+ }
3673
+
3674
+ // src/index.ts
3675
+ var program = new Command();
3676
+ var stdoutIsTty = () => Boolean(process.stdout.isTTY);
3677
+ function print(value, globals) {
3678
+ process.stdout.write(`${formatOutput(value, { pretty: globals.pretty ?? stdoutIsTty(), table: globals.table ?? false })}
3679
+ `);
3680
+ }
3681
+ function fail(envelope, code = 1) {
3682
+ process.stderr.write(`${JSON.stringify(envelope)}
3683
+ `);
3684
+ process.exit(code);
3685
+ }
3686
+ function settingsOrFail(globals, needsKey) {
3687
+ const settings = resolveSettings({ apiKey: globals.apiKey, apiUrl: globals.apiUrl });
3688
+ if (needsKey && !settings.apiKey) {
3689
+ fail({ error: { code: "no_api_key", message: "No API key. Run `mentio auth:set --key mk_live_...`, set MENTIO_API_KEY, or pass --api-key." } }, 2);
3690
+ }
3691
+ return settings;
3692
+ }
3693
+ function addFieldOption(command, field) {
3694
+ const option = new Option(`--${field.name} <value>`, flagHelp(field));
3695
+ if (field.enum && field.type !== "array") option.choices(field.nullable ? [...field.enum, "null"] : field.enum);
3696
+ command.addOption(option);
3697
+ }
3698
+ function registerOperation(op) {
3699
+ const name = commandName(op);
3700
+ const command = program.command(name).description(op.summary).summary(op.summary);
3701
+ if (op.description) command.addHelpText("after", `
3702
+ ${op.description}
3703
+ `);
3704
+ for (const param of op.params.filter((p) => p.in === "path")) command.argument(`<${param.name}>`, param.description ?? "");
3705
+ for (const param of op.params.filter((p) => p.in === "query")) addFieldOption(command, param);
3706
+ if (op.body) {
3707
+ for (const field of op.body.fields) addFieldOption(command, field);
3708
+ command.option("--json <object>", 'The whole body as JSON; flags override its fields. "-" reads stdin.');
3709
+ }
3710
+ if (op.response === "csv") command.option("--out <file>", "Write the CSV to a file instead of stdout.");
3711
+ command.action(async (...args) => {
3712
+ const cmd = args[args.length - 1];
3713
+ const positional = args.slice(0, -2);
3714
+ const flags = cmd.opts();
3715
+ const globals = program.opts();
3716
+ try {
3717
+ let jsonBody = typeof flags.json === "string" ? flags.json : void 0;
3718
+ if (jsonBody === "-") jsonBody = await readStdin();
3719
+ const request = buildRequest(op, positional, flags, jsonBody);
3720
+ const settings = settingsOrFail(globals, op.operationId !== "getHealth");
3721
+ const outcome = await runOperation(clientFor(settings), op, request);
3722
+ if (!outcome.ok) fail(errorEnvelope(outcome));
3723
+ if (op.response === "csv") {
3724
+ const csv = typeof outcome.value === "string" ? outcome.value : "";
3725
+ if (typeof flags.out === "string") {
3726
+ writeFileSync2(flags.out, csv);
3727
+ print({ ok: true, file: flags.out, bytes: Buffer.byteLength(csv) }, globals);
3728
+ } else {
3729
+ process.stdout.write(csv);
3730
+ }
3731
+ return;
3732
+ }
3733
+ print(outcome.value === void 0 ? { ok: true, status: outcome.status } : outcome.value, globals);
3734
+ } catch (err) {
3735
+ if (err instanceof UsageError) fail({ error: { code: "usage", message: err.message } }, 2);
3736
+ throw err;
3737
+ }
3738
+ });
3739
+ }
3740
+ async function readStdin() {
3741
+ const chunks = [];
3742
+ for await (const chunk of process.stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
3743
+ return Buffer.concat(chunks).toString("utf8");
3744
+ }
3745
+ function registerAuth() {
3746
+ program.command("auth:set").description("Store an API key (and optionally the API host) in ~/.mentio/config.json").requiredOption("--key <key>", "API key from the dashboard or POST /v1/api-keys (mk_live_...)").option("--url <url>", "API host for a self-hosted deployment").action((flags) => {
3747
+ const path = writeConfig({ apiKey: flags.key, ...flags.url ? { apiUrl: flags.url } : {} });
3748
+ print({ ok: true, file: path, key: keyPrefix(flags.key) }, program.opts());
3749
+ });
3750
+ program.command("auth:logout").description("Remove the stored API key").action(() => {
3751
+ const path = writeConfig({ apiKey: null });
3752
+ print({ ok: true, file: path }, program.opts());
3753
+ });
3754
+ program.command("auth:check").description("Verify the key: which workspace it belongs to and where it came from").action(async () => {
3755
+ const globals = program.opts();
3756
+ const settings = settingsOrFail(globals, true);
3757
+ const result = await clientFor(settings).request({ method: "GET", url: "/v1/company", security: BEARER, throwOnError: false });
3758
+ if (result.error !== void 0 || !result.response?.ok) {
3759
+ fail(errorEnvelope({ ok: false, status: result.response?.status ?? 0, value: result.error }));
3760
+ }
3761
+ const company = result.data;
3762
+ print({ ok: true, workspace: company?.name ?? null, key: keyPrefix(settings.apiKey ?? ""), source: settings.source, apiUrl: settings.apiUrl }, globals);
3763
+ });
3764
+ }
3765
+ function registerWatch() {
3766
+ const search = OPERATIONS.find((op) => op.operationId === "searchMentions");
3767
+ if (!search) return;
3768
+ const command = program.command("mentions:watch").description("Follow the feed: print each new mention as one JSON line (tail -f for mentions)").option("--interval <seconds>", "Seconds between polls", "30").option("--from-start", "Print the current newest page first instead of only what arrives next");
3769
+ const skip = /* @__PURE__ */ new Set(["cursor", "limit", "sort", "since", "until"]);
3770
+ const filters = search.params.filter((p) => p.in === "query" && !skip.has(p.name));
3771
+ for (const param of filters) addFieldOption(command, param);
3772
+ command.action(async (flags) => {
3773
+ const globals = program.opts();
3774
+ const settings = settingsOrFail(globals, true);
3775
+ const query = {};
3776
+ try {
3777
+ for (const param of filters) {
3778
+ const raw = flags[param.name];
3779
+ if (raw === void 0) continue;
3780
+ const value = coerce(param, String(raw));
3781
+ if (value !== null && typeof value !== "object") query[param.name] = value;
3782
+ }
3783
+ } catch (err) {
3784
+ if (err instanceof UsageError) fail({ error: { code: "usage", message: err.message } }, 2);
3785
+ throw err;
3786
+ }
3787
+ const seconds = Number(flags.interval);
3788
+ if (!Number.isFinite(seconds) || seconds < 5) fail({ error: { code: "usage", message: "--interval must be at least 5 seconds" } }, 2);
3789
+ const controller = new AbortController();
3790
+ process.on("SIGINT", () => controller.abort());
3791
+ process.on("SIGTERM", () => controller.abort());
3792
+ await watchMentions({
3793
+ client: clientFor(settings),
3794
+ query,
3795
+ intervalMs: seconds * 1e3,
3796
+ fromStart: flags.fromStart === true,
3797
+ write: (line) => process.stdout.write(`${line}
3798
+ `),
3799
+ warn: (line) => process.stderr.write(`${line}
3800
+ `),
3801
+ signal: controller.signal
3802
+ });
3803
+ });
3804
+ }
3805
+ function registerMcp() {
3806
+ program.command("mcp:config").description("Print the MCP client configuration for the Mentio MCP server, key included").addOption(new Option("--client <kind>", "Which client to print for").choices(["claude", "cursor", "vscode", "generic"]).default("claude")).option("--url <url>", "MCP server URL", DEFAULT_MCP_URL).action((flags) => {
3807
+ const globals = program.opts();
3808
+ const settings = resolveSettings({ apiKey: globals.apiKey, apiUrl: globals.apiUrl });
3809
+ const key = settings.apiKey ?? "mk_live_...";
3810
+ if (!settings.apiKey) process.stderr.write("No API key configured; printing a placeholder. Run `mentio auth:set --key ...` first.\n");
3811
+ process.stdout.write(`${mcpConfig(flags.client, flags.url, key)}
3812
+ `);
3813
+ });
3814
+ }
3815
+ program.name("mentio").description("Command line for the Mentio API. Commands are noun:verb; every endpoint has one.").version(package_default.version, "-V, --version").option("--api-key <key>", "API key (overrides MENTIO_API_KEY and the stored key)").option("--api-url <url>", "API host (overrides MENTIO_API_URL and the stored host)").option("--pretty", "Indent JSON output (the default on a terminal)").option("--table", "Render lists as a table").showHelpAfterError("(run with --help for usage)").configureHelp({ sortSubcommands: true });
3816
+ registerAuth();
3817
+ for (const op of OPERATIONS) registerOperation(op);
3818
+ registerWatch();
3819
+ registerMcp();
3820
+ program.parseAsync(process.argv).catch((err) => {
3821
+ fail({ error: { code: "internal_error", message: err instanceof Error ? err.message : String(err) } });
3822
+ });
3823
+ //# sourceMappingURL=index.js.map