autotel-devtools 6.0.0 → 6.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/README.md +25 -1
- package/dist/cli.cjs +146 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +146 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +84 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +84 -16
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +59 -4
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +17 -1
- package/dist/server/index.d.ts +17 -1
- package/dist/server/index.js +58 -5
- package/dist/server/index.js.map +1 -1
- package/dist/widget.global.js +1 -1
- package/package.json +2 -2
- package/skills/autotel-devtools/SKILL.md +4 -2
package/dist/index.cjs
CHANGED
|
@@ -375,6 +375,9 @@ var DevtoolsServer = class {
|
|
|
375
375
|
this.onData = options.onData;
|
|
376
376
|
this.httpServer = options.server ?? http.createServer();
|
|
377
377
|
this.wss = new ws.WebSocketServer({ server: this.httpServer, path: options.path ?? "/ws" });
|
|
378
|
+
this.wss.on("error", (err) => {
|
|
379
|
+
if (this.httpServer.listening) throw err;
|
|
380
|
+
});
|
|
378
381
|
this.wss.on("connection", (ws) => {
|
|
379
382
|
this.clients.add(ws);
|
|
380
383
|
this.log(`Client connected (${this.clients.size} total)`);
|
|
@@ -922,7 +925,17 @@ function decodeOtlpMetricsRequest(body) {
|
|
|
922
925
|
return decodeRequest("opentelemetry.proto.metrics.v1.ExportMetricsServiceRequest", body);
|
|
923
926
|
}
|
|
924
927
|
|
|
928
|
+
// src/server/identity.ts
|
|
929
|
+
var DEVTOOLS_IDENTITY = "autotel-devtools";
|
|
930
|
+
|
|
925
931
|
// src/server/http.ts
|
|
932
|
+
function sendOtlpError(res, req, e) {
|
|
933
|
+
sendJson(res, 400, {
|
|
934
|
+
error: "Invalid OTLP payload",
|
|
935
|
+
message: e instanceof Error ? e.message : String(e),
|
|
936
|
+
contentType: req.headers["content-type"] ?? null
|
|
937
|
+
});
|
|
938
|
+
}
|
|
926
939
|
var PROTOBUF_DECODERS = {
|
|
927
940
|
traces: decodeOtlpTraceRequest,
|
|
928
941
|
logs: decodeOtlpLogsRequest,
|
|
@@ -943,6 +956,18 @@ function findPackageRoot() {
|
|
|
943
956
|
return dir;
|
|
944
957
|
}
|
|
945
958
|
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><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>`;
|
|
959
|
+
var cachedVersion = null;
|
|
960
|
+
function getVersion() {
|
|
961
|
+
if (cachedVersion !== null) return cachedVersion;
|
|
962
|
+
let version = "unknown";
|
|
963
|
+
try {
|
|
964
|
+
const pkg = JSON.parse(fs.readFileSync(path.resolve(findPackageRoot(), "package.json"), "utf8"));
|
|
965
|
+
if (typeof pkg.version === "string") version = pkg.version;
|
|
966
|
+
} catch {
|
|
967
|
+
}
|
|
968
|
+
cachedVersion = version;
|
|
969
|
+
return version;
|
|
970
|
+
}
|
|
946
971
|
var cachedWidgetJs = null;
|
|
947
972
|
function getWidgetJs() {
|
|
948
973
|
if (!cachedWidgetJs) {
|
|
@@ -969,6 +994,8 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
969
994
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
970
995
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
971
996
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
997
|
+
res.setHeader("x-autotel-devtools", getVersion());
|
|
998
|
+
res.setHeader("Access-Control-Expose-Headers", "x-autotel-devtools");
|
|
972
999
|
if (req.method === "OPTIONS") {
|
|
973
1000
|
res.writeHead(204);
|
|
974
1001
|
res.end();
|
|
@@ -987,7 +1014,12 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
987
1014
|
return;
|
|
988
1015
|
}
|
|
989
1016
|
if (req.method === "GET" && url === "/healthz") {
|
|
990
|
-
sendJson(res, 200, {
|
|
1017
|
+
sendJson(res, 200, {
|
|
1018
|
+
ok: true,
|
|
1019
|
+
service: DEVTOOLS_IDENTITY,
|
|
1020
|
+
version: getVersion(),
|
|
1021
|
+
clients: devtools.clientCount
|
|
1022
|
+
});
|
|
991
1023
|
return;
|
|
992
1024
|
}
|
|
993
1025
|
if (req.method === "GET" && url === "/v1/traces") {
|
|
@@ -1007,7 +1039,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1007
1039
|
devtools.addTraces(traces);
|
|
1008
1040
|
sendJson(res, 200, { acceptedTraces: traces.length });
|
|
1009
1041
|
} catch (e) {
|
|
1010
|
-
|
|
1042
|
+
sendOtlpError(res, req, e);
|
|
1011
1043
|
}
|
|
1012
1044
|
return;
|
|
1013
1045
|
}
|
|
@@ -1018,7 +1050,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1018
1050
|
devtools.addLogs(logs);
|
|
1019
1051
|
sendJson(res, 200, { acceptedLogs: logs.length });
|
|
1020
1052
|
} catch (e) {
|
|
1021
|
-
|
|
1053
|
+
sendOtlpError(res, req, e);
|
|
1022
1054
|
}
|
|
1023
1055
|
return;
|
|
1024
1056
|
}
|
|
@@ -1028,7 +1060,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1028
1060
|
const count = countOtlpMetrics(payload);
|
|
1029
1061
|
sendJson(res, 200, { acceptedMetrics: count });
|
|
1030
1062
|
} catch (e) {
|
|
1031
|
-
|
|
1063
|
+
sendOtlpError(res, req, e);
|
|
1032
1064
|
}
|
|
1033
1065
|
return;
|
|
1034
1066
|
}
|
|
@@ -1036,43 +1068,79 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1036
1068
|
});
|
|
1037
1069
|
}
|
|
1038
1070
|
var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
1071
|
+
var DEFAULT_MAX_PORT_TRIES = 20;
|
|
1039
1072
|
function formatAddress(host, port) {
|
|
1040
1073
|
return host.includes(":") ? `[${host}]:${port}` : `${host}:${port}`;
|
|
1041
1074
|
}
|
|
1042
1075
|
function listenLoopbackDualStack(args) {
|
|
1043
|
-
const { primary, port, host, attachSecondary } = args;
|
|
1076
|
+
const { primary, port, host, attachSecondary, maxTries } = args;
|
|
1077
|
+
const maxAttempts = Math.max(1, maxTries ?? DEFAULT_MAX_PORT_TRIES);
|
|
1044
1078
|
let sibling;
|
|
1045
1079
|
const ready = new Promise(
|
|
1046
|
-
(resolve2) => {
|
|
1080
|
+
(resolve2, reject) => {
|
|
1047
1081
|
const addresses = [];
|
|
1048
1082
|
const warnings = [];
|
|
1049
1083
|
const primaryHost = host === "localhost" ? "127.0.0.1" : host;
|
|
1050
|
-
|
|
1084
|
+
let candidate = port;
|
|
1085
|
+
let attempt = 0;
|
|
1086
|
+
const bindFailed = (atPort, msg) => reject(
|
|
1087
|
+
new Error(`could not bind ${formatAddress(primaryHost, atPort)}: ${msg}`)
|
|
1088
|
+
);
|
|
1089
|
+
const onError = (e) => {
|
|
1090
|
+
if (e.code !== "EADDRINUSE") return bindFailed(candidate, e.message);
|
|
1091
|
+
if (++attempt >= maxAttempts) {
|
|
1092
|
+
reject(
|
|
1093
|
+
new Error(
|
|
1094
|
+
`could not bind ${formatAddress(primaryHost, port)}: ${maxAttempts} consecutive ports in use`
|
|
1095
|
+
)
|
|
1096
|
+
);
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
candidate++;
|
|
1100
|
+
listen();
|
|
1101
|
+
};
|
|
1102
|
+
const onListening = () => {
|
|
1103
|
+
primary.removeListener("error", onError);
|
|
1104
|
+
if (candidate !== port) {
|
|
1105
|
+
warnings.push(`port ${port} was busy; using ${candidate} instead`);
|
|
1106
|
+
}
|
|
1051
1107
|
const addr = primary.address();
|
|
1052
|
-
const resolvedPort = addr && typeof addr === "object" ? addr.port :
|
|
1108
|
+
const resolvedPort = addr && typeof addr === "object" ? addr.port : candidate;
|
|
1053
1109
|
addresses.push(formatAddress(primaryHost, resolvedPort));
|
|
1054
1110
|
if (!LOOPBACK.has(host)) {
|
|
1055
|
-
resolve2({ addresses, warnings });
|
|
1111
|
+
resolve2({ addresses, port: resolvedPort, warnings });
|
|
1056
1112
|
return;
|
|
1057
1113
|
}
|
|
1058
1114
|
const siblingHost = primaryHost === "::1" ? "127.0.0.1" : "::1";
|
|
1059
1115
|
const s = http.createServer();
|
|
1060
1116
|
attachSecondary(s);
|
|
1061
|
-
const
|
|
1117
|
+
const onSiblingError = (se) => {
|
|
1062
1118
|
s.close();
|
|
1063
1119
|
warnings.push(
|
|
1064
|
-
`could not also bind ${formatAddress(siblingHost, resolvedPort)} (${
|
|
1120
|
+
`could not also bind ${formatAddress(siblingHost, resolvedPort)} (${se.message}); clients using the ${siblingHost === "::1" ? "IPv6" : "IPv4"} form of "localhost" may not connect.`
|
|
1065
1121
|
);
|
|
1066
|
-
resolve2({ addresses, warnings });
|
|
1122
|
+
resolve2({ addresses, port: resolvedPort, warnings });
|
|
1067
1123
|
};
|
|
1068
|
-
s.once("error",
|
|
1124
|
+
s.once("error", onSiblingError);
|
|
1069
1125
|
s.listen(resolvedPort, siblingHost, () => {
|
|
1070
|
-
s.off("error",
|
|
1126
|
+
s.off("error", onSiblingError);
|
|
1071
1127
|
sibling = s;
|
|
1072
1128
|
addresses.push(formatAddress(siblingHost, resolvedPort));
|
|
1073
|
-
resolve2({ addresses, warnings });
|
|
1129
|
+
resolve2({ addresses, port: resolvedPort, warnings });
|
|
1074
1130
|
});
|
|
1075
|
-
}
|
|
1131
|
+
};
|
|
1132
|
+
const listen = () => {
|
|
1133
|
+
try {
|
|
1134
|
+
primary.listen(candidate, primaryHost);
|
|
1135
|
+
} catch (e) {
|
|
1136
|
+
primary.removeListener("error", onError);
|
|
1137
|
+
primary.removeListener("listening", onListening);
|
|
1138
|
+
bindFailed(candidate, e.message);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
primary.on("error", onError);
|
|
1142
|
+
primary.once("listening", onListening);
|
|
1143
|
+
listen();
|
|
1076
1144
|
}
|
|
1077
1145
|
);
|
|
1078
1146
|
return {
|