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