@jaypie/mcp 0.8.139 → 0.8.141

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.
@@ -1,850 +0,0 @@
1
- import * as https from 'node:https';
2
-
3
- /**
4
- * Datadog API integration module
5
- */
6
- const nullLogger = {
7
- info: () => { },
8
- error: () => { },
9
- };
10
- /**
11
- * Get Datadog credentials from environment variables
12
- */
13
- function getDatadogCredentials() {
14
- const apiKey = process.env.DATADOG_API_KEY || process.env.DD_API_KEY;
15
- const appKey = process.env.DATADOG_APP_KEY ||
16
- process.env.DATADOG_APPLICATION_KEY ||
17
- process.env.DD_APP_KEY ||
18
- process.env.DD_APPLICATION_KEY;
19
- if (!apiKey || !appKey) {
20
- return null;
21
- }
22
- return { apiKey, appKey };
23
- }
24
- /**
25
- * Validate Datadog setup without making API calls
26
- */
27
- function validateDatadogSetup() {
28
- const apiKeySource = process.env.DATADOG_API_KEY
29
- ? "DATADOG_API_KEY"
30
- : process.env.DD_API_KEY
31
- ? "DD_API_KEY"
32
- : null;
33
- const appKeySource = process.env.DATADOG_APP_KEY
34
- ? "DATADOG_APP_KEY"
35
- : process.env.DATADOG_APPLICATION_KEY
36
- ? "DATADOG_APPLICATION_KEY"
37
- : process.env.DD_APP_KEY
38
- ? "DD_APP_KEY"
39
- : process.env.DD_APPLICATION_KEY
40
- ? "DD_APPLICATION_KEY"
41
- : null;
42
- return {
43
- apiKey: { present: apiKeySource !== null, source: apiKeySource },
44
- appKey: { present: appKeySource !== null, source: appKeySource },
45
- success: apiKeySource !== null && appKeySource !== null,
46
- };
47
- }
48
- /**
49
- * Validate Datadog API credentials
50
- */
51
- async function validateDatadogCredentials(credentials) {
52
- return new Promise((resolve) => {
53
- const options = {
54
- hostname: "api.datadoghq.com",
55
- port: 443,
56
- path: "/api/v1/validate",
57
- method: "GET",
58
- headers: {
59
- "DD-API-KEY": credentials.apiKey,
60
- "DD-APPLICATION-KEY": credentials.appKey,
61
- },
62
- };
63
- const req = https.request(options, (res) => {
64
- let data = "";
65
- res.on("data", (chunk) => {
66
- data += chunk.toString();
67
- });
68
- res.on("end", () => {
69
- if (res.statusCode === 200) {
70
- resolve({ valid: true });
71
- }
72
- else {
73
- resolve({
74
- valid: false,
75
- error: `Datadog API returned status ${res.statusCode}: ${data}`,
76
- });
77
- }
78
- });
79
- });
80
- req.on("error", (error) => {
81
- resolve({ valid: false, error: error.message });
82
- });
83
- req.end();
84
- });
85
- }
86
- /**
87
- * Build query string from environment variables and options
88
- */
89
- function buildDatadogQuery(options) {
90
- const ddEnv = process.env.DD_ENV;
91
- const ddQuery = process.env.DD_QUERY;
92
- const queryParts = [];
93
- // Add source (parameter only, no env var default)
94
- if (options.source) {
95
- queryParts.push(`source:${options.source}`);
96
- }
97
- // Add env (parameter > env var)
98
- const effectiveEnv = options.env || ddEnv;
99
- if (effectiveEnv) {
100
- queryParts.push(`env:${effectiveEnv}`);
101
- }
102
- // Add service (parameter only, no env var default)
103
- if (options.service) {
104
- queryParts.push(`service:${options.service}`);
105
- }
106
- // Add base query from DD_QUERY if available
107
- if (ddQuery) {
108
- queryParts.push(ddQuery);
109
- }
110
- // Add user-provided query terms
111
- if (options.query) {
112
- queryParts.push(options.query);
113
- }
114
- return queryParts.join(" ");
115
- }
116
- /**
117
- * Search Datadog logs
118
- */
119
- async function searchDatadogLogs(credentials, options = {}, logger = nullLogger) {
120
- const effectiveQuery = buildDatadogQuery(options);
121
- const effectiveFrom = options.from || "now-15m";
122
- const effectiveTo = options.to || "now";
123
- const effectiveLimit = Math.min(options.limit || 50, 1000);
124
- const effectiveSort = options.sort || "-timestamp";
125
- logger.info(`Effective query: ${effectiveQuery}`);
126
- logger.info(`Search params: from=${effectiveFrom}, to=${effectiveTo}, limit=${effectiveLimit}, sort=${effectiveSort}`);
127
- const requestBody = JSON.stringify({
128
- filter: {
129
- query: effectiveQuery,
130
- from: effectiveFrom,
131
- to: effectiveTo,
132
- },
133
- page: {
134
- limit: effectiveLimit,
135
- },
136
- sort: effectiveSort,
137
- });
138
- return new Promise((resolve) => {
139
- const requestOptions = {
140
- hostname: "api.datadoghq.com",
141
- port: 443,
142
- path: "/api/v2/logs/events/search",
143
- method: "POST",
144
- headers: {
145
- "Content-Type": "application/json",
146
- "DD-API-KEY": credentials.apiKey,
147
- "DD-APPLICATION-KEY": credentials.appKey,
148
- "Content-Length": Buffer.byteLength(requestBody),
149
- },
150
- };
151
- const req = https.request(requestOptions, (res) => {
152
- let data = "";
153
- res.on("data", (chunk) => {
154
- data += chunk.toString();
155
- });
156
- res.on("end", () => {
157
- logger.info(`Response status: ${res.statusCode}`);
158
- if (res.statusCode !== 200) {
159
- logger.error(`Datadog API error: ${res.statusCode}`);
160
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
161
- if (res.statusCode === 400) {
162
- errorMessage = `Invalid query syntax. Check your query: "${effectiveQuery}". Datadog error: ${data}`;
163
- }
164
- else if (res.statusCode === 403) {
165
- errorMessage =
166
- "Access denied. Verify your API and Application keys have logs_read permission.";
167
- }
168
- else if (res.statusCode === 429) {
169
- errorMessage =
170
- "Rate limited by Datadog. Wait a moment and try again, or reduce your query scope.";
171
- }
172
- resolve({
173
- success: false,
174
- query: effectiveQuery,
175
- timeRange: { from: effectiveFrom, to: effectiveTo },
176
- logs: [],
177
- error: errorMessage,
178
- });
179
- return;
180
- }
181
- try {
182
- const response = JSON.parse(data);
183
- const logs = (response.data || []).map((log) => {
184
- const attrs = log.attributes || {};
185
- return {
186
- id: log.id,
187
- timestamp: attrs.timestamp,
188
- status: attrs.status,
189
- service: attrs.service,
190
- message: attrs.message,
191
- attributes: attrs.attributes,
192
- };
193
- });
194
- logger.info(`Retrieved ${logs.length} log entries`);
195
- resolve({
196
- success: true,
197
- query: effectiveQuery,
198
- timeRange: { from: effectiveFrom, to: effectiveTo },
199
- logs,
200
- });
201
- }
202
- catch (parseError) {
203
- logger.error("Failed to parse Datadog response:", parseError);
204
- resolve({
205
- success: false,
206
- query: effectiveQuery,
207
- timeRange: { from: effectiveFrom, to: effectiveTo },
208
- logs: [],
209
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
210
- });
211
- }
212
- });
213
- });
214
- req.on("error", (error) => {
215
- logger.error("Request error:", error);
216
- resolve({
217
- success: false,
218
- query: effectiveQuery,
219
- timeRange: { from: effectiveFrom, to: effectiveTo },
220
- logs: [],
221
- error: `Connection error: ${error.message}`,
222
- });
223
- });
224
- req.write(requestBody);
225
- req.end();
226
- });
227
- }
228
- /**
229
- * Aggregate Datadog logs using the Analytics API
230
- * Groups logs by specified fields and computes aggregations
231
- */
232
- async function aggregateDatadogLogs(credentials, options, logger = nullLogger) {
233
- const effectiveQuery = buildDatadogQuery(options);
234
- const effectiveFrom = options.from || "now-15m";
235
- const effectiveTo = options.to || "now";
236
- const groupBy = options.groupBy;
237
- const compute = options.compute || [{ aggregation: "count" }];
238
- logger.info(`Analytics query: ${effectiveQuery}`);
239
- logger.info(`Group by: ${groupBy.join(", ")}`);
240
- logger.info(`Time range: ${effectiveFrom} to ${effectiveTo}`);
241
- // Build compute array - each item needs aggregation and type
242
- const computeItems = compute.map((c) => {
243
- const item = {
244
- aggregation: c.aggregation,
245
- type: "total",
246
- };
247
- if (c.metric) {
248
- item.metric = c.metric;
249
- }
250
- return item;
251
- });
252
- // Build group_by with proper sort configuration
253
- const groupByItems = groupBy.map((field) => {
254
- const item = {
255
- facet: field,
256
- limit: 100,
257
- sort: {
258
- type: "measure",
259
- order: "desc",
260
- aggregation: compute[0]?.aggregation || "count",
261
- },
262
- };
263
- return item;
264
- });
265
- const requestBody = JSON.stringify({
266
- filter: {
267
- query: effectiveQuery,
268
- from: effectiveFrom,
269
- to: effectiveTo,
270
- },
271
- group_by: groupByItems,
272
- compute: computeItems,
273
- page: {
274
- limit: 100,
275
- },
276
- });
277
- return new Promise((resolve) => {
278
- const requestOptions = {
279
- hostname: "api.datadoghq.com",
280
- port: 443,
281
- path: "/api/v2/logs/analytics/aggregate",
282
- method: "POST",
283
- headers: {
284
- "Content-Type": "application/json",
285
- "DD-API-KEY": credentials.apiKey,
286
- "DD-APPLICATION-KEY": credentials.appKey,
287
- "Content-Length": Buffer.byteLength(requestBody),
288
- },
289
- };
290
- const req = https.request(requestOptions, (res) => {
291
- let data = "";
292
- res.on("data", (chunk) => {
293
- data += chunk.toString();
294
- });
295
- res.on("end", () => {
296
- logger.info(`Response status: ${res.statusCode}`);
297
- if (res.statusCode !== 200) {
298
- logger.error(`Datadog Analytics API error: ${res.statusCode}`);
299
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
300
- if (res.statusCode === 400) {
301
- errorMessage = `Invalid query or groupBy fields. Verify facet names exist: ${groupBy.join(", ")}. Datadog error: ${data}`;
302
- }
303
- else if (res.statusCode === 403) {
304
- errorMessage =
305
- "Access denied. Verify your API and Application keys have logs_read permission.";
306
- }
307
- else if (res.statusCode === 429) {
308
- errorMessage =
309
- "Rate limited by Datadog. Wait a moment and try again, or reduce your query scope.";
310
- }
311
- resolve({
312
- success: false,
313
- query: effectiveQuery,
314
- timeRange: { from: effectiveFrom, to: effectiveTo },
315
- groupBy,
316
- buckets: [],
317
- error: errorMessage,
318
- });
319
- return;
320
- }
321
- try {
322
- const response = JSON.parse(data);
323
- const buckets = (response.data?.buckets || []).map((bucket) => ({
324
- by: bucket.by || {},
325
- computes: bucket.computes || {},
326
- }));
327
- logger.info(`Retrieved ${buckets.length} aggregation buckets`);
328
- resolve({
329
- success: true,
330
- query: effectiveQuery,
331
- timeRange: { from: effectiveFrom, to: effectiveTo },
332
- groupBy,
333
- buckets,
334
- });
335
- }
336
- catch (parseError) {
337
- logger.error("Failed to parse Datadog analytics response:", parseError);
338
- resolve({
339
- success: false,
340
- query: effectiveQuery,
341
- timeRange: { from: effectiveFrom, to: effectiveTo },
342
- groupBy,
343
- buckets: [],
344
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
345
- });
346
- }
347
- });
348
- });
349
- req.on("error", (error) => {
350
- logger.error("Request error:", error);
351
- resolve({
352
- success: false,
353
- query: effectiveQuery,
354
- timeRange: { from: effectiveFrom, to: effectiveTo },
355
- groupBy,
356
- buckets: [],
357
- error: `Connection error: ${error.message}`,
358
- });
359
- });
360
- req.write(requestBody);
361
- req.end();
362
- });
363
- }
364
- /**
365
- * List Datadog monitors with optional filtering
366
- */
367
- async function listDatadogMonitors(credentials, options = {}, logger = nullLogger) {
368
- logger.info("Fetching Datadog monitors");
369
- const queryParams = new URLSearchParams();
370
- if (options.tags && options.tags.length > 0) {
371
- queryParams.set("tags", options.tags.join(","));
372
- }
373
- if (options.monitorTags && options.monitorTags.length > 0) {
374
- queryParams.set("monitor_tags", options.monitorTags.join(","));
375
- }
376
- if (options.name) {
377
- queryParams.set("name", options.name);
378
- }
379
- const queryString = queryParams.toString();
380
- const path = `/api/v1/monitor${queryString ? `?${queryString}` : ""}`;
381
- logger.info(`Request path: ${path}`);
382
- return new Promise((resolve) => {
383
- const requestOptions = {
384
- hostname: "api.datadoghq.com",
385
- port: 443,
386
- path,
387
- method: "GET",
388
- headers: {
389
- "DD-API-KEY": credentials.apiKey,
390
- "DD-APPLICATION-KEY": credentials.appKey,
391
- },
392
- };
393
- const req = https.request(requestOptions, (res) => {
394
- let data = "";
395
- res.on("data", (chunk) => {
396
- data += chunk.toString();
397
- });
398
- res.on("end", () => {
399
- logger.info(`Response status: ${res.statusCode}`);
400
- if (res.statusCode !== 200) {
401
- logger.error(`Datadog Monitors API error: ${res.statusCode}`);
402
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
403
- if (res.statusCode === 403) {
404
- errorMessage =
405
- "Access denied. Verify your API and Application keys have monitors_read permission.";
406
- }
407
- else if (res.statusCode === 429) {
408
- errorMessage =
409
- "Rate limited by Datadog. Wait a moment and try again.";
410
- }
411
- resolve({
412
- success: false,
413
- monitors: [],
414
- error: errorMessage,
415
- });
416
- return;
417
- }
418
- try {
419
- const response = JSON.parse(data);
420
- let monitors = response.map((monitor) => ({
421
- id: monitor.id,
422
- name: monitor.name,
423
- type: monitor.type,
424
- status: monitor.overall_state || "Unknown",
425
- message: monitor.message,
426
- tags: monitor.tags || [],
427
- priority: monitor.priority,
428
- query: monitor.query,
429
- overallState: monitor.overall_state,
430
- }));
431
- // Filter by status if specified
432
- if (options.status && options.status.length > 0) {
433
- monitors = monitors.filter((m) => options.status.includes(m.status));
434
- }
435
- logger.info(`Retrieved ${monitors.length} monitors`);
436
- resolve({
437
- success: true,
438
- monitors,
439
- });
440
- }
441
- catch (parseError) {
442
- logger.error("Failed to parse Datadog monitors response:", parseError);
443
- resolve({
444
- success: false,
445
- monitors: [],
446
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
447
- });
448
- }
449
- });
450
- });
451
- req.on("error", (error) => {
452
- logger.error("Request error:", error);
453
- resolve({
454
- success: false,
455
- monitors: [],
456
- error: `Connection error: ${error.message}`,
457
- });
458
- });
459
- req.end();
460
- });
461
- }
462
- /**
463
- * List Datadog Synthetic tests
464
- */
465
- async function listDatadogSynthetics(credentials, options = {}, logger = nullLogger) {
466
- logger.info("Fetching Datadog Synthetic tests");
467
- return new Promise((resolve) => {
468
- const requestOptions = {
469
- hostname: "api.datadoghq.com",
470
- port: 443,
471
- path: "/api/v1/synthetics/tests",
472
- method: "GET",
473
- headers: {
474
- "DD-API-KEY": credentials.apiKey,
475
- "DD-APPLICATION-KEY": credentials.appKey,
476
- },
477
- };
478
- const req = https.request(requestOptions, (res) => {
479
- let data = "";
480
- res.on("data", (chunk) => {
481
- data += chunk.toString();
482
- });
483
- res.on("end", () => {
484
- logger.info(`Response status: ${res.statusCode}`);
485
- if (res.statusCode !== 200) {
486
- logger.error(`Datadog Synthetics API error: ${res.statusCode}`);
487
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
488
- if (res.statusCode === 403) {
489
- errorMessage =
490
- "Access denied. Verify your API and Application keys have synthetics_read permission.";
491
- }
492
- else if (res.statusCode === 429) {
493
- errorMessage =
494
- "Rate limited by Datadog. Wait a moment and try again.";
495
- }
496
- resolve({
497
- success: false,
498
- tests: [],
499
- error: errorMessage,
500
- });
501
- return;
502
- }
503
- try {
504
- const response = JSON.parse(data);
505
- let tests = (response.tests || []).map((test) => ({
506
- publicId: test.public_id,
507
- name: test.name,
508
- type: test.type,
509
- status: test.status,
510
- tags: test.tags || [],
511
- locations: test.locations || [],
512
- message: test.message,
513
- }));
514
- // Filter by type if specified
515
- if (options.type) {
516
- tests = tests.filter((t) => t.type === options.type);
517
- }
518
- // Filter by tags if specified
519
- if (options.tags && options.tags.length > 0) {
520
- tests = tests.filter((t) => options.tags.some((tag) => t.tags.includes(tag)));
521
- }
522
- logger.info(`Retrieved ${tests.length} synthetic tests`);
523
- resolve({
524
- success: true,
525
- tests,
526
- });
527
- }
528
- catch (parseError) {
529
- logger.error("Failed to parse Datadog synthetics response:", parseError);
530
- resolve({
531
- success: false,
532
- tests: [],
533
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
534
- });
535
- }
536
- });
537
- });
538
- req.on("error", (error) => {
539
- logger.error("Request error:", error);
540
- resolve({
541
- success: false,
542
- tests: [],
543
- error: `Connection error: ${error.message}`,
544
- });
545
- });
546
- req.end();
547
- });
548
- }
549
- /**
550
- * Get recent results for a specific Synthetic test
551
- */
552
- async function getDatadogSyntheticResults(credentials, publicId, logger = nullLogger) {
553
- logger.info(`Fetching results for Synthetic test: ${publicId}`);
554
- return new Promise((resolve) => {
555
- const requestOptions = {
556
- hostname: "api.datadoghq.com",
557
- port: 443,
558
- path: `/api/v1/synthetics/tests/${publicId}/results`,
559
- method: "GET",
560
- headers: {
561
- "DD-API-KEY": credentials.apiKey,
562
- "DD-APPLICATION-KEY": credentials.appKey,
563
- },
564
- };
565
- const req = https.request(requestOptions, (res) => {
566
- let data = "";
567
- res.on("data", (chunk) => {
568
- data += chunk.toString();
569
- });
570
- res.on("end", () => {
571
- logger.info(`Response status: ${res.statusCode}`);
572
- if (res.statusCode !== 200) {
573
- logger.error(`Datadog Synthetics Results API error: ${res.statusCode}`);
574
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
575
- if (res.statusCode === 403) {
576
- errorMessage =
577
- "Access denied. Verify your API and Application keys have synthetics_read permission.";
578
- }
579
- else if (res.statusCode === 404) {
580
- errorMessage = `Synthetic test '${publicId}' not found. Use datadog_synthetics (without testId) to list available tests.`;
581
- }
582
- else if (res.statusCode === 429) {
583
- errorMessage =
584
- "Rate limited by Datadog. Wait a moment and try again.";
585
- }
586
- resolve({
587
- success: false,
588
- publicId,
589
- results: [],
590
- error: errorMessage,
591
- });
592
- return;
593
- }
594
- try {
595
- const response = JSON.parse(data);
596
- const results = (response.results || []).map((result) => ({
597
- publicId,
598
- resultId: result.result_id,
599
- status: result.status,
600
- checkTime: result.check_time,
601
- passed: result.result?.passed ?? result.status === 0,
602
- location: result.dc_id?.toString(),
603
- }));
604
- logger.info(`Retrieved ${results.length} synthetic results`);
605
- resolve({
606
- success: true,
607
- publicId,
608
- results,
609
- });
610
- }
611
- catch (parseError) {
612
- logger.error("Failed to parse Datadog synthetic results:", parseError);
613
- resolve({
614
- success: false,
615
- publicId,
616
- results: [],
617
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
618
- });
619
- }
620
- });
621
- });
622
- req.on("error", (error) => {
623
- logger.error("Request error:", error);
624
- resolve({
625
- success: false,
626
- publicId,
627
- results: [],
628
- error: `Connection error: ${error.message}`,
629
- });
630
- });
631
- req.end();
632
- });
633
- }
634
- /**
635
- * Query Datadog metrics
636
- */
637
- async function queryDatadogMetrics(credentials, options, logger = nullLogger) {
638
- logger.info(`Querying metrics: ${options.query}`);
639
- logger.info(`Time range: ${options.from} to ${options.to}`);
640
- const queryParams = new URLSearchParams({
641
- query: options.query,
642
- from: options.from.toString(),
643
- to: options.to.toString(),
644
- });
645
- return new Promise((resolve) => {
646
- const requestOptions = {
647
- hostname: "api.datadoghq.com",
648
- port: 443,
649
- path: `/api/v1/query?${queryParams.toString()}`,
650
- method: "GET",
651
- headers: {
652
- "DD-API-KEY": credentials.apiKey,
653
- "DD-APPLICATION-KEY": credentials.appKey,
654
- },
655
- };
656
- const req = https.request(requestOptions, (res) => {
657
- let data = "";
658
- res.on("data", (chunk) => {
659
- data += chunk.toString();
660
- });
661
- res.on("end", () => {
662
- logger.info(`Response status: ${res.statusCode}`);
663
- if (res.statusCode !== 200) {
664
- logger.error(`Datadog Metrics API error: ${res.statusCode}`);
665
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
666
- if (res.statusCode === 400) {
667
- errorMessage = `Invalid metric query. Check format: 'aggregation:metric.name{tags}'. Query: "${options.query}". Datadog error: ${data}`;
668
- }
669
- else if (res.statusCode === 403) {
670
- errorMessage =
671
- "Access denied. Verify your API and Application keys have metrics_read permission.";
672
- }
673
- else if (res.statusCode === 429) {
674
- errorMessage =
675
- "Rate limited by Datadog. Wait a moment and try again, or reduce your time range.";
676
- }
677
- resolve({
678
- success: false,
679
- query: options.query,
680
- timeRange: { from: options.from, to: options.to },
681
- series: [],
682
- error: errorMessage,
683
- });
684
- return;
685
- }
686
- try {
687
- const response = JSON.parse(data);
688
- const series = (response.series || []).map((s) => ({
689
- metric: s.metric,
690
- scope: s.scope,
691
- pointlist: s.pointlist,
692
- unit: s.unit?.[0]?.name,
693
- }));
694
- logger.info(`Retrieved ${series.length} metric series`);
695
- resolve({
696
- success: true,
697
- query: options.query,
698
- timeRange: { from: options.from, to: options.to },
699
- series,
700
- });
701
- }
702
- catch (parseError) {
703
- logger.error("Failed to parse Datadog metrics response:", parseError);
704
- resolve({
705
- success: false,
706
- query: options.query,
707
- timeRange: { from: options.from, to: options.to },
708
- series: [],
709
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
710
- });
711
- }
712
- });
713
- });
714
- req.on("error", (error) => {
715
- logger.error("Request error:", error);
716
- resolve({
717
- success: false,
718
- query: options.query,
719
- timeRange: { from: options.from, to: options.to },
720
- series: [],
721
- error: `Connection error: ${error.message}`,
722
- });
723
- });
724
- req.end();
725
- });
726
- }
727
- /**
728
- * Search Datadog RUM events
729
- */
730
- async function searchDatadogRum(credentials, options = {}, logger = nullLogger) {
731
- const effectiveQuery = options.query || "*";
732
- const effectiveFrom = options.from || "now-15m";
733
- const effectiveTo = options.to || "now";
734
- const effectiveLimit = Math.min(options.limit || 50, 1000);
735
- const effectiveSort = options.sort || "-timestamp";
736
- logger.info(`RUM query: ${effectiveQuery}`);
737
- logger.info(`Time range: ${effectiveFrom} to ${effectiveTo}`);
738
- const requestBody = JSON.stringify({
739
- filter: {
740
- query: effectiveQuery,
741
- from: effectiveFrom,
742
- to: effectiveTo,
743
- },
744
- page: {
745
- limit: effectiveLimit,
746
- },
747
- sort: effectiveSort,
748
- });
749
- return new Promise((resolve) => {
750
- const requestOptions = {
751
- hostname: "api.datadoghq.com",
752
- port: 443,
753
- path: "/api/v2/rum/events/search",
754
- method: "POST",
755
- headers: {
756
- "Content-Type": "application/json",
757
- "DD-API-KEY": credentials.apiKey,
758
- "DD-APPLICATION-KEY": credentials.appKey,
759
- "Content-Length": Buffer.byteLength(requestBody),
760
- },
761
- };
762
- const req = https.request(requestOptions, (res) => {
763
- let data = "";
764
- res.on("data", (chunk) => {
765
- data += chunk.toString();
766
- });
767
- res.on("end", () => {
768
- logger.info(`Response status: ${res.statusCode}`);
769
- if (res.statusCode !== 200) {
770
- logger.error(`Datadog RUM API error: ${res.statusCode}`);
771
- let errorMessage = `Datadog API returned status ${res.statusCode}: ${data}`;
772
- // Check for specific "No valid indexes" error which means no RUM app is configured
773
- if (data.includes("No valid indexes")) {
774
- errorMessage =
775
- "No RUM application found. Ensure you have a RUM application configured in Datadog and it has collected data. " +
776
- "You can create a RUM application at https://app.datadoghq.com/rum/list";
777
- }
778
- else if (res.statusCode === 400) {
779
- errorMessage = `Invalid RUM query. Check syntax: "${effectiveQuery}". Datadog error: ${data}`;
780
- }
781
- else if (res.statusCode === 403) {
782
- errorMessage =
783
- "Access denied. Verify your API and Application keys have rum_read permission.";
784
- }
785
- else if (res.statusCode === 429) {
786
- errorMessage =
787
- "Rate limited by Datadog. Wait a moment and try again, or reduce your query scope.";
788
- }
789
- resolve({
790
- success: false,
791
- query: effectiveQuery,
792
- timeRange: { from: effectiveFrom, to: effectiveTo },
793
- events: [],
794
- error: errorMessage,
795
- });
796
- return;
797
- }
798
- try {
799
- const response = JSON.parse(data);
800
- const events = (response.data || []).map((event) => {
801
- const attrs = event.attributes?.attributes || {};
802
- return {
803
- id: event.id,
804
- type: event.type,
805
- timestamp: event.attributes?.timestamp,
806
- sessionId: attrs.session?.id,
807
- viewUrl: attrs.view?.url,
808
- viewName: attrs.view?.name,
809
- errorMessage: attrs.error?.message,
810
- errorType: attrs.error?.type,
811
- attributes: attrs,
812
- };
813
- });
814
- logger.info(`Retrieved ${events.length} RUM events`);
815
- resolve({
816
- success: true,
817
- query: effectiveQuery,
818
- timeRange: { from: effectiveFrom, to: effectiveTo },
819
- events,
820
- });
821
- }
822
- catch (parseError) {
823
- logger.error("Failed to parse Datadog RUM response:", parseError);
824
- resolve({
825
- success: false,
826
- query: effectiveQuery,
827
- timeRange: { from: effectiveFrom, to: effectiveTo },
828
- events: [],
829
- error: `Failed to parse response: ${parseError instanceof Error ? parseError.message : "Unknown error"}`,
830
- });
831
- }
832
- });
833
- });
834
- req.on("error", (error) => {
835
- logger.error("Request error:", error);
836
- resolve({
837
- success: false,
838
- query: effectiveQuery,
839
- timeRange: { from: effectiveFrom, to: effectiveTo },
840
- events: [],
841
- error: `Connection error: ${error.message}`,
842
- });
843
- });
844
- req.write(requestBody);
845
- req.end();
846
- });
847
- }
848
-
849
- export { aggregateDatadogLogs, buildDatadogQuery, getDatadogCredentials, getDatadogSyntheticResults, listDatadogMonitors, listDatadogSynthetics, queryDatadogMetrics, searchDatadogLogs, searchDatadogRum, validateDatadogCredentials, validateDatadogSetup };
850
- //# sourceMappingURL=datadog.js.map