autotel-devtools 6.0.0 → 6.0.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 +6 -1
- package/dist/cli.cjs +80 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +80 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +51 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +51 -12
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +3 -0
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +3 -0
- 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/README.md
CHANGED
|
@@ -152,12 +152,17 @@ AUTOTEL_DEVTOOLS_TITLE="My App" # Dashboard title (optional)
|
|
|
152
152
|
### CLI Options
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
|
+
npx autotel-devtools 4319 # port as a bare positional
|
|
155
156
|
npx autotel-devtools --port 4319 --host 0.0.0.0
|
|
156
157
|
```
|
|
157
158
|
|
|
159
|
+
Arguments:
|
|
160
|
+
|
|
161
|
+
- `[port]` - Port to listen on, shorthand for `--port` (an explicit `--port` always wins)
|
|
162
|
+
|
|
158
163
|
Options:
|
|
159
164
|
|
|
160
|
-
- `--port, -p` - Port to listen on (default: 4318)
|
|
165
|
+
- `--port, -p` - Port to listen on (default: 4318). If the port is taken, the receiver walks forward to the next free port and prints a warning.
|
|
161
166
|
- `--host, -H` - Host to bind to (default: 127.0.0.1)
|
|
162
167
|
- `--title, -t` - Dashboard title
|
|
163
168
|
|
package/dist/cli.cjs
CHANGED
|
@@ -373,6 +373,9 @@ var DevtoolsServer = class {
|
|
|
373
373
|
this.onData = options.onData;
|
|
374
374
|
this.httpServer = options.server ?? http.createServer();
|
|
375
375
|
this.wss = new ws.WebSocketServer({ server: this.httpServer, path: options.path ?? "/ws" });
|
|
376
|
+
this.wss.on("error", (err) => {
|
|
377
|
+
if (this.httpServer.listening) throw err;
|
|
378
|
+
});
|
|
376
379
|
this.wss.on("connection", (ws) => {
|
|
377
380
|
this.clients.add(ws);
|
|
378
381
|
this.log(`Client connected (${this.clients.size} total)`);
|
|
@@ -1034,43 +1037,79 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1034
1037
|
});
|
|
1035
1038
|
}
|
|
1036
1039
|
var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
1040
|
+
var DEFAULT_MAX_PORT_TRIES = 20;
|
|
1037
1041
|
function formatAddress(host, port) {
|
|
1038
1042
|
return host.includes(":") ? `[${host}]:${port}` : `${host}:${port}`;
|
|
1039
1043
|
}
|
|
1040
1044
|
function listenLoopbackDualStack(args) {
|
|
1041
|
-
const { primary, port, host, attachSecondary } = args;
|
|
1045
|
+
const { primary, port, host, attachSecondary, maxTries } = args;
|
|
1046
|
+
const maxAttempts = Math.max(1, maxTries ?? DEFAULT_MAX_PORT_TRIES);
|
|
1042
1047
|
let sibling;
|
|
1043
1048
|
const ready = new Promise(
|
|
1044
|
-
(resolve3) => {
|
|
1049
|
+
(resolve3, reject) => {
|
|
1045
1050
|
const addresses = [];
|
|
1046
1051
|
const warnings = [];
|
|
1047
1052
|
const primaryHost = host === "localhost" ? "127.0.0.1" : host;
|
|
1048
|
-
|
|
1053
|
+
let candidate = port;
|
|
1054
|
+
let attempt = 0;
|
|
1055
|
+
const bindFailed = (atPort, msg) => reject(
|
|
1056
|
+
new Error(`could not bind ${formatAddress(primaryHost, atPort)}: ${msg}`)
|
|
1057
|
+
);
|
|
1058
|
+
const onError = (e) => {
|
|
1059
|
+
if (e.code !== "EADDRINUSE") return bindFailed(candidate, e.message);
|
|
1060
|
+
if (++attempt >= maxAttempts) {
|
|
1061
|
+
reject(
|
|
1062
|
+
new Error(
|
|
1063
|
+
`could not bind ${formatAddress(primaryHost, port)}: ${maxAttempts} consecutive ports in use`
|
|
1064
|
+
)
|
|
1065
|
+
);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
candidate++;
|
|
1069
|
+
listen();
|
|
1070
|
+
};
|
|
1071
|
+
const onListening = () => {
|
|
1072
|
+
primary.removeListener("error", onError);
|
|
1073
|
+
if (candidate !== port) {
|
|
1074
|
+
warnings.push(`port ${port} was busy; using ${candidate} instead`);
|
|
1075
|
+
}
|
|
1049
1076
|
const addr = primary.address();
|
|
1050
|
-
const resolvedPort = addr && typeof addr === "object" ? addr.port :
|
|
1077
|
+
const resolvedPort = addr && typeof addr === "object" ? addr.port : candidate;
|
|
1051
1078
|
addresses.push(formatAddress(primaryHost, resolvedPort));
|
|
1052
1079
|
if (!LOOPBACK.has(host)) {
|
|
1053
|
-
resolve3({ addresses, warnings });
|
|
1080
|
+
resolve3({ addresses, port: resolvedPort, warnings });
|
|
1054
1081
|
return;
|
|
1055
1082
|
}
|
|
1056
1083
|
const siblingHost = primaryHost === "::1" ? "127.0.0.1" : "::1";
|
|
1057
1084
|
const s = http.createServer();
|
|
1058
1085
|
attachSecondary(s);
|
|
1059
|
-
const
|
|
1086
|
+
const onSiblingError = (se) => {
|
|
1060
1087
|
s.close();
|
|
1061
1088
|
warnings.push(
|
|
1062
|
-
`could not also bind ${formatAddress(siblingHost, resolvedPort)} (${
|
|
1089
|
+
`could not also bind ${formatAddress(siblingHost, resolvedPort)} (${se.message}); clients using the ${siblingHost === "::1" ? "IPv6" : "IPv4"} form of "localhost" may not connect.`
|
|
1063
1090
|
);
|
|
1064
|
-
resolve3({ addresses, warnings });
|
|
1091
|
+
resolve3({ addresses, port: resolvedPort, warnings });
|
|
1065
1092
|
};
|
|
1066
|
-
s.once("error",
|
|
1093
|
+
s.once("error", onSiblingError);
|
|
1067
1094
|
s.listen(resolvedPort, siblingHost, () => {
|
|
1068
|
-
s.off("error",
|
|
1095
|
+
s.off("error", onSiblingError);
|
|
1069
1096
|
sibling = s;
|
|
1070
1097
|
addresses.push(formatAddress(siblingHost, resolvedPort));
|
|
1071
|
-
resolve3({ addresses, warnings });
|
|
1098
|
+
resolve3({ addresses, port: resolvedPort, warnings });
|
|
1072
1099
|
});
|
|
1073
|
-
}
|
|
1100
|
+
};
|
|
1101
|
+
const listen = () => {
|
|
1102
|
+
try {
|
|
1103
|
+
primary.listen(candidate, primaryHost);
|
|
1104
|
+
} catch (e) {
|
|
1105
|
+
primary.removeListener("error", onError);
|
|
1106
|
+
primary.removeListener("listening", onListening);
|
|
1107
|
+
bindFailed(candidate, e.message);
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
primary.on("error", onError);
|
|
1111
|
+
primary.once("listening", onListening);
|
|
1112
|
+
listen();
|
|
1074
1113
|
}
|
|
1075
1114
|
);
|
|
1076
1115
|
return {
|
|
@@ -1087,10 +1126,14 @@ function printHelp() {
|
|
|
1087
1126
|
process.stdout.write(
|
|
1088
1127
|
`autotel-devtools - Standalone OTLP receiver with web devtools UI
|
|
1089
1128
|
|
|
1090
|
-
Usage: autotel-devtools [options]
|
|
1129
|
+
Usage: autotel-devtools [port] [options]
|
|
1130
|
+
|
|
1131
|
+
Arguments:
|
|
1132
|
+
port Port to listen on (shorthand for --port; must be a positive integer)
|
|
1091
1133
|
|
|
1092
1134
|
Options:
|
|
1093
|
-
-p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT)
|
|
1135
|
+
-p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).
|
|
1136
|
+
If the port is taken, the next free port is used and a warning is shown.
|
|
1094
1137
|
-H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)
|
|
1095
1138
|
-t, --title <title> Dashboard title (env: AUTOTEL_DEVTOOLS_TITLE)
|
|
1096
1139
|
Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT
|
|
@@ -1110,7 +1153,8 @@ Endpoints:
|
|
|
1110
1153
|
|
|
1111
1154
|
Examples:
|
|
1112
1155
|
npx autotel-devtools
|
|
1113
|
-
npx autotel-devtools
|
|
1156
|
+
npx autotel-devtools 4319
|
|
1157
|
+
npx autotel-devtools -p 4319 -H 0.0.0.0
|
|
1114
1158
|
|
|
1115
1159
|
Then point your app:
|
|
1116
1160
|
OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js
|
|
@@ -1137,10 +1181,12 @@ function printVersion() {
|
|
|
1137
1181
|
}
|
|
1138
1182
|
function parseArgs(argv) {
|
|
1139
1183
|
const options = {
|
|
1140
|
-
port:
|
|
1184
|
+
port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || "4318"),
|
|
1141
1185
|
host: process.env.AUTOTEL_DEVTOOLS_HOST || "127.0.0.1",
|
|
1142
1186
|
title: process.env.AUTOTEL_DEVTOOLS_TITLE
|
|
1143
1187
|
};
|
|
1188
|
+
let portWasExplicit = false;
|
|
1189
|
+
let positionalPortConsumed = false;
|
|
1144
1190
|
for (let i = 0; i < argv.length; i++) {
|
|
1145
1191
|
const arg = argv[i];
|
|
1146
1192
|
const next = argv[i + 1];
|
|
@@ -1153,7 +1199,8 @@ function parseArgs(argv) {
|
|
|
1153
1199
|
return null;
|
|
1154
1200
|
}
|
|
1155
1201
|
if ((arg === "--port" || arg === "-p") && next) {
|
|
1156
|
-
options.port =
|
|
1202
|
+
options.port = parsePort(next);
|
|
1203
|
+
portWasExplicit = true;
|
|
1157
1204
|
i++;
|
|
1158
1205
|
continue;
|
|
1159
1206
|
}
|
|
@@ -1167,9 +1214,23 @@ function parseArgs(argv) {
|
|
|
1167
1214
|
i++;
|
|
1168
1215
|
continue;
|
|
1169
1216
|
}
|
|
1217
|
+
if (/^\d+$/.test(arg) && !positionalPortConsumed) {
|
|
1218
|
+
if (!portWasExplicit) options.port = parsePort(arg);
|
|
1219
|
+
positionalPortConsumed = true;
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1170
1222
|
}
|
|
1171
1223
|
return options;
|
|
1172
1224
|
}
|
|
1225
|
+
function parsePort(value) {
|
|
1226
|
+
const n = Number(value);
|
|
1227
|
+
if (!Number.isInteger(n) || n < 0 || n > 65535) {
|
|
1228
|
+
process.stderr.write(`[autotel-devtools] invalid port: ${value}
|
|
1229
|
+
`);
|
|
1230
|
+
process.exit(2);
|
|
1231
|
+
}
|
|
1232
|
+
return n;
|
|
1233
|
+
}
|
|
1173
1234
|
async function main() {
|
|
1174
1235
|
const options = parseArgs(process.argv.slice(2));
|
|
1175
1236
|
if (!options) {
|
|
@@ -1184,8 +1245,8 @@ async function main() {
|
|
|
1184
1245
|
host: options.host,
|
|
1185
1246
|
attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer)
|
|
1186
1247
|
});
|
|
1187
|
-
const { addresses, warnings } = await listeners.ready;
|
|
1188
|
-
const uiBase = `http://${options.host === "localhost" ? "127.0.0.1" : options.host}:${
|
|
1248
|
+
const { addresses, warnings, port: boundPort } = await listeners.ready;
|
|
1249
|
+
const uiBase = `http://${options.host === "localhost" ? "127.0.0.1" : options.host}:${boundPort}`;
|
|
1189
1250
|
const title = options.title || "autotel-devtools";
|
|
1190
1251
|
process.stdout.write(`
|
|
1191
1252
|
${title}
|