autotel-devtools 8.0.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/cli.cjs +76 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +76 -7
- package/dist/cli.js.map +1 -1
- package/dist/{error-aggregator-CAk_pt3Z.d.ts → error-aggregator-D0Uu5r38.d.ts} +1 -1
- package/dist/{error-aggregator-CbLiuot4.d.cts → error-aggregator-D1Mr221Y.d.cts} +1 -1
- package/dist/{exporter-DjLkU621.d.cts → exporter-De6p4iAD.d.cts} +6 -0
- package/dist/{exporter-DjLkU621.d.ts → exporter-De6p4iAD.d.ts} +6 -0
- package/dist/genai/index.cjs +7 -2
- package/dist/genai/index.cjs.map +1 -1
- package/dist/genai/index.d.cts +6 -2
- package/dist/genai/index.d.ts +6 -2
- package/dist/genai/index.js +7 -2
- package/dist/genai/index.js.map +1 -1
- package/dist/index.cjs +76 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +76 -6
- package/dist/index.js.map +1 -1
- package/dist/server/exporter.d.cts +1 -1
- package/dist/server/exporter.d.ts +1 -1
- package/dist/server/index.cjs +76 -4
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +30 -5
- package/dist/server/index.d.ts +30 -5
- package/dist/server/index.js +73 -5
- package/dist/server/index.js.map +1 -1
- package/dist/widget.global.js +10 -9
- package/package.json +1 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { D as DevtoolsServer, L as LogData, T as TraceData, b as DevtoolsData } from '../exporter-
|
|
2
|
-
export { d as DevtoolsServerOptions, a as DevtoolsSpanExporter, E as ErrorGroup, c as ErrorOccurrence, M as MetricData, S as SpanData } from '../exporter-
|
|
1
|
+
import { D as DevtoolsServer, L as LogData, T as TraceData, b as DevtoolsData } from '../exporter-De6p4iAD.cjs';
|
|
2
|
+
export { d as DevtoolsServerOptions, a as DevtoolsSpanExporter, E as ErrorGroup, c as ErrorOccurrence, M as MetricData, S as SpanData } from '../exporter-De6p4iAD.cjs';
|
|
3
3
|
export { DevtoolsLogExporter } from './log-exporter.cjs';
|
|
4
4
|
export { DevtoolsRemoteExporter, DevtoolsRemoteExporterOptions } from './remote-exporter.cjs';
|
|
5
|
-
export { E as ErrorAggregator } from '../error-aggregator-
|
|
5
|
+
export { E as ErrorAggregator } from '../error-aggregator-D1Mr221Y.cjs';
|
|
6
6
|
import { Server } from 'node:http';
|
|
7
7
|
import '@opentelemetry/sdk-trace-base';
|
|
8
8
|
import '@opentelemetry/core';
|
|
@@ -12,9 +12,34 @@ interface HttpServerOptions {
|
|
|
12
12
|
port?: number;
|
|
13
13
|
host?: string;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
interface DevtoolsRoutesOptions {
|
|
16
|
+
/** Bound to a loopback host (the default). Enables the DNS-rebinding `Host`
|
|
17
|
+
* check on read endpoints; an explicit non-loopback bind opts out. */
|
|
18
|
+
loopbackOnly?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare function attachDevtoolsRoutes(httpServer: Server, devtools: DevtoolsServer, options?: DevtoolsRoutesOptions): void;
|
|
16
21
|
declare function createDevtoolsHttpServer(devtools: DevtoolsServer, _options?: HttpServerOptions): Server;
|
|
17
22
|
|
|
23
|
+
/** True for `localhost`, any `127.x.x.x`, and IPv6 loopback. Case-insensitive. */
|
|
24
|
+
declare function isLoopbackHostname(hostname: string): boolean;
|
|
25
|
+
/** True when the `Host` header names a loopback host. */
|
|
26
|
+
declare function hostHeaderIsLoopback(host: string): boolean;
|
|
27
|
+
/** True when an `Origin` header names a loopback origin. A malformed or opaque
|
|
28
|
+
* origin (e.g. the literal `null` from a sandboxed iframe) is treated as
|
|
29
|
+
* non-loopback. */
|
|
30
|
+
declare function originIsLoopback(origin: string): boolean;
|
|
31
|
+
interface GuardHeaders {
|
|
32
|
+
origin?: string;
|
|
33
|
+
host?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decide whether a request to a sensitive (read/mutate) endpoint is allowed.
|
|
37
|
+
* - A present, non-loopback `Origin` is always rejected (cross-origin read).
|
|
38
|
+
* - When `loopbackOnly`, a present, non-loopback `Host` is rejected (DNS
|
|
39
|
+
* rebinding). Skipped when the receiver is bound to a non-loopback host.
|
|
40
|
+
*/
|
|
41
|
+
declare function allowSensitiveRequest(headers: GuardHeaders, loopbackOnly: boolean): boolean;
|
|
42
|
+
|
|
18
43
|
declare function parseOtlpTraces(payload: unknown): TraceData[];
|
|
19
44
|
declare function parseOtlpLogs(payload: unknown): LogData[];
|
|
20
45
|
/**
|
|
@@ -64,4 +89,4 @@ declare function appendWithLimit<T>(items: T[], item: T, limit: number): T[];
|
|
|
64
89
|
declare function appendManyWithLimit<T>(items: T[], incoming: T[], limit: number): T[];
|
|
65
90
|
declare function applyTelemetryLimits(data: DevtoolsData, limits: TelemetryLimits): DevtoolsData;
|
|
66
91
|
|
|
67
|
-
export { DEVTOOLS_IDENTITY, DevtoolsData, DevtoolsServer, type HttpServerOptions, LogData, type PortHolder, type TelemetryLimits, TraceData, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, isProtobufContentType, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
|
92
|
+
export { DEVTOOLS_IDENTITY, DevtoolsData, type DevtoolsRoutesOptions, DevtoolsServer, type HttpServerOptions, LogData, type PortHolder, type TelemetryLimits, TraceData, allowSensitiveRequest, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, hostHeaderIsLoopback, isLoopbackHostname, isProtobufContentType, originIsLoopback, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { D as DevtoolsServer, L as LogData, T as TraceData, b as DevtoolsData } from '../exporter-
|
|
2
|
-
export { d as DevtoolsServerOptions, a as DevtoolsSpanExporter, E as ErrorGroup, c as ErrorOccurrence, M as MetricData, S as SpanData } from '../exporter-
|
|
1
|
+
import { D as DevtoolsServer, L as LogData, T as TraceData, b as DevtoolsData } from '../exporter-De6p4iAD.js';
|
|
2
|
+
export { d as DevtoolsServerOptions, a as DevtoolsSpanExporter, E as ErrorGroup, c as ErrorOccurrence, M as MetricData, S as SpanData } from '../exporter-De6p4iAD.js';
|
|
3
3
|
export { DevtoolsLogExporter } from './log-exporter.js';
|
|
4
4
|
export { DevtoolsRemoteExporter, DevtoolsRemoteExporterOptions } from './remote-exporter.js';
|
|
5
|
-
export { E as ErrorAggregator } from '../error-aggregator-
|
|
5
|
+
export { E as ErrorAggregator } from '../error-aggregator-D0Uu5r38.js';
|
|
6
6
|
import { Server } from 'node:http';
|
|
7
7
|
import '@opentelemetry/sdk-trace-base';
|
|
8
8
|
import '@opentelemetry/core';
|
|
@@ -12,9 +12,34 @@ interface HttpServerOptions {
|
|
|
12
12
|
port?: number;
|
|
13
13
|
host?: string;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
interface DevtoolsRoutesOptions {
|
|
16
|
+
/** Bound to a loopback host (the default). Enables the DNS-rebinding `Host`
|
|
17
|
+
* check on read endpoints; an explicit non-loopback bind opts out. */
|
|
18
|
+
loopbackOnly?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare function attachDevtoolsRoutes(httpServer: Server, devtools: DevtoolsServer, options?: DevtoolsRoutesOptions): void;
|
|
16
21
|
declare function createDevtoolsHttpServer(devtools: DevtoolsServer, _options?: HttpServerOptions): Server;
|
|
17
22
|
|
|
23
|
+
/** True for `localhost`, any `127.x.x.x`, and IPv6 loopback. Case-insensitive. */
|
|
24
|
+
declare function isLoopbackHostname(hostname: string): boolean;
|
|
25
|
+
/** True when the `Host` header names a loopback host. */
|
|
26
|
+
declare function hostHeaderIsLoopback(host: string): boolean;
|
|
27
|
+
/** True when an `Origin` header names a loopback origin. A malformed or opaque
|
|
28
|
+
* origin (e.g. the literal `null` from a sandboxed iframe) is treated as
|
|
29
|
+
* non-loopback. */
|
|
30
|
+
declare function originIsLoopback(origin: string): boolean;
|
|
31
|
+
interface GuardHeaders {
|
|
32
|
+
origin?: string;
|
|
33
|
+
host?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Decide whether a request to a sensitive (read/mutate) endpoint is allowed.
|
|
37
|
+
* - A present, non-loopback `Origin` is always rejected (cross-origin read).
|
|
38
|
+
* - When `loopbackOnly`, a present, non-loopback `Host` is rejected (DNS
|
|
39
|
+
* rebinding). Skipped when the receiver is bound to a non-loopback host.
|
|
40
|
+
*/
|
|
41
|
+
declare function allowSensitiveRequest(headers: GuardHeaders, loopbackOnly: boolean): boolean;
|
|
42
|
+
|
|
18
43
|
declare function parseOtlpTraces(payload: unknown): TraceData[];
|
|
19
44
|
declare function parseOtlpLogs(payload: unknown): LogData[];
|
|
20
45
|
/**
|
|
@@ -64,4 +89,4 @@ declare function appendWithLimit<T>(items: T[], item: T, limit: number): T[];
|
|
|
64
89
|
declare function appendManyWithLimit<T>(items: T[], incoming: T[], limit: number): T[];
|
|
65
90
|
declare function applyTelemetryLimits(data: DevtoolsData, limits: TelemetryLimits): DevtoolsData;
|
|
66
91
|
|
|
67
|
-
export { DEVTOOLS_IDENTITY, DevtoolsData, DevtoolsServer, type HttpServerOptions, LogData, type PortHolder, type TelemetryLimits, TraceData, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, isProtobufContentType, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
|
92
|
+
export { DEVTOOLS_IDENTITY, DevtoolsData, type DevtoolsRoutesOptions, DevtoolsServer, type HttpServerOptions, LogData, type PortHolder, type TelemetryLimits, TraceData, allowSensitiveRequest, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, hostHeaderIsLoopback, isLoopbackHostname, isProtobufContentType, originIsLoopback, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
package/dist/server/index.js
CHANGED
|
@@ -356,6 +356,40 @@ function applyTelemetryLimits(data, limits) {
|
|
|
356
356
|
};
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
// src/server/origin-guard.ts
|
|
360
|
+
var LOOPBACK_IPV6 = /* @__PURE__ */ new Set(["::1", "0:0:0:0:0:0:0:1"]);
|
|
361
|
+
function isLoopbackHostname(hostname) {
|
|
362
|
+
const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
363
|
+
return h === "localhost" || /^127\./.test(h) || LOOPBACK_IPV6.has(h);
|
|
364
|
+
}
|
|
365
|
+
function hostnameFromHostHeader(host) {
|
|
366
|
+
const h = host.trim();
|
|
367
|
+
if (h.startsWith("[")) {
|
|
368
|
+
const end = h.indexOf("]");
|
|
369
|
+
return end > 0 ? h.slice(1, end) : h;
|
|
370
|
+
}
|
|
371
|
+
const colon = h.indexOf(":");
|
|
372
|
+
return colon === -1 ? h : h.slice(0, colon);
|
|
373
|
+
}
|
|
374
|
+
function hostHeaderIsLoopback(host) {
|
|
375
|
+
return isLoopbackHostname(hostnameFromHostHeader(host));
|
|
376
|
+
}
|
|
377
|
+
function originIsLoopback(origin) {
|
|
378
|
+
try {
|
|
379
|
+
return isLoopbackHostname(new URL(origin).hostname);
|
|
380
|
+
} catch {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function allowSensitiveRequest(headers, loopbackOnly) {
|
|
385
|
+
const { origin, host } = headers;
|
|
386
|
+
if (origin && origin.length > 0 && !originIsLoopback(origin)) return false;
|
|
387
|
+
if (loopbackOnly && host && host.length > 0 && !hostHeaderIsLoopback(host)) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
359
393
|
// src/server/server.ts
|
|
360
394
|
var DevtoolsServer = class {
|
|
361
395
|
wss;
|
|
@@ -375,7 +409,12 @@ var DevtoolsServer = class {
|
|
|
375
409
|
this._port = options.port ?? 4318;
|
|
376
410
|
this.onData = options.onData;
|
|
377
411
|
this.httpServer = options.server ?? createServer();
|
|
378
|
-
|
|
412
|
+
const loopbackOnly = options.host == null || hostHeaderIsLoopback(options.host);
|
|
413
|
+
this.wss = new WebSocketServer({
|
|
414
|
+
server: this.httpServer,
|
|
415
|
+
path: options.path ?? "/ws",
|
|
416
|
+
verifyClient: ({ origin, req }) => allowSensitiveRequest({ origin, host: req.headers.host }, loopbackOnly)
|
|
417
|
+
});
|
|
379
418
|
this.wss.on("error", (err) => {
|
|
380
419
|
if (this.httpServer.listening) throw err;
|
|
381
420
|
});
|
|
@@ -409,6 +448,7 @@ var DevtoolsServer = class {
|
|
|
409
448
|
}
|
|
410
449
|
addTrace(trace) {
|
|
411
450
|
const existing = this.traces.find((t) => t.traceId === trace.traceId);
|
|
451
|
+
const merged = existing ?? trace;
|
|
412
452
|
if (existing) {
|
|
413
453
|
const existingSpanIds = new Set(existing.spans.map((s) => s.spanId));
|
|
414
454
|
for (const span of trace.spans) {
|
|
@@ -420,6 +460,14 @@ var DevtoolsServer = class {
|
|
|
420
460
|
existing.endTime = Math.max(existing.endTime, trace.endTime);
|
|
421
461
|
existing.duration = existing.endTime - existing.startTime;
|
|
422
462
|
if (trace.status === "ERROR") existing.status = "ERROR";
|
|
463
|
+
const root = existing.spans.find((s) => !s.parentSpanId);
|
|
464
|
+
if (root) {
|
|
465
|
+
existing.rootSpan = root;
|
|
466
|
+
const rootService = root.attributes?.["service.name"];
|
|
467
|
+
if (typeof rootService === "string" && rootService.length > 0) {
|
|
468
|
+
existing.service = rootService;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
423
471
|
} else {
|
|
424
472
|
this.traces = appendWithLimit(
|
|
425
473
|
this.traces,
|
|
@@ -428,7 +476,7 @@ var DevtoolsServer = class {
|
|
|
428
476
|
);
|
|
429
477
|
}
|
|
430
478
|
this.errorAggregator.addErrorsFromTrace(trace);
|
|
431
|
-
this.broadcast({ traces: [
|
|
479
|
+
this.broadcast({ traces: [merged], metrics: [], logs: [], errors: this.errorAggregator.getErrorGroups() });
|
|
432
480
|
}
|
|
433
481
|
addTraces(traces) {
|
|
434
482
|
for (const trace of traces) this.addTrace(trace);
|
|
@@ -1440,7 +1488,8 @@ function findPackageRoot() {
|
|
|
1440
1488
|
}
|
|
1441
1489
|
return dir;
|
|
1442
1490
|
}
|
|
1443
|
-
var
|
|
1491
|
+
var DEVTOOLS_FAVICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#0f172a"/><text x="32" y="41" text-anchor="middle" font-size="32">\u{1F6F0}\uFE0F</text></svg>';
|
|
1492
|
+
var FULLPAGE_HTML = `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>autotel-devtools</title><link rel="icon" href="/favicon.svg" type="image/svg+xml"><style>*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;width:100%;overflow:hidden}</style></head><body><script src="/widget.js?mode=fullpage"></script></body></html>`;
|
|
1444
1493
|
var cachedVersion = null;
|
|
1445
1494
|
function getVersion() {
|
|
1446
1495
|
if (cachedVersion !== null) return cachedVersion;
|
|
@@ -1474,8 +1523,10 @@ function getWidgetJs() {
|
|
|
1474
1523
|
}
|
|
1475
1524
|
return cachedWidgetJs;
|
|
1476
1525
|
}
|
|
1477
|
-
function attachDevtoolsRoutes(httpServer, devtools) {
|
|
1526
|
+
function attachDevtoolsRoutes(httpServer, devtools, options = {}) {
|
|
1527
|
+
const loopbackOnly = options.loopbackOnly ?? true;
|
|
1478
1528
|
httpServer.on("request", async (req, res) => {
|
|
1529
|
+
if (req.headers.upgrade?.toLowerCase() === "websocket") return;
|
|
1479
1530
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1480
1531
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
1481
1532
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
@@ -1498,6 +1549,15 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1498
1549
|
res.end(js);
|
|
1499
1550
|
return;
|
|
1500
1551
|
}
|
|
1552
|
+
if (req.method === "GET" && (url === "/favicon.svg" || url === "/favicon.ico")) {
|
|
1553
|
+
res.writeHead(200, {
|
|
1554
|
+
"Content-Type": "image/svg+xml; charset=utf-8",
|
|
1555
|
+
"Cache-Control": "public, max-age=86400",
|
|
1556
|
+
"Content-Length": Buffer.byteLength(DEVTOOLS_FAVICON_SVG)
|
|
1557
|
+
});
|
|
1558
|
+
res.end(DEVTOOLS_FAVICON_SVG);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1501
1561
|
if (req.method === "GET" && url === "/healthz") {
|
|
1502
1562
|
sendJson(res, 200, {
|
|
1503
1563
|
ok: true,
|
|
@@ -1508,11 +1568,19 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1508
1568
|
return;
|
|
1509
1569
|
}
|
|
1510
1570
|
if (req.method === "GET" && url === "/v1/traces") {
|
|
1571
|
+
if (!allowSensitiveRequest(req.headers, loopbackOnly)) {
|
|
1572
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1511
1575
|
const data = devtools.getCurrentData();
|
|
1512
1576
|
sendJson(res, 200, { traces: data.traces, count: data.traces.length });
|
|
1513
1577
|
return;
|
|
1514
1578
|
}
|
|
1515
1579
|
if (req.method === "DELETE" && url === "/v1/traces") {
|
|
1580
|
+
if (!allowSensitiveRequest(req.headers, loopbackOnly)) {
|
|
1581
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1516
1584
|
devtools.clearData();
|
|
1517
1585
|
sendJson(res, 200, { cleared: true });
|
|
1518
1586
|
return;
|
|
@@ -1558,6 +1626,6 @@ function createDevtoolsHttpServer(devtools, _options = {}) {
|
|
|
1558
1626
|
return server;
|
|
1559
1627
|
}
|
|
1560
1628
|
|
|
1561
|
-
export { DEVTOOLS_IDENTITY, DevtoolsLogExporter, DevtoolsRemoteExporter, DevtoolsServer, DevtoolsSpanExporter, ErrorAggregator, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, isProtobufContentType, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
|
1629
|
+
export { DEVTOOLS_IDENTITY, DevtoolsLogExporter, DevtoolsRemoteExporter, DevtoolsServer, DevtoolsSpanExporter, ErrorAggregator, allowSensitiveRequest, appendManyWithLimit, appendWithLimit, applyTelemetryLimits, attachDevtoolsRoutes, createDevtoolsHttpServer, decodeOtlpLogsRequest, decodeOtlpMetricsRequest, decodeOtlpTraceRequest, hostHeaderIsLoopback, isLoopbackHostname, isProtobufContentType, originIsLoopback, parseOtlpLogs, parseOtlpTraces, probePortHolder, resolveTelemetryLimits };
|
|
1562
1630
|
//# sourceMappingURL=index.js.map
|
|
1563
1631
|
//# sourceMappingURL=index.js.map
|