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.cjs
CHANGED
|
@@ -363,6 +363,40 @@ function applyTelemetryLimits(data, limits) {
|
|
|
363
363
|
};
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// src/server/origin-guard.ts
|
|
367
|
+
var LOOPBACK_IPV6 = /* @__PURE__ */ new Set(["::1", "0:0:0:0:0:0:0:1"]);
|
|
368
|
+
function isLoopbackHostname(hostname) {
|
|
369
|
+
const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
370
|
+
return h === "localhost" || /^127\./.test(h) || LOOPBACK_IPV6.has(h);
|
|
371
|
+
}
|
|
372
|
+
function hostnameFromHostHeader(host) {
|
|
373
|
+
const h = host.trim();
|
|
374
|
+
if (h.startsWith("[")) {
|
|
375
|
+
const end = h.indexOf("]");
|
|
376
|
+
return end > 0 ? h.slice(1, end) : h;
|
|
377
|
+
}
|
|
378
|
+
const colon = h.indexOf(":");
|
|
379
|
+
return colon === -1 ? h : h.slice(0, colon);
|
|
380
|
+
}
|
|
381
|
+
function hostHeaderIsLoopback(host) {
|
|
382
|
+
return isLoopbackHostname(hostnameFromHostHeader(host));
|
|
383
|
+
}
|
|
384
|
+
function originIsLoopback(origin) {
|
|
385
|
+
try {
|
|
386
|
+
return isLoopbackHostname(new URL(origin).hostname);
|
|
387
|
+
} catch {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function allowSensitiveRequest(headers, loopbackOnly) {
|
|
392
|
+
const { origin, host } = headers;
|
|
393
|
+
if (origin && origin.length > 0 && !originIsLoopback(origin)) return false;
|
|
394
|
+
if (loopbackOnly && host && host.length > 0 && !hostHeaderIsLoopback(host)) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
|
|
366
400
|
// src/server/server.ts
|
|
367
401
|
var DevtoolsServer = class {
|
|
368
402
|
wss;
|
|
@@ -382,7 +416,12 @@ var DevtoolsServer = class {
|
|
|
382
416
|
this._port = options.port ?? 4318;
|
|
383
417
|
this.onData = options.onData;
|
|
384
418
|
this.httpServer = options.server ?? http.createServer();
|
|
385
|
-
|
|
419
|
+
const loopbackOnly = options.host == null || hostHeaderIsLoopback(options.host);
|
|
420
|
+
this.wss = new ws.WebSocketServer({
|
|
421
|
+
server: this.httpServer,
|
|
422
|
+
path: options.path ?? "/ws",
|
|
423
|
+
verifyClient: ({ origin, req }) => allowSensitiveRequest({ origin, host: req.headers.host }, loopbackOnly)
|
|
424
|
+
});
|
|
386
425
|
this.wss.on("error", (err) => {
|
|
387
426
|
if (this.httpServer.listening) throw err;
|
|
388
427
|
});
|
|
@@ -416,6 +455,7 @@ var DevtoolsServer = class {
|
|
|
416
455
|
}
|
|
417
456
|
addTrace(trace) {
|
|
418
457
|
const existing = this.traces.find((t) => t.traceId === trace.traceId);
|
|
458
|
+
const merged = existing ?? trace;
|
|
419
459
|
if (existing) {
|
|
420
460
|
const existingSpanIds = new Set(existing.spans.map((s) => s.spanId));
|
|
421
461
|
for (const span of trace.spans) {
|
|
@@ -427,6 +467,14 @@ var DevtoolsServer = class {
|
|
|
427
467
|
existing.endTime = Math.max(existing.endTime, trace.endTime);
|
|
428
468
|
existing.duration = existing.endTime - existing.startTime;
|
|
429
469
|
if (trace.status === "ERROR") existing.status = "ERROR";
|
|
470
|
+
const root = existing.spans.find((s) => !s.parentSpanId);
|
|
471
|
+
if (root) {
|
|
472
|
+
existing.rootSpan = root;
|
|
473
|
+
const rootService = root.attributes?.["service.name"];
|
|
474
|
+
if (typeof rootService === "string" && rootService.length > 0) {
|
|
475
|
+
existing.service = rootService;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
430
478
|
} else {
|
|
431
479
|
this.traces = appendWithLimit(
|
|
432
480
|
this.traces,
|
|
@@ -435,7 +483,7 @@ var DevtoolsServer = class {
|
|
|
435
483
|
);
|
|
436
484
|
}
|
|
437
485
|
this.errorAggregator.addErrorsFromTrace(trace);
|
|
438
|
-
this.broadcast({ traces: [
|
|
486
|
+
this.broadcast({ traces: [merged], metrics: [], logs: [], errors: this.errorAggregator.getErrorGroups() });
|
|
439
487
|
}
|
|
440
488
|
addTraces(traces) {
|
|
441
489
|
for (const trace of traces) this.addTrace(trace);
|
|
@@ -1447,7 +1495,8 @@ function findPackageRoot() {
|
|
|
1447
1495
|
}
|
|
1448
1496
|
return dir;
|
|
1449
1497
|
}
|
|
1450
|
-
var
|
|
1498
|
+
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>';
|
|
1499
|
+
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>`;
|
|
1451
1500
|
var cachedVersion = null;
|
|
1452
1501
|
function getVersion() {
|
|
1453
1502
|
if (cachedVersion !== null) return cachedVersion;
|
|
@@ -1481,8 +1530,10 @@ function getWidgetJs() {
|
|
|
1481
1530
|
}
|
|
1482
1531
|
return cachedWidgetJs;
|
|
1483
1532
|
}
|
|
1484
|
-
function attachDevtoolsRoutes(httpServer, devtools) {
|
|
1533
|
+
function attachDevtoolsRoutes(httpServer, devtools, options = {}) {
|
|
1534
|
+
const loopbackOnly = options.loopbackOnly ?? true;
|
|
1485
1535
|
httpServer.on("request", async (req, res) => {
|
|
1536
|
+
if (req.headers.upgrade?.toLowerCase() === "websocket") return;
|
|
1486
1537
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1487
1538
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
1488
1539
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
@@ -1505,6 +1556,15 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1505
1556
|
res.end(js);
|
|
1506
1557
|
return;
|
|
1507
1558
|
}
|
|
1559
|
+
if (req.method === "GET" && (url === "/favicon.svg" || url === "/favicon.ico")) {
|
|
1560
|
+
res.writeHead(200, {
|
|
1561
|
+
"Content-Type": "image/svg+xml; charset=utf-8",
|
|
1562
|
+
"Cache-Control": "public, max-age=86400",
|
|
1563
|
+
"Content-Length": Buffer.byteLength(DEVTOOLS_FAVICON_SVG)
|
|
1564
|
+
});
|
|
1565
|
+
res.end(DEVTOOLS_FAVICON_SVG);
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1508
1568
|
if (req.method === "GET" && url === "/healthz") {
|
|
1509
1569
|
sendJson(res, 200, {
|
|
1510
1570
|
ok: true,
|
|
@@ -1515,11 +1575,19 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1515
1575
|
return;
|
|
1516
1576
|
}
|
|
1517
1577
|
if (req.method === "GET" && url === "/v1/traces") {
|
|
1578
|
+
if (!allowSensitiveRequest(req.headers, loopbackOnly)) {
|
|
1579
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1518
1582
|
const data = devtools.getCurrentData();
|
|
1519
1583
|
sendJson(res, 200, { traces: data.traces, count: data.traces.length });
|
|
1520
1584
|
return;
|
|
1521
1585
|
}
|
|
1522
1586
|
if (req.method === "DELETE" && url === "/v1/traces") {
|
|
1587
|
+
if (!allowSensitiveRequest(req.headers, loopbackOnly)) {
|
|
1588
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1523
1591
|
devtools.clearData();
|
|
1524
1592
|
sendJson(res, 200, { cleared: true });
|
|
1525
1593
|
return;
|
|
@@ -1571,6 +1639,7 @@ exports.DevtoolsRemoteExporter = DevtoolsRemoteExporter;
|
|
|
1571
1639
|
exports.DevtoolsServer = DevtoolsServer;
|
|
1572
1640
|
exports.DevtoolsSpanExporter = DevtoolsSpanExporter;
|
|
1573
1641
|
exports.ErrorAggregator = ErrorAggregator;
|
|
1642
|
+
exports.allowSensitiveRequest = allowSensitiveRequest;
|
|
1574
1643
|
exports.appendManyWithLimit = appendManyWithLimit;
|
|
1575
1644
|
exports.appendWithLimit = appendWithLimit;
|
|
1576
1645
|
exports.applyTelemetryLimits = applyTelemetryLimits;
|
|
@@ -1579,7 +1648,10 @@ exports.createDevtoolsHttpServer = createDevtoolsHttpServer;
|
|
|
1579
1648
|
exports.decodeOtlpLogsRequest = decodeOtlpLogsRequest;
|
|
1580
1649
|
exports.decodeOtlpMetricsRequest = decodeOtlpMetricsRequest;
|
|
1581
1650
|
exports.decodeOtlpTraceRequest = decodeOtlpTraceRequest;
|
|
1651
|
+
exports.hostHeaderIsLoopback = hostHeaderIsLoopback;
|
|
1652
|
+
exports.isLoopbackHostname = isLoopbackHostname;
|
|
1582
1653
|
exports.isProtobufContentType = isProtobufContentType;
|
|
1654
|
+
exports.originIsLoopback = originIsLoopback;
|
|
1583
1655
|
exports.parseOtlpLogs = parseOtlpLogs;
|
|
1584
1656
|
exports.parseOtlpTraces = parseOtlpTraces;
|
|
1585
1657
|
exports.probePortHolder = probePortHolder;
|