autotel-devtools 13.1.1 → 14.0.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 +24 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/{error-aggregator-C8X-RQRi.d.ts → error-aggregator-BrgYNNoC.d.ts} +2 -3
- package/dist/{error-aggregator-7Q2H7htn.d.cts → error-aggregator-bZ-prq97.d.cts} +2 -3
- package/dist/{exporter-yqXiHw1Q.d.ts → exporter-BLqTs00O.d.ts} +1 -2
- package/dist/{exporter-kXTGDFoH.d.cts → exporter-BSIS3Qgy.d.cts} +1 -2
- package/dist/genai/index.cjs.map +1 -1
- package/dist/genai/index.d.cts +0 -1
- package/dist/genai/index.d.ts +0 -1
- package/dist/genai/index.js.map +1 -1
- package/dist/http-BUHnkhrC.cjs.map +1 -1
- package/dist/http-yIS667nX.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js.map +1 -1
- package/dist/listen-DGzVi7DE.cjs.map +1 -1
- package/dist/listen-NVMbBWHw.js.map +1 -1
- package/dist/resource-utils-B4UVvfnH.js.map +1 -1
- package/dist/resource-utils-DjHJB6uc.cjs.map +1 -1
- package/dist/server/exporter.cjs.map +1 -1
- package/dist/server/exporter.d.cts +1 -1
- package/dist/server/exporter.d.ts +1 -1
- package/dist/server/exporter.js.map +1 -1
- package/dist/server/index.d.cts +2 -3
- package/dist/server/index.d.ts +2 -3
- package/dist/server/log-exporter.cjs.map +1 -1
- package/dist/server/log-exporter.d.cts +0 -1
- package/dist/server/log-exporter.d.ts +0 -1
- package/dist/server/log-exporter.js.map +1 -1
- package/dist/server/remote-exporter.d.cts +0 -1
- package/dist/server/remote-exporter.d.ts +0 -1
- package/dist/widget.global.js +14 -13
- package/package.json +27 -27
- package/skills/autotel-devtools/SKILL.md +48 -46
package/README.md
CHANGED
|
@@ -43,8 +43,8 @@ node app.js
|
|
|
43
43
|
|
|
44
44
|
The endpoints accept **both OTLP/JSON and OTLP/protobuf** (`application/x-protobuf`),
|
|
45
45
|
selected automatically from the request `Content-Type`. That means SDKs that default
|
|
46
|
-
to protobuf over OTLP HTTP
|
|
47
|
-
|
|
46
|
+
to protobuf over OTLP HTTP, including the Python, Java, and Go OpenTelemetry SDKs.
|
|
47
|
+
Work without any extra configuration:
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# Python / Java / Go SDKs default to http/protobuf — just point them at the receiver
|
|
@@ -84,7 +84,7 @@ Or programmatically:
|
|
|
84
84
|
Use in Node.js with autotel:
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
|
-
import { init,
|
|
87
|
+
import { init, withTracing } from 'autotel';
|
|
88
88
|
import { createDevtools } from 'autotel-devtools';
|
|
89
89
|
|
|
90
90
|
const { server, httpServer, exporter, port, close } = createDevtools({
|
|
@@ -100,7 +100,7 @@ init({
|
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
// Your traced code
|
|
103
|
-
const myFunction =
|
|
103
|
+
const myFunction = withTracing({ name: 'example.run' })((ctx) => async () => {
|
|
104
104
|
// ... this span appears in devtools
|
|
105
105
|
});
|
|
106
106
|
```
|
|
@@ -117,19 +117,20 @@ const myFunction = trace((ctx) => async () => {
|
|
|
117
117
|
Every response carries an `x-autotel-devtools: <version>` header, and `GET /healthz`
|
|
118
118
|
returns `{ ok, service: "autotel-devtools", version, clients }`. Use either to confirm
|
|
119
119
|
you are talking to autotel-devtools rather than another OTLP collector that happens to
|
|
120
|
-
share the port
|
|
120
|
+
share the port. For example before pointing an exporter at `:4318`:
|
|
121
121
|
|
|
122
122
|
```ts
|
|
123
|
-
import { probePortHolder } from 'autotel-devtools/server'
|
|
123
|
+
import { probePortHolder } from 'autotel-devtools/server';
|
|
124
124
|
|
|
125
125
|
// 'autotel-devtools' | 'foreign' | 'none'
|
|
126
|
-
const holder = await probePortHolder('127.0.0.1', 4318)
|
|
126
|
+
const holder = await probePortHolder('127.0.0.1', 4318);
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
If you start the receiver and the requested port is held by a
|
|
129
|
+
If you start the receiver and the requested port is held by a _foreign_ process (some
|
|
130
130
|
IDEs run their own OTLP collector on `:4318`), the CLI falls forward to the next free
|
|
131
131
|
port and warns that exporters still aimed at the busy port are reaching that other
|
|
132
|
-
process
|
|
132
|
+
process. Point them at the bound port, or free the original.
|
|
133
|
+
|
|
133
134
|
- **Exporters** - OpenTelemetry span/log exporters
|
|
134
135
|
|
|
135
136
|
### Widget (Svelte 5)
|
|
@@ -150,7 +151,7 @@ process — point them at the bound port, or free the original.
|
|
|
150
151
|
- ✅ Service map visualization
|
|
151
152
|
- ✅ Resources view (derived from telemetry)
|
|
152
153
|
- ✅ GenAI run summaries + narrated walkthrough
|
|
153
|
-
- ✅ **Agents view
|
|
154
|
+
- ✅ **Agents view**: observe coding agents (Claude Code, opencode) from their OTel metrics + log events
|
|
154
155
|
- ✅ Search with debounce (300ms)
|
|
155
156
|
- ✅ Configurable telemetry limits (env vars)
|
|
156
157
|
- ✅ Widget position persistence (localStorage)
|
|
@@ -163,7 +164,7 @@ When your app emits OpenTelemetry GenAI spans (Vercel AI SDK, Pydantic AI, OpenA
|
|
|
163
164
|
Agents, Anthropic, Google GenAI, LangChain, …), the **GenAI** tab gives two extras
|
|
164
165
|
on top of the per-span detail:
|
|
165
166
|
|
|
166
|
-
- A **run summary strip** sits above the detail for any multi-span run
|
|
167
|
+
- A **run summary strip** sits above the detail for any multi-span run: total
|
|
167
168
|
cost (table-priced; a trailing `+` marks a lower bound when some calls are
|
|
168
169
|
unpriced), input→output tokens, reasoning tokens, model calls, tool
|
|
169
170
|
executions, sub-agents, duration and errors.
|
|
@@ -176,8 +177,8 @@ on top of the per-span detail:
|
|
|
176
177
|
### Agents: observe Claude Code (and other coding agents)
|
|
177
178
|
|
|
178
179
|
Coding agents like **Claude Code** emit OpenTelemetry **metrics and log events**
|
|
179
|
-
(no traces). The **Agents** tab reconstructs them into a session-centric view
|
|
180
|
-
|
|
180
|
+
(no traces). The **Agents** tab reconstructs them into a session-centric view.
|
|
181
|
+
Powered by the [`autotel-agents`](../autotel-agents) package, which also handles
|
|
181
182
|
opencode and is one adapter away from Codex.
|
|
182
183
|
|
|
183
184
|
One command starts the receiver _and_ launches Claude Code wired to it:
|
|
@@ -186,21 +187,21 @@ One command starts the receiver _and_ launches Claude Code wired to it:
|
|
|
186
187
|
npx autotel-devtools claude
|
|
187
188
|
```
|
|
188
189
|
|
|
189
|
-
This sets the telemetry env for a live local view
|
|
190
|
+
This sets the telemetry env for a live local view. OTLP **`http/protobuf`** to
|
|
190
191
|
this receiver (not the gRPC setup in most guides, which this receiver doesn't
|
|
191
192
|
speak), 1s export intervals, and `session.id` kept on metrics. Then open the UI
|
|
192
193
|
and switch to **Agents**.
|
|
193
194
|
|
|
194
|
-
- `--print-env
|
|
195
|
+
- `--print-env`: print the env block instead of launching (for managed-settings
|
|
195
196
|
/ MDM / VS Code), e.g. `npx autotel-devtools claude --print-env`.
|
|
196
|
-
- `--log-prompts
|
|
197
|
+
- `--log-prompts`: capture prompt _text_ (default is private: length only).
|
|
197
198
|
|
|
198
199
|
What you get per session: a **timeline** (prompts → tool calls → API requests →
|
|
199
200
|
decisions), a **rollup** (cost, tokens, requests, lines changed), and breakdowns
|
|
200
201
|
by **tool category**, **MCP server** (`mcp__server__tool`), **sub-agent** (`Task`)
|
|
201
|
-
and **skill** (`Skill`)
|
|
202
|
+
and **skill** (`Skill`). Plus an aggregate strip across all sessions. Cost uses
|
|
202
203
|
the agent's reported `cost_usd`, falling back to a token estimate (badged). MCP
|
|
203
|
-
protocol internals are out of scope here
|
|
204
|
+
protocol internals are out of scope here. That's `autotel-mcp-instrumentation`.
|
|
204
205
|
|
|
205
206
|
## Configuration
|
|
206
207
|
|
|
@@ -247,7 +248,7 @@ not a silent black hole.
|
|
|
247
248
|
## Behind a dev-server proxy
|
|
248
249
|
|
|
249
250
|
If your app's dev server proxies `/v1/traces` to the receiver, two classic
|
|
250
|
-
bugs make spans vanish with **no error
|
|
251
|
+
bugs make spans vanish with **no error**. Both worth knowing:
|
|
251
252
|
|
|
252
253
|
```ts
|
|
253
254
|
// Express / http-proxy-middleware
|
|
@@ -274,7 +275,7 @@ app.use(
|
|
|
274
275
|
## Verifying ingestion in tests
|
|
275
276
|
|
|
276
277
|
The receiver exposes an HTTP read-back so a test can assert the collector
|
|
277
|
-
**actually received** spans
|
|
278
|
+
**actually received** spans. Instead of asserting "the client tried to send",
|
|
278
279
|
which a browser-level route intercept can fake (it fulfils the request before
|
|
279
280
|
it reaches any server):
|
|
280
281
|
|
|
@@ -287,7 +288,7 @@ DELETE /v1/traces # clear captured telemetry (reset between tests)
|
|
|
287
288
|
// Playwright / integration test — bypass any page.route() intercept and ask
|
|
288
289
|
// the collector directly.
|
|
289
290
|
await fetch(`${RECEIVER}/v1/traces`, { method: 'DELETE' }); // reset
|
|
290
|
-
await runTheUserFlow();
|
|
291
|
+
await runTheUserFlow(); // app emits spans
|
|
291
292
|
await expect
|
|
292
293
|
.poll(async () => (await (await fetch(`${RECEIVER}/v1/traces`)).json()).count)
|
|
293
294
|
.toBeGreaterThan(0);
|
|
@@ -299,7 +300,7 @@ by the origin guard below.
|
|
|
299
300
|
## Read-surface origin guard
|
|
300
301
|
|
|
301
302
|
OTLP **ingestion** (`POST /v1/{traces,logs,metrics}`), `GET /widget.js` and
|
|
302
|
-
`GET /healthz` are open to any origin
|
|
303
|
+
`GET /healthz` are open to any origin. Browser apps on arbitrary dev origins
|
|
303
304
|
must be able to send telemetry and load the embeddable widget. The **read**
|
|
304
305
|
surface is not: `GET /v1/traces`, `DELETE /v1/traces` and the `/ws` WebSocket are
|
|
305
306
|
origin-checked so a web page you happen to be visiting can't `fetch()` or stream
|
|
@@ -310,7 +311,7 @@ your locally captured prompts, responses and tokens.
|
|
|
310
311
|
rebinding) is also rejected. `--host 0.0.0.0` opts into network exposure and
|
|
311
312
|
applies only the `Origin` check.
|
|
312
313
|
|
|
313
|
-
The embedded widget keeps working
|
|
314
|
+
The embedded widget keeps working. It connects from a loopback origin
|
|
314
315
|
(`http://localhost:<your-app-port>`). Server-side reads with no `Origin` pass.
|
|
315
316
|
|
|
316
317
|
## License
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","names":["hostHeaderIsLoopback","DevtoolsServer","listenLoopbackDualStack","attachDevtoolsRoutes","probePortHolder"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// src/cli.ts\nimport { createServer } from 'node:http'\nimport { spawn } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { DevtoolsServer } from './server/server'\nimport { attachDevtoolsRoutes } from './server/http'\nimport { hostHeaderIsLoopback } from './server/origin-guard'\nimport { listenLoopbackDualStack } from './server/listen'\nimport { probePortHolder } from './server/identity'\n\ninterface CliOptions {\n port: number\n host: string\n title?: string\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"></script>\n` + '\\n',\n )\n}\n\nfunction printVersion(): void {\n try {\n const dir = dirname(fileURLToPath(import.meta.url))\n const pkgPath = resolve(dir, '..', 'package.json')\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))\n process.stdout.write(`${pkg.version}\\n`)\n } catch {\n process.stdout.write('unknown\\n')\n }\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n const options: CliOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n title: process.env.AUTOTEL_DEVTOOLS_TITLE,\n }\n\n // An explicit `--port`/`-p` always wins, regardless of where it sits in\n // argv. A bare numeric positional is shorthand for `--port`, but only when\n // no explicit flag was given — and only the first positional counts, so a\n // stray \"4318\" can't override an earlier positional either.\n let portWasExplicit = false\n let positionalPortConsumed = false\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n if (arg === '--help' || arg === '-h') { printHelp(); return null }\n if (arg === '--version' || arg === '-v') { printVersion(); return null }\n if ((arg === '--port' || arg === '-p') && next) { options.port = parsePort(next); portWasExplicit = true; i++; continue }\n if ((arg === '--host' || arg === '-H') && next) { options.host = next; i++; continue }\n if ((arg === '--title' || arg === '-t') && next) { options.title = next; i++; continue }\n if (/^\\d+$/.test(arg) && !positionalPortConsumed) {\n if (!portWasExplicit) options.port = parsePort(arg)\n positionalPortConsumed = true\n continue\n }\n }\n\n return options\n}\n\nfunction parsePort(value: string): number {\n const n = Number(value)\n if (!Number.isInteger(n) || n < 0 || n > 65535) {\n process.stderr.write(`[autotel-devtools] invalid port: ${value}\\n`)\n process.exit(2)\n }\n return n\n}\n\ninterface RunningReceiver {\n wsServer: DevtoolsServer\n closeAll: () => Promise<void>\n addresses: string[]\n warnings: string[]\n boundPort: number\n uiBase: string\n}\n\nasync function startReceiver(options: CliOptions): Promise<RunningReceiver> {\n const httpServer = createServer()\n const loopbackOnly = hostHeaderIsLoopback(options.host)\n const wsServer = new DevtoolsServer({ server: httpServer, host: options.host, verbose: true })\n attachDevtoolsRoutes(httpServer, wsServer, { loopbackOnly })\n\n const listeners = listenLoopbackDualStack({\n primary: httpServer,\n port: options.port,\n host: options.host,\n attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer, { loopbackOnly }),\n })\n\n const { addresses, warnings, port: boundPort } = await listeners.ready\n\n // Falling forward to a different port means the one we wanted is held by\n // someone else. Classify them: another autotel-devtools (benign) versus a\n // foreign process. The foreign case is the silent footgun — apps keep\n // exporting OTLP to the requested port and reach that process, not us, so\n // this UI stays empty while they see export errors. Say so, with the fix.\n if (boundPort !== options.port) {\n const holder = await probePortHolder(options.host, options.port)\n if (holder === 'autotel-devtools') {\n warnings.push(\n `another autotel-devtools is already running on port ${options.port}; ` +\n `this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`,\n )\n } else {\n warnings.push(\n `port ${options.port} is held by another process that is NOT autotel-devtools. ` +\n `Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. ` +\n `Point your exporter at :${boundPort}, or free :${options.port} and restart.`,\n )\n }\n }\n\n const uiBase = `http://${options.host === 'localhost' ? '127.0.0.1' : options.host}:${boundPort}`\n return {\n wsServer,\n closeAll: () => Promise.all([wsServer.close(), listeners.closeSibling()]).then(() => undefined),\n addresses,\n warnings,\n boundPort,\n uiBase,\n }\n}\n\n// Telemetry env that wires Claude Code (and any OTel-via-env CLI) to this\n// receiver for a *live* local view: HTTP/protobuf (NOT gRPC — this receiver\n// speaks HTTP only), 1s export intervals so events show up promptly, and\n// session.id kept on metrics so metric-only signals join their session.\nfunction buildAgentEnv(uiBase: string, logPrompts: boolean): Record<string, string> {\n const env: Record<string, string> = {\n CLAUDE_CODE_ENABLE_TELEMETRY: '1',\n OTEL_METRICS_EXPORTER: 'otlp',\n OTEL_LOGS_EXPORTER: 'otlp',\n OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',\n OTEL_EXPORTER_OTLP_ENDPOINT: uiBase,\n OTEL_METRIC_EXPORT_INTERVAL: '1000',\n OTEL_LOGS_EXPORT_INTERVAL: '1000',\n OTEL_METRICS_INCLUDE_SESSION_ID: 'true',\n }\n // Private by default: prompt *text* only flows when explicitly opted in.\n if (logPrompts) env.OTEL_LOG_USER_PROMPTS = '1'\n return env\n}\n\nfunction printEnvBlock(env: Record<string, string>): void {\n for (const [key, value] of Object.entries(env)) {\n process.stdout.write(`export ${key}=${value}\\n`)\n }\n}\n\ninterface ClaudeOptions {\n port: number\n host: string\n printEnv: boolean\n logPrompts: boolean\n claudeArgs: string[]\n}\n\nfunction parseClaudeArgs(argv: string[]): ClaudeOptions {\n const opts: ClaudeOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n printEnv: false,\n logPrompts: false,\n claudeArgs: [],\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n if (arg === '--') { opts.claudeArgs.push(...argv.slice(i + 1)); break }\n if (arg === '--print-env') { opts.printEnv = true; continue }\n if (arg === '--log-prompts') { opts.logPrompts = true; continue }\n if ((arg === '--port' || arg === '-p') && next) { opts.port = parsePort(next); i++; continue }\n if ((arg === '--host' || arg === '-H') && next) { opts.host = next; i++; continue }\n // Anything else is passed straight through to `claude`.\n opts.claudeArgs.push(arg)\n }\n return opts\n}\n\nasync function runClaudeSubcommand(argv: string[]): Promise<void> {\n const opts = parseClaudeArgs(argv)\n\n // --print-env needs no running server: emit the block for the configured\n // port so users can paste it into a shell / managed-settings file.\n if (opts.printEnv) {\n const uiBase = `http://${opts.host === 'localhost' ? '127.0.0.1' : opts.host}:${opts.port}`\n printEnvBlock(buildAgentEnv(uiBase, opts.logPrompts))\n return\n }\n\n const receiver = await startReceiver({ port: opts.port, host: opts.host })\n const env = buildAgentEnv(receiver.uiBase, opts.logPrompts)\n\n process.stdout.write(`\\n autotel-devtools — Claude Code\\n\\n`)\n process.stdout.write(` Receiver: ${receiver.uiBase}\\n`)\n process.stdout.write(` UI: ${receiver.uiBase} → open the \"Agents\" tab\\n`)\n if (!opts.logPrompts) {\n process.stdout.write(` Prompts: private (length only) — add --log-prompts to capture text\\n`)\n }\n for (const w of receiver.warnings) process.stdout.write(` ⚠ ${w}\\n`)\n process.stdout.write(`\\n Launching claude…\\n\\n`)\n\n const child = spawn('claude', opts.claudeArgs, {\n stdio: 'inherit',\n env: { ...process.env, ...env },\n })\n\n const shutdown = () => { receiver.closeAll().then(() => process.exit(0)) }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n\n child.on('error', (err) => {\n const reason = (err as NodeJS.ErrnoException).code === 'ENOENT'\n ? `'claude' not found on PATH. Install Claude Code, or run the receiver alone with 'npx autotel-devtools' and point claude at ${receiver.uiBase} using --print-env.`\n : err.message\n process.stderr.write(`[autotel-devtools] ${reason}\\n`)\n receiver.closeAll().then(() => process.exit(1))\n })\n child.on('exit', (code) => {\n receiver.closeAll().then(() => process.exit(code ?? 0))\n })\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2)\n\n // Subcommand: `autotel-devtools claude [claude args] [--print-env] [--log-prompts]`\n if (argv[0] === 'claude') {\n await runClaudeSubcommand(argv.slice(1))\n return\n }\n\n const options = parseArgs(argv)\n if (!options) { process.exit(0) }\n\n const receiver = await startReceiver(options)\n const { addresses, warnings, uiBase } = receiver\n const title = options.title || 'autotel-devtools'\n process.stdout.write(`\\n ${title}\\n\\n`)\n process.stdout.write(` Listening: ${addresses.join(' + ')}\\n`)\n process.stdout.write(` UI: ${uiBase} (open in a browser)\\n`)\n process.stdout.write(` OTLP: ${uiBase}/v1/traces\\n`)\n process.stdout.write(` WebSocket: ${uiBase.replace('http', 'ws')}/ws\\n\\n`)\n // The widget bundle auto-mounts on load, so the bare <script> tag is all the\n // user needs — spell that out, plus the two opt-in variations, so nobody\n // wonders whether they also have to add an element by hand.\n process.stdout.write(\n ` Embed in your app — paste into your HTML; a floating panel appears automatically:\\n`,\n )\n process.stdout.write(` <script src=\"${uiBase}/widget.js\"></script>\\n\\n`)\n process.stdout.write(\n ` full screen instead: <script src=\"${uiBase}/widget.js?mode=fullpage\"></script>\\n`,\n )\n process.stdout.write(\n ` choose where it goes: add <autotel-devtools></autotel-devtools> to your markup\\n\\n`,\n )\n process.stdout.write(` Or point any OTLP exporter at this receiver:\\n`)\n process.stdout.write(` OTEL_EXPORTER_OTLP_PROTOCOL=http/json\\n`)\n process.stdout.write(` OTEL_EXPORTER_OTLP_ENDPOINT=${uiBase}\\n\\n`)\n // Self-check: confirm the collector is reachable AND that ingestion works,\n // not just that something is listening. Reading /v1/traces back is the same\n // check a test should make instead of trusting \"the client tried to send\".\n process.stdout.write(` Verify ingestion: curl -s ${uiBase}/v1/traces\\n\\n`)\n for (const w of warnings) {\n process.stdout.write(` ⚠ ${w}\\n`)\n }\n if (warnings.length > 0) process.stdout.write('\\n')\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0))\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((error) => {\n process.stderr.write(`[autotel-devtools] failed to start: ${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["hostHeaderIsLoopback","DevtoolsServer","listenLoopbackDualStack","attachDevtoolsRoutes","probePortHolder"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// src/cli.ts\nimport { createServer } from 'node:http';\nimport { spawn } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DevtoolsServer } from './server/server';\nimport { attachDevtoolsRoutes } from './server/http';\nimport { hostHeaderIsLoopback } from './server/origin-guard';\nimport { listenLoopbackDualStack } from './server/listen';\nimport { probePortHolder } from './server/identity';\n\ninterface CliOptions {\n port: number;\n host: string;\n title?: string;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"></script>\n` + '\\n',\n );\n}\n\nfunction printVersion(): void {\n try {\n const dir = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(dir, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n process.stdout.write(`${pkg.version}\\n`);\n } catch {\n process.stdout.write('unknown\\n');\n }\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n const options: CliOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n title: process.env.AUTOTEL_DEVTOOLS_TITLE,\n };\n\n // An explicit `--port`/`-p` always wins, regardless of where it sits in\n // argv. A bare numeric positional is shorthand for `--port`, but only when\n // no explicit flag was given — and only the first positional counts, so a\n // stray \"4318\" can't override an earlier positional either.\n let portWasExplicit = false;\n let positionalPortConsumed = false;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = argv[i + 1];\n if (arg === '--help' || arg === '-h') {\n printHelp();\n return null;\n }\n if (arg === '--version' || arg === '-v') {\n printVersion();\n return null;\n }\n if ((arg === '--port' || arg === '-p') && next) {\n options.port = parsePort(next);\n portWasExplicit = true;\n i++;\n continue;\n }\n if ((arg === '--host' || arg === '-H') && next) {\n options.host = next;\n i++;\n continue;\n }\n if ((arg === '--title' || arg === '-t') && next) {\n options.title = next;\n i++;\n continue;\n }\n if (/^\\d+$/.test(arg) && !positionalPortConsumed) {\n if (!portWasExplicit) options.port = parsePort(arg);\n positionalPortConsumed = true;\n continue;\n }\n }\n\n return options;\n}\n\nfunction parsePort(value: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0 || n > 65535) {\n process.stderr.write(`[autotel-devtools] invalid port: ${value}\\n`);\n process.exit(2);\n }\n return n;\n}\n\ninterface RunningReceiver {\n wsServer: DevtoolsServer;\n closeAll: () => Promise<void>;\n addresses: string[];\n warnings: string[];\n boundPort: number;\n uiBase: string;\n}\n\nasync function startReceiver(options: CliOptions): Promise<RunningReceiver> {\n const httpServer = createServer();\n const loopbackOnly = hostHeaderIsLoopback(options.host);\n const wsServer = new DevtoolsServer({\n server: httpServer,\n host: options.host,\n verbose: true,\n });\n attachDevtoolsRoutes(httpServer, wsServer, { loopbackOnly });\n\n const listeners = listenLoopbackDualStack({\n primary: httpServer,\n port: options.port,\n host: options.host,\n attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer, { loopbackOnly }),\n });\n\n const { addresses, warnings, port: boundPort } = await listeners.ready;\n\n // Falling forward to a different port means the one we wanted is held by\n // someone else. Classify them: another autotel-devtools (benign) versus a\n // foreign process. The foreign case is the silent footgun — apps keep\n // exporting OTLP to the requested port and reach that process, not us, so\n // this UI stays empty while they see export errors. Say so, with the fix.\n if (boundPort !== options.port) {\n const holder = await probePortHolder(options.host, options.port);\n if (holder === 'autotel-devtools') {\n warnings.push(\n `another autotel-devtools is already running on port ${options.port}; ` +\n `this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`,\n );\n } else {\n warnings.push(\n `port ${options.port} is held by another process that is NOT autotel-devtools. ` +\n `Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. ` +\n `Point your exporter at :${boundPort}, or free :${options.port} and restart.`,\n );\n }\n }\n\n const uiBase = `http://${options.host === 'localhost' ? '127.0.0.1' : options.host}:${boundPort}`;\n return {\n wsServer,\n closeAll: () =>\n Promise.all([wsServer.close(), listeners.closeSibling()]).then(\n () => undefined,\n ),\n addresses,\n warnings,\n boundPort,\n uiBase,\n };\n}\n\n// Telemetry env that wires Claude Code (and any OTel-via-env CLI) to this\n// receiver for a *live* local view: HTTP/protobuf (NOT gRPC — this receiver\n// speaks HTTP only), 1s export intervals so events show up promptly, and\n// session.id kept on metrics so metric-only signals join their session.\nfunction buildAgentEnv(\n uiBase: string,\n logPrompts: boolean,\n): Record<string, string> {\n const env: Record<string, string> = {\n CLAUDE_CODE_ENABLE_TELEMETRY: '1',\n OTEL_METRICS_EXPORTER: 'otlp',\n OTEL_LOGS_EXPORTER: 'otlp',\n OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',\n OTEL_EXPORTER_OTLP_ENDPOINT: uiBase,\n OTEL_METRIC_EXPORT_INTERVAL: '1000',\n OTEL_LOGS_EXPORT_INTERVAL: '1000',\n OTEL_METRICS_INCLUDE_SESSION_ID: 'true',\n };\n // Private by default: prompt *text* only flows when explicitly opted in.\n if (logPrompts) env.OTEL_LOG_USER_PROMPTS = '1';\n return env;\n}\n\nfunction printEnvBlock(env: Record<string, string>): void {\n for (const [key, value] of Object.entries(env)) {\n process.stdout.write(`export ${key}=${value}\\n`);\n }\n}\n\ninterface ClaudeOptions {\n port: number;\n host: string;\n printEnv: boolean;\n logPrompts: boolean;\n claudeArgs: string[];\n}\n\nfunction parseClaudeArgs(argv: string[]): ClaudeOptions {\n const opts: ClaudeOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n printEnv: false,\n logPrompts: false,\n claudeArgs: [],\n };\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = argv[i + 1];\n if (arg === '--') {\n opts.claudeArgs.push(...argv.slice(i + 1));\n break;\n }\n if (arg === '--print-env') {\n opts.printEnv = true;\n continue;\n }\n if (arg === '--log-prompts') {\n opts.logPrompts = true;\n continue;\n }\n if ((arg === '--port' || arg === '-p') && next) {\n opts.port = parsePort(next);\n i++;\n continue;\n }\n if ((arg === '--host' || arg === '-H') && next) {\n opts.host = next;\n i++;\n continue;\n }\n // Anything else is passed straight through to `claude`.\n opts.claudeArgs.push(arg);\n }\n return opts;\n}\n\nasync function runClaudeSubcommand(argv: string[]): Promise<void> {\n const opts = parseClaudeArgs(argv);\n\n // --print-env needs no running server: emit the block for the configured\n // port so users can paste it into a shell / managed-settings file.\n if (opts.printEnv) {\n const uiBase = `http://${opts.host === 'localhost' ? '127.0.0.1' : opts.host}:${opts.port}`;\n printEnvBlock(buildAgentEnv(uiBase, opts.logPrompts));\n return;\n }\n\n const receiver = await startReceiver({ port: opts.port, host: opts.host });\n const env = buildAgentEnv(receiver.uiBase, opts.logPrompts);\n\n process.stdout.write(`\\n autotel-devtools — Claude Code\\n\\n`);\n process.stdout.write(` Receiver: ${receiver.uiBase}\\n`);\n process.stdout.write(\n ` UI: ${receiver.uiBase} → open the \"Agents\" tab\\n`,\n );\n if (!opts.logPrompts) {\n process.stdout.write(\n ` Prompts: private (length only) — add --log-prompts to capture text\\n`,\n );\n }\n for (const w of receiver.warnings) process.stdout.write(` ⚠ ${w}\\n`);\n process.stdout.write(`\\n Launching claude…\\n\\n`);\n\n const child = spawn('claude', opts.claudeArgs, {\n stdio: 'inherit',\n env: { ...process.env, ...env },\n });\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n child.on('error', (err) => {\n const reason =\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ? `'claude' not found on PATH. Install Claude Code, or run the receiver alone with 'npx autotel-devtools' and point claude at ${receiver.uiBase} using --print-env.`\n : err.message;\n process.stderr.write(`[autotel-devtools] ${reason}\\n`);\n receiver.closeAll().then(() => process.exit(1));\n });\n child.on('exit', (code) => {\n receiver.closeAll().then(() => process.exit(code ?? 0));\n });\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n\n // Subcommand: `autotel-devtools claude [claude args] [--print-env] [--log-prompts]`\n if (argv[0] === 'claude') {\n await runClaudeSubcommand(argv.slice(1));\n return;\n }\n\n const options = parseArgs(argv);\n if (!options) {\n process.exit(0);\n }\n\n const receiver = await startReceiver(options);\n const { addresses, warnings, uiBase } = receiver;\n const title = options.title || 'autotel-devtools';\n process.stdout.write(`\\n ${title}\\n\\n`);\n process.stdout.write(` Listening: ${addresses.join(' + ')}\\n`);\n process.stdout.write(` UI: ${uiBase} (open in a browser)\\n`);\n process.stdout.write(` OTLP: ${uiBase}/v1/traces\\n`);\n process.stdout.write(` WebSocket: ${uiBase.replace('http', 'ws')}/ws\\n\\n`);\n // The widget bundle auto-mounts on load, so the bare <script> tag is all the\n // user needs — spell that out, plus the two opt-in variations, so nobody\n // wonders whether they also have to add an element by hand.\n process.stdout.write(\n ` Embed in your app — paste into your HTML; a floating panel appears automatically:\\n`,\n );\n process.stdout.write(` <script src=\"${uiBase}/widget.js\"></script>\\n\\n`);\n process.stdout.write(\n ` full screen instead: <script src=\"${uiBase}/widget.js?mode=fullpage\"></script>\\n`,\n );\n process.stdout.write(\n ` choose where it goes: add <autotel-devtools></autotel-devtools> to your markup\\n\\n`,\n );\n process.stdout.write(` Or point any OTLP exporter at this receiver:\\n`);\n process.stdout.write(` OTEL_EXPORTER_OTLP_PROTOCOL=http/json\\n`);\n process.stdout.write(` OTEL_EXPORTER_OTLP_ENDPOINT=${uiBase}\\n\\n`);\n // Self-check: confirm the collector is reachable AND that ingestion works,\n // not just that something is listening. Reading /v1/traces back is the same\n // check a test should make instead of trusting \"the client tried to send\".\n process.stdout.write(` Verify ingestion: curl -s ${uiBase}/v1/traces\\n\\n`);\n for (const w of warnings) {\n process.stdout.write(` ⚠ ${w}\\n`);\n }\n if (warnings.length > 0) process.stdout.write('\\n');\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((error) => {\n process.stderr.write(\n `[autotel-devtools] failed to start: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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"}
|
package/dist/cli.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// src/cli.ts\nimport { createServer } from 'node:http'\nimport { spawn } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { DevtoolsServer } from './server/server'\nimport { attachDevtoolsRoutes } from './server/http'\nimport { hostHeaderIsLoopback } from './server/origin-guard'\nimport { listenLoopbackDualStack } from './server/listen'\nimport { probePortHolder } from './server/identity'\n\ninterface CliOptions {\n port: number\n host: string\n title?: string\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"></script>\n` + '\\n',\n )\n}\n\nfunction printVersion(): void {\n try {\n const dir = dirname(fileURLToPath(import.meta.url))\n const pkgPath = resolve(dir, '..', 'package.json')\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))\n process.stdout.write(`${pkg.version}\\n`)\n } catch {\n process.stdout.write('unknown\\n')\n }\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n const options: CliOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n title: process.env.AUTOTEL_DEVTOOLS_TITLE,\n }\n\n // An explicit `--port`/`-p` always wins, regardless of where it sits in\n // argv. A bare numeric positional is shorthand for `--port`, but only when\n // no explicit flag was given — and only the first positional counts, so a\n // stray \"4318\" can't override an earlier positional either.\n let portWasExplicit = false\n let positionalPortConsumed = false\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n if (arg === '--help' || arg === '-h') { printHelp(); return null }\n if (arg === '--version' || arg === '-v') { printVersion(); return null }\n if ((arg === '--port' || arg === '-p') && next) { options.port = parsePort(next); portWasExplicit = true; i++; continue }\n if ((arg === '--host' || arg === '-H') && next) { options.host = next; i++; continue }\n if ((arg === '--title' || arg === '-t') && next) { options.title = next; i++; continue }\n if (/^\\d+$/.test(arg) && !positionalPortConsumed) {\n if (!portWasExplicit) options.port = parsePort(arg)\n positionalPortConsumed = true\n continue\n }\n }\n\n return options\n}\n\nfunction parsePort(value: string): number {\n const n = Number(value)\n if (!Number.isInteger(n) || n < 0 || n > 65535) {\n process.stderr.write(`[autotel-devtools] invalid port: ${value}\\n`)\n process.exit(2)\n }\n return n\n}\n\ninterface RunningReceiver {\n wsServer: DevtoolsServer\n closeAll: () => Promise<void>\n addresses: string[]\n warnings: string[]\n boundPort: number\n uiBase: string\n}\n\nasync function startReceiver(options: CliOptions): Promise<RunningReceiver> {\n const httpServer = createServer()\n const loopbackOnly = hostHeaderIsLoopback(options.host)\n const wsServer = new DevtoolsServer({ server: httpServer, host: options.host, verbose: true })\n attachDevtoolsRoutes(httpServer, wsServer, { loopbackOnly })\n\n const listeners = listenLoopbackDualStack({\n primary: httpServer,\n port: options.port,\n host: options.host,\n attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer, { loopbackOnly }),\n })\n\n const { addresses, warnings, port: boundPort } = await listeners.ready\n\n // Falling forward to a different port means the one we wanted is held by\n // someone else. Classify them: another autotel-devtools (benign) versus a\n // foreign process. The foreign case is the silent footgun — apps keep\n // exporting OTLP to the requested port and reach that process, not us, so\n // this UI stays empty while they see export errors. Say so, with the fix.\n if (boundPort !== options.port) {\n const holder = await probePortHolder(options.host, options.port)\n if (holder === 'autotel-devtools') {\n warnings.push(\n `another autotel-devtools is already running on port ${options.port}; ` +\n `this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`,\n )\n } else {\n warnings.push(\n `port ${options.port} is held by another process that is NOT autotel-devtools. ` +\n `Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. ` +\n `Point your exporter at :${boundPort}, or free :${options.port} and restart.`,\n )\n }\n }\n\n const uiBase = `http://${options.host === 'localhost' ? '127.0.0.1' : options.host}:${boundPort}`\n return {\n wsServer,\n closeAll: () => Promise.all([wsServer.close(), listeners.closeSibling()]).then(() => undefined),\n addresses,\n warnings,\n boundPort,\n uiBase,\n }\n}\n\n// Telemetry env that wires Claude Code (and any OTel-via-env CLI) to this\n// receiver for a *live* local view: HTTP/protobuf (NOT gRPC — this receiver\n// speaks HTTP only), 1s export intervals so events show up promptly, and\n// session.id kept on metrics so metric-only signals join their session.\nfunction buildAgentEnv(uiBase: string, logPrompts: boolean): Record<string, string> {\n const env: Record<string, string> = {\n CLAUDE_CODE_ENABLE_TELEMETRY: '1',\n OTEL_METRICS_EXPORTER: 'otlp',\n OTEL_LOGS_EXPORTER: 'otlp',\n OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',\n OTEL_EXPORTER_OTLP_ENDPOINT: uiBase,\n OTEL_METRIC_EXPORT_INTERVAL: '1000',\n OTEL_LOGS_EXPORT_INTERVAL: '1000',\n OTEL_METRICS_INCLUDE_SESSION_ID: 'true',\n }\n // Private by default: prompt *text* only flows when explicitly opted in.\n if (logPrompts) env.OTEL_LOG_USER_PROMPTS = '1'\n return env\n}\n\nfunction printEnvBlock(env: Record<string, string>): void {\n for (const [key, value] of Object.entries(env)) {\n process.stdout.write(`export ${key}=${value}\\n`)\n }\n}\n\ninterface ClaudeOptions {\n port: number\n host: string\n printEnv: boolean\n logPrompts: boolean\n claudeArgs: string[]\n}\n\nfunction parseClaudeArgs(argv: string[]): ClaudeOptions {\n const opts: ClaudeOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n printEnv: false,\n logPrompts: false,\n claudeArgs: [],\n }\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]\n const next = argv[i + 1]\n if (arg === '--') { opts.claudeArgs.push(...argv.slice(i + 1)); break }\n if (arg === '--print-env') { opts.printEnv = true; continue }\n if (arg === '--log-prompts') { opts.logPrompts = true; continue }\n if ((arg === '--port' || arg === '-p') && next) { opts.port = parsePort(next); i++; continue }\n if ((arg === '--host' || arg === '-H') && next) { opts.host = next; i++; continue }\n // Anything else is passed straight through to `claude`.\n opts.claudeArgs.push(arg)\n }\n return opts\n}\n\nasync function runClaudeSubcommand(argv: string[]): Promise<void> {\n const opts = parseClaudeArgs(argv)\n\n // --print-env needs no running server: emit the block for the configured\n // port so users can paste it into a shell / managed-settings file.\n if (opts.printEnv) {\n const uiBase = `http://${opts.host === 'localhost' ? '127.0.0.1' : opts.host}:${opts.port}`\n printEnvBlock(buildAgentEnv(uiBase, opts.logPrompts))\n return\n }\n\n const receiver = await startReceiver({ port: opts.port, host: opts.host })\n const env = buildAgentEnv(receiver.uiBase, opts.logPrompts)\n\n process.stdout.write(`\\n autotel-devtools — Claude Code\\n\\n`)\n process.stdout.write(` Receiver: ${receiver.uiBase}\\n`)\n process.stdout.write(` UI: ${receiver.uiBase} → open the \"Agents\" tab\\n`)\n if (!opts.logPrompts) {\n process.stdout.write(` Prompts: private (length only) — add --log-prompts to capture text\\n`)\n }\n for (const w of receiver.warnings) process.stdout.write(` ⚠ ${w}\\n`)\n process.stdout.write(`\\n Launching claude…\\n\\n`)\n\n const child = spawn('claude', opts.claudeArgs, {\n stdio: 'inherit',\n env: { ...process.env, ...env },\n })\n\n const shutdown = () => { receiver.closeAll().then(() => process.exit(0)) }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n\n child.on('error', (err) => {\n const reason = (err as NodeJS.ErrnoException).code === 'ENOENT'\n ? `'claude' not found on PATH. Install Claude Code, or run the receiver alone with 'npx autotel-devtools' and point claude at ${receiver.uiBase} using --print-env.`\n : err.message\n process.stderr.write(`[autotel-devtools] ${reason}\\n`)\n receiver.closeAll().then(() => process.exit(1))\n })\n child.on('exit', (code) => {\n receiver.closeAll().then(() => process.exit(code ?? 0))\n })\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2)\n\n // Subcommand: `autotel-devtools claude [claude args] [--print-env] [--log-prompts]`\n if (argv[0] === 'claude') {\n await runClaudeSubcommand(argv.slice(1))\n return\n }\n\n const options = parseArgs(argv)\n if (!options) { process.exit(0) }\n\n const receiver = await startReceiver(options)\n const { addresses, warnings, uiBase } = receiver\n const title = options.title || 'autotel-devtools'\n process.stdout.write(`\\n ${title}\\n\\n`)\n process.stdout.write(` Listening: ${addresses.join(' + ')}\\n`)\n process.stdout.write(` UI: ${uiBase} (open in a browser)\\n`)\n process.stdout.write(` OTLP: ${uiBase}/v1/traces\\n`)\n process.stdout.write(` WebSocket: ${uiBase.replace('http', 'ws')}/ws\\n\\n`)\n // The widget bundle auto-mounts on load, so the bare <script> tag is all the\n // user needs — spell that out, plus the two opt-in variations, so nobody\n // wonders whether they also have to add an element by hand.\n process.stdout.write(\n ` Embed in your app — paste into your HTML; a floating panel appears automatically:\\n`,\n )\n process.stdout.write(` <script src=\"${uiBase}/widget.js\"></script>\\n\\n`)\n process.stdout.write(\n ` full screen instead: <script src=\"${uiBase}/widget.js?mode=fullpage\"></script>\\n`,\n )\n process.stdout.write(\n ` choose where it goes: add <autotel-devtools></autotel-devtools> to your markup\\n\\n`,\n )\n process.stdout.write(` Or point any OTLP exporter at this receiver:\\n`)\n process.stdout.write(` OTEL_EXPORTER_OTLP_PROTOCOL=http/json\\n`)\n process.stdout.write(` OTEL_EXPORTER_OTLP_ENDPOINT=${uiBase}\\n\\n`)\n // Self-check: confirm the collector is reachable AND that ingestion works,\n // not just that something is listening. Reading /v1/traces back is the same\n // check a test should make instead of trusting \"the client tried to send\".\n process.stdout.write(` Verify ingestion: curl -s ${uiBase}/v1/traces\\n\\n`)\n for (const w of warnings) {\n process.stdout.write(` ⚠ ${w}\\n`)\n }\n if (warnings.length > 0) process.stdout.write('\\n')\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0))\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n}\n\nmain().catch((error) => {\n process.stderr.write(`[autotel-devtools] failed to start: ${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// src/cli.ts\nimport { createServer } from 'node:http';\nimport { spawn } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DevtoolsServer } from './server/server';\nimport { attachDevtoolsRoutes } from './server/http';\nimport { hostHeaderIsLoopback } from './server/origin-guard';\nimport { listenLoopbackDualStack } from './server/listen';\nimport { probePortHolder } from './server/identity';\n\ninterface CliOptions {\n port: number;\n host: string;\n title?: string;\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n `autotel-devtools - Standalone OTLP receiver with web devtools UI\n\nUsage: autotel-devtools [port] [options]\n autotel-devtools claude [claude args] [--print-env] [--log-prompts]\n\nSubcommands:\n claude Start the receiver AND launch Claude Code wired to it\n (HTTP/protobuf → this receiver, 1s intervals, session id on).\n Open the \"Agents\" tab to watch sessions, tokens, cost,\n tool / MCP / sub-agent / skill usage live.\n --print-env Print the telemetry env block and exit (don't launch)\n --log-prompts Capture prompt text (default: length only / private)\n\nArguments:\n port Port to listen on (shorthand for --port; must be a positive integer)\n\nOptions:\n -p, --port <port> Port to listen on (default: 4318, env: AUTOTEL_DEVTOOLS_PORT).\n If the port is taken, the next free port is used and a warning is shown.\n -H, --host <host> Host to bind to (default: 127.0.0.1, env: AUTOTEL_DEVTOOLS_HOST)\n -t, --title <title> Dashboard title (env: AUTOTEL_DEVTOOLS_TITLE)\n Env limits: AUTOTEL_MAX_TRACE_COUNT, AUTOTEL_MAX_LOG_COUNT, AUTOTEL_MAX_METRIC_COUNT\n -h, --help Show this help message\n -v, --version Show version number\n\nEndpoints:\n GET / Web devtools UI (fullpage)\n GET /widget.js Widget bundle (embed in your app)\n POST /v1/traces Receive OTLP JSON trace data\n GET /v1/traces Read back received traces (verify ingestion in tests)\n DELETE /v1/traces Clear captured telemetry (test reset)\n POST /v1/logs Receive OTLP JSON log data\n POST /v1/metrics Receive OTLP JSON metric data\n WS /ws WebSocket stream for real-time updates\n GET /healthz Health check\n\nExamples:\n npx autotel-devtools\n npx autotel-devtools 4319\n npx autotel-devtools -p 4319 -H 0.0.0.0\n npx autotel-devtools claude # watch Claude Code in the Agents tab\n npx autotel-devtools claude --print-env # just print the env (for MDM / VS Code)\n\nThen point your app:\n OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node app.js\n\nView in browser:\n http://localhost:4318\n\nOr embed widget in your app:\n <script src=\"http://localhost:4318/widget.js\"></script>\n` + '\\n',\n );\n}\n\nfunction printVersion(): void {\n try {\n const dir = dirname(fileURLToPath(import.meta.url));\n const pkgPath = resolve(dir, '..', 'package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));\n process.stdout.write(`${pkg.version}\\n`);\n } catch {\n process.stdout.write('unknown\\n');\n }\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n const options: CliOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n title: process.env.AUTOTEL_DEVTOOLS_TITLE,\n };\n\n // An explicit `--port`/`-p` always wins, regardless of where it sits in\n // argv. A bare numeric positional is shorthand for `--port`, but only when\n // no explicit flag was given — and only the first positional counts, so a\n // stray \"4318\" can't override an earlier positional either.\n let portWasExplicit = false;\n let positionalPortConsumed = false;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = argv[i + 1];\n if (arg === '--help' || arg === '-h') {\n printHelp();\n return null;\n }\n if (arg === '--version' || arg === '-v') {\n printVersion();\n return null;\n }\n if ((arg === '--port' || arg === '-p') && next) {\n options.port = parsePort(next);\n portWasExplicit = true;\n i++;\n continue;\n }\n if ((arg === '--host' || arg === '-H') && next) {\n options.host = next;\n i++;\n continue;\n }\n if ((arg === '--title' || arg === '-t') && next) {\n options.title = next;\n i++;\n continue;\n }\n if (/^\\d+$/.test(arg) && !positionalPortConsumed) {\n if (!portWasExplicit) options.port = parsePort(arg);\n positionalPortConsumed = true;\n continue;\n }\n }\n\n return options;\n}\n\nfunction parsePort(value: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0 || n > 65535) {\n process.stderr.write(`[autotel-devtools] invalid port: ${value}\\n`);\n process.exit(2);\n }\n return n;\n}\n\ninterface RunningReceiver {\n wsServer: DevtoolsServer;\n closeAll: () => Promise<void>;\n addresses: string[];\n warnings: string[];\n boundPort: number;\n uiBase: string;\n}\n\nasync function startReceiver(options: CliOptions): Promise<RunningReceiver> {\n const httpServer = createServer();\n const loopbackOnly = hostHeaderIsLoopback(options.host);\n const wsServer = new DevtoolsServer({\n server: httpServer,\n host: options.host,\n verbose: true,\n });\n attachDevtoolsRoutes(httpServer, wsServer, { loopbackOnly });\n\n const listeners = listenLoopbackDualStack({\n primary: httpServer,\n port: options.port,\n host: options.host,\n attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer, { loopbackOnly }),\n });\n\n const { addresses, warnings, port: boundPort } = await listeners.ready;\n\n // Falling forward to a different port means the one we wanted is held by\n // someone else. Classify them: another autotel-devtools (benign) versus a\n // foreign process. The foreign case is the silent footgun — apps keep\n // exporting OTLP to the requested port and reach that process, not us, so\n // this UI stays empty while they see export errors. Say so, with the fix.\n if (boundPort !== options.port) {\n const holder = await probePortHolder(options.host, options.port);\n if (holder === 'autotel-devtools') {\n warnings.push(\n `another autotel-devtools is already running on port ${options.port}; ` +\n `this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`,\n );\n } else {\n warnings.push(\n `port ${options.port} is held by another process that is NOT autotel-devtools. ` +\n `Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. ` +\n `Point your exporter at :${boundPort}, or free :${options.port} and restart.`,\n );\n }\n }\n\n const uiBase = `http://${options.host === 'localhost' ? '127.0.0.1' : options.host}:${boundPort}`;\n return {\n wsServer,\n closeAll: () =>\n Promise.all([wsServer.close(), listeners.closeSibling()]).then(\n () => undefined,\n ),\n addresses,\n warnings,\n boundPort,\n uiBase,\n };\n}\n\n// Telemetry env that wires Claude Code (and any OTel-via-env CLI) to this\n// receiver for a *live* local view: HTTP/protobuf (NOT gRPC — this receiver\n// speaks HTTP only), 1s export intervals so events show up promptly, and\n// session.id kept on metrics so metric-only signals join their session.\nfunction buildAgentEnv(\n uiBase: string,\n logPrompts: boolean,\n): Record<string, string> {\n const env: Record<string, string> = {\n CLAUDE_CODE_ENABLE_TELEMETRY: '1',\n OTEL_METRICS_EXPORTER: 'otlp',\n OTEL_LOGS_EXPORTER: 'otlp',\n OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',\n OTEL_EXPORTER_OTLP_ENDPOINT: uiBase,\n OTEL_METRIC_EXPORT_INTERVAL: '1000',\n OTEL_LOGS_EXPORT_INTERVAL: '1000',\n OTEL_METRICS_INCLUDE_SESSION_ID: 'true',\n };\n // Private by default: prompt *text* only flows when explicitly opted in.\n if (logPrompts) env.OTEL_LOG_USER_PROMPTS = '1';\n return env;\n}\n\nfunction printEnvBlock(env: Record<string, string>): void {\n for (const [key, value] of Object.entries(env)) {\n process.stdout.write(`export ${key}=${value}\\n`);\n }\n}\n\ninterface ClaudeOptions {\n port: number;\n host: string;\n printEnv: boolean;\n logPrompts: boolean;\n claudeArgs: string[];\n}\n\nfunction parseClaudeArgs(argv: string[]): ClaudeOptions {\n const opts: ClaudeOptions = {\n port: parsePort(process.env.AUTOTEL_DEVTOOLS_PORT || '4318'),\n host: process.env.AUTOTEL_DEVTOOLS_HOST || '127.0.0.1',\n printEnv: false,\n logPrompts: false,\n claudeArgs: [],\n };\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = argv[i + 1];\n if (arg === '--') {\n opts.claudeArgs.push(...argv.slice(i + 1));\n break;\n }\n if (arg === '--print-env') {\n opts.printEnv = true;\n continue;\n }\n if (arg === '--log-prompts') {\n opts.logPrompts = true;\n continue;\n }\n if ((arg === '--port' || arg === '-p') && next) {\n opts.port = parsePort(next);\n i++;\n continue;\n }\n if ((arg === '--host' || arg === '-H') && next) {\n opts.host = next;\n i++;\n continue;\n }\n // Anything else is passed straight through to `claude`.\n opts.claudeArgs.push(arg);\n }\n return opts;\n}\n\nasync function runClaudeSubcommand(argv: string[]): Promise<void> {\n const opts = parseClaudeArgs(argv);\n\n // --print-env needs no running server: emit the block for the configured\n // port so users can paste it into a shell / managed-settings file.\n if (opts.printEnv) {\n const uiBase = `http://${opts.host === 'localhost' ? '127.0.0.1' : opts.host}:${opts.port}`;\n printEnvBlock(buildAgentEnv(uiBase, opts.logPrompts));\n return;\n }\n\n const receiver = await startReceiver({ port: opts.port, host: opts.host });\n const env = buildAgentEnv(receiver.uiBase, opts.logPrompts);\n\n process.stdout.write(`\\n autotel-devtools — Claude Code\\n\\n`);\n process.stdout.write(` Receiver: ${receiver.uiBase}\\n`);\n process.stdout.write(\n ` UI: ${receiver.uiBase} → open the \"Agents\" tab\\n`,\n );\n if (!opts.logPrompts) {\n process.stdout.write(\n ` Prompts: private (length only) — add --log-prompts to capture text\\n`,\n );\n }\n for (const w of receiver.warnings) process.stdout.write(` ⚠ ${w}\\n`);\n process.stdout.write(`\\n Launching claude…\\n\\n`);\n\n const child = spawn('claude', opts.claudeArgs, {\n stdio: 'inherit',\n env: { ...process.env, ...env },\n });\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n child.on('error', (err) => {\n const reason =\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ? `'claude' not found on PATH. Install Claude Code, or run the receiver alone with 'npx autotel-devtools' and point claude at ${receiver.uiBase} using --print-env.`\n : err.message;\n process.stderr.write(`[autotel-devtools] ${reason}\\n`);\n receiver.closeAll().then(() => process.exit(1));\n });\n child.on('exit', (code) => {\n receiver.closeAll().then(() => process.exit(code ?? 0));\n });\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n\n // Subcommand: `autotel-devtools claude [claude args] [--print-env] [--log-prompts]`\n if (argv[0] === 'claude') {\n await runClaudeSubcommand(argv.slice(1));\n return;\n }\n\n const options = parseArgs(argv);\n if (!options) {\n process.exit(0);\n }\n\n const receiver = await startReceiver(options);\n const { addresses, warnings, uiBase } = receiver;\n const title = options.title || 'autotel-devtools';\n process.stdout.write(`\\n ${title}\\n\\n`);\n process.stdout.write(` Listening: ${addresses.join(' + ')}\\n`);\n process.stdout.write(` UI: ${uiBase} (open in a browser)\\n`);\n process.stdout.write(` OTLP: ${uiBase}/v1/traces\\n`);\n process.stdout.write(` WebSocket: ${uiBase.replace('http', 'ws')}/ws\\n\\n`);\n // The widget bundle auto-mounts on load, so the bare <script> tag is all the\n // user needs — spell that out, plus the two opt-in variations, so nobody\n // wonders whether they also have to add an element by hand.\n process.stdout.write(\n ` Embed in your app — paste into your HTML; a floating panel appears automatically:\\n`,\n );\n process.stdout.write(` <script src=\"${uiBase}/widget.js\"></script>\\n\\n`);\n process.stdout.write(\n ` full screen instead: <script src=\"${uiBase}/widget.js?mode=fullpage\"></script>\\n`,\n );\n process.stdout.write(\n ` choose where it goes: add <autotel-devtools></autotel-devtools> to your markup\\n\\n`,\n );\n process.stdout.write(` Or point any OTLP exporter at this receiver:\\n`);\n process.stdout.write(` OTEL_EXPORTER_OTLP_PROTOCOL=http/json\\n`);\n process.stdout.write(` OTEL_EXPORTER_OTLP_ENDPOINT=${uiBase}\\n\\n`);\n // Self-check: confirm the collector is reachable AND that ingestion works,\n // not just that something is listening. Reading /v1/traces back is the same\n // check a test should make instead of trusting \"the client tried to send\".\n process.stdout.write(` Verify ingestion: curl -s ${uiBase}/v1/traces\\n\\n`);\n for (const w of warnings) {\n process.stdout.write(` ⚠ ${w}\\n`);\n }\n if (warnings.length > 0) process.stdout.write('\\n');\n\n const shutdown = () => {\n receiver.closeAll().then(() => process.exit(0));\n };\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nmain().catch((error) => {\n process.stderr.write(\n `[autotel-devtools] failed to start: ${error instanceof Error ? error.message : String(error)}\\n`,\n );\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-
|
|
2
|
-
|
|
1
|
+
import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-BLqTs00O.js";
|
|
3
2
|
//#region src/server/error-aggregator.d.ts
|
|
4
3
|
interface ErrorAggregatorOptions {
|
|
5
4
|
/**
|
|
@@ -117,4 +116,4 @@ declare class ErrorAggregator {
|
|
|
117
116
|
}
|
|
118
117
|
//#endregion
|
|
119
118
|
export { ErrorAggregator as t };
|
|
120
|
-
//# sourceMappingURL=error-aggregator-
|
|
119
|
+
//# sourceMappingURL=error-aggregator-BrgYNNoC.d.ts.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-
|
|
2
|
-
|
|
1
|
+
import { a as ErrorGroup, o as ErrorOccurrence, u as TraceData } from "./exporter-BSIS3Qgy.cjs";
|
|
3
2
|
//#region src/server/error-aggregator.d.ts
|
|
4
3
|
interface ErrorAggregatorOptions {
|
|
5
4
|
/**
|
|
@@ -117,4 +116,4 @@ declare class ErrorAggregator {
|
|
|
117
116
|
}
|
|
118
117
|
//#endregion
|
|
119
118
|
export { ErrorAggregator as t };
|
|
120
|
-
//# sourceMappingURL=error-aggregator-
|
|
119
|
+
//# sourceMappingURL=error-aggregator-bZ-prq97.d.cts.map
|
|
@@ -2,7 +2,6 @@ import { Server } from "node:http";
|
|
|
2
2
|
import { AgentRawEvent, AgentSession, OtelMetricRecord } from "autotel-agents";
|
|
3
3
|
import { ExportResult } from "@opentelemetry/core";
|
|
4
4
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
5
|
-
|
|
6
5
|
//#region src/server/types.d.ts
|
|
7
6
|
interface SpanData {
|
|
8
7
|
traceId: string;
|
|
@@ -189,4 +188,4 @@ declare class DevtoolsSpanExporter implements SpanExporter {
|
|
|
189
188
|
}
|
|
190
189
|
//#endregion
|
|
191
190
|
export { ErrorGroup as a, MetricData as c, DevtoolsData as i, SpanData as l, DevtoolsServer as n, ErrorOccurrence as o, DevtoolsServerOptions as r, LogData as s, DevtoolsSpanExporter as t, TraceData as u };
|
|
192
|
-
//# sourceMappingURL=exporter-
|
|
191
|
+
//# sourceMappingURL=exporter-BLqTs00O.d.ts.map
|
|
@@ -2,7 +2,6 @@ import { Server } from "node:http";
|
|
|
2
2
|
import { AgentRawEvent, AgentSession, OtelMetricRecord } from "autotel-agents";
|
|
3
3
|
import { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base";
|
|
4
4
|
import { ExportResult } from "@opentelemetry/core";
|
|
5
|
-
|
|
6
5
|
//#region src/server/types.d.ts
|
|
7
6
|
interface SpanData {
|
|
8
7
|
traceId: string;
|
|
@@ -189,4 +188,4 @@ declare class DevtoolsSpanExporter implements SpanExporter {
|
|
|
189
188
|
}
|
|
190
189
|
//#endregion
|
|
191
190
|
export { ErrorGroup as a, MetricData as c, DevtoolsData as i, SpanData as l, DevtoolsServer as n, ErrorOccurrence as o, DevtoolsServerOptions as r, LogData as s, DevtoolsSpanExporter as t, TraceData as u };
|
|
192
|
-
//# sourceMappingURL=exporter-
|
|
191
|
+
//# sourceMappingURL=exporter-BSIS3Qgy.d.cts.map
|