llmtap 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,292 @@
1
+ <p align="center">
2
+ <h1 align="center">LLMTap</h1>
3
+ </p>
4
+
5
+ <h3 align="center">DevTools for AI Agents</h3>
6
+
7
+ <p align="center">
8
+ See every LLM call. Trace agent workflows. Track costs.<br/>
9
+ Local-first. Zero-config. Two lines of code.
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="#quick-start">Quick Start</a> &middot;
14
+ <a href="#features">Features</a> &middot;
15
+ <a href="#supported-providers">Providers</a> &middot;
16
+ <a href="#api-reference">API</a> &middot;
17
+ <a href="#architecture">Architecture</a>
18
+ </p>
19
+
20
+ ---
21
+
22
+ Most LLM observability tools require cloud accounts, proxy configurations, or framework-specific callbacks. LLMTap takes a different approach: it runs entirely on your machine, instruments any LLM client with a single function call, and gives you a real-time dashboard at `localhost`.
23
+
24
+ No sign-ups. No API keys to manage the tool itself. Your prompts and responses never leave your machine.
25
+
26
+ <!-- hero-screenshot -->
27
+
28
+ ---
29
+
30
+ ## Quick Start
31
+
32
+ **1. Start LLMTap**
33
+
34
+ ```bash
35
+ npx llmtap
36
+ ```
37
+
38
+ The collector starts on `http://localhost:4781` and opens the dashboard.
39
+
40
+ **2. Instrument your code**
41
+
42
+ ```typescript
43
+ import OpenAI from "openai";
44
+ import { wrap } from "@llmtap/sdk";
45
+
46
+ const client = wrap(new OpenAI());
47
+ ```
48
+
49
+ **3. Use your client as normal**
50
+
51
+ ```typescript
52
+ const res = await client.chat.completions.create({
53
+ model: "gpt-4o",
54
+ messages: [{ role: "user", content: "Hello, world" }],
55
+ });
56
+ // Traced automatically -- tokens, cost, latency, full request/response
57
+ ```
58
+
59
+ Open `http://localhost:4781`. Traces appear in real time.
60
+
61
+ ---
62
+
63
+ ## Features
64
+
65
+ **Transparent instrumentation** -- `wrap()` returns an ES Proxy. Your client behaves identically. No code changes beyond the wrap call. Streaming, tool calls, and multi-turn conversations all work.
66
+
67
+ **Real-time dashboard** -- Traces stream to the browser via SSE. See every LLM call as it happens with token counts, costs, latency, and full message content.
68
+
69
+ <!-- dashboard-screenshot -->
70
+
71
+ **Cost tracking** -- Built-in pricing for 50+ models across all major providers. Input and output costs calculated per-call. Override pricing for custom or fine-tuned models at runtime.
72
+
73
+ **Trace grouping** -- Group multiple LLM calls into a single trace with `startTrace()`. See total cost and token usage for multi-step agent pipelines.
74
+
75
+ ```typescript
76
+ import { wrap, startTrace } from "@llmtap/sdk";
77
+
78
+ await startTrace("research-agent", async () => {
79
+ const plan = await client.chat.completions.create({ model: "gpt-4o", messages: [...] });
80
+ const draft = await client.chat.completions.create({ model: "gpt-4o-mini", messages: [...] });
81
+ });
82
+ // Both calls grouped under "research-agent" in the dashboard
83
+ ```
84
+
85
+ **Streaming support** -- Full support for streaming responses across all providers. Token counts and costs are captured after the stream completes. Your application sees the exact same stream.
86
+
87
+ **OpenTelemetry export** -- Export traces in OTLP format following GenAI Semantic Conventions. Forward to Datadog, Grafana, Jaeger, or any OTLP-compatible backend.
88
+
89
+ ```bash
90
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 npx llmtap
91
+ ```
92
+
93
+ **Privacy by default** -- Everything runs locally. No data is sent anywhere unless you explicitly configure OTLP forwarding. Content capture can be disabled entirely.
94
+
95
+ ---
96
+
97
+ ## Supported Providers
98
+
99
+ LLMTap works with any provider that uses the OpenAI, Anthropic, or Google Gemini SDK format.
100
+
101
+ | Provider | SDK | Detection |
102
+ |----------|-----|-----------|
103
+ | **OpenAI** | `openai` | Automatic |
104
+ | **Anthropic** | `@anthropic-ai/sdk` | Automatic |
105
+ | **Google Gemini** | `@google/generative-ai` | Automatic |
106
+ | **DeepSeek** | `openai` (compatible) | Auto via base URL |
107
+ | **Groq** | `openai` (compatible) | Auto via base URL |
108
+ | **Together** | `openai` (compatible) | Auto via base URL |
109
+ | **Fireworks** | `openai` (compatible) | Auto via base URL |
110
+ | **OpenRouter** | `openai` (compatible) | Auto via base URL |
111
+ | **xAI (Grok)** | `openai` (compatible) | Auto via base URL |
112
+ | **Ollama** | `openai` (compatible) | Auto via base URL |
113
+ | **Vercel AI SDK** | `ai` | Via `wrapVercelAI()` |
114
+ | **Any OpenAI-compatible** | `openai` | Manual or auto |
115
+
116
+ ```typescript
117
+ // OpenAI
118
+ const openai = wrap(new OpenAI());
119
+
120
+ // Anthropic
121
+ import Anthropic from "@anthropic-ai/sdk";
122
+ const claude = wrap(new Anthropic());
123
+
124
+ // Google Gemini
125
+ import { GoogleGenerativeAI } from "@google/generative-ai";
126
+ const gemini = wrap(new GoogleGenerativeAI(process.env.GOOGLE_API_KEY));
127
+
128
+ // DeepSeek, Groq, or any OpenAI-compatible provider
129
+ const deepseek = wrap(new OpenAI({
130
+ baseURL: "https://api.deepseek.com",
131
+ apiKey: process.env.DEEPSEEK_API_KEY,
132
+ }));
133
+ ```
134
+
135
+ ---
136
+
137
+ ## API Reference
138
+
139
+ ### `wrap(client, options?)`
140
+
141
+ Wraps an LLM client to trace all API calls. Returns a proxy -- the client works identically.
142
+
143
+ ```typescript
144
+ const client = wrap(new OpenAI());
145
+ const client = wrap(new OpenAI(), { provider: "deepseek", tags: { env: "staging" } });
146
+ ```
147
+
148
+ | Option | Type | Description |
149
+ |--------|------|-------------|
150
+ | `provider` | `string` | Override auto-detected provider name |
151
+ | `tags` | `Record<string, string>` | Custom tags attached to every span |
152
+
153
+ ### `startTrace(name, fn, options?)`
154
+
155
+ Groups multiple LLM calls under a single trace.
156
+
157
+ ```typescript
158
+ const result = await startTrace("my-pipeline", async () => {
159
+ const step1 = await client.chat.completions.create({ ... });
160
+ const step2 = await client.chat.completions.create({ ... });
161
+ return step2;
162
+ }, { sessionId: "user-123", tags: { workflow: "summarize" } });
163
+ ```
164
+
165
+ | Option | Type | Description |
166
+ |--------|------|-------------|
167
+ | `sessionId` | `string` | Group traces into a session |
168
+ | `tags` | `Record<string, string>` | Custom tags on the trace |
169
+
170
+ ### `init(config)`
171
+
172
+ Configure the SDK globally. All options can also be set via environment variables.
173
+
174
+ ```typescript
175
+ import { init } from "@llmtap/sdk";
176
+
177
+ init({
178
+ collectorUrl: "http://localhost:4781",
179
+ captureContent: true,
180
+ enabled: true,
181
+ debug: false,
182
+ sessionId: "my-session",
183
+ onError: (err, ctx) => console.warn("LLMTap:", err.message),
184
+ });
185
+ ```
186
+
187
+ | Config | Env Var | Default | Description |
188
+ |--------|---------|---------|-------------|
189
+ | `collectorUrl` | `LLMTAP_COLLECTOR_URL` | `http://localhost:4781` | Collector endpoint |
190
+ | `captureContent` | `LLMTAP_CAPTURE_CONTENT` | `true` | Capture message content |
191
+ | `enabled` | `LLMTAP_ENABLED` | `true` | Enable/disable tracing |
192
+ | `debug` | `LLMTAP_DEBUG` | `false` | Debug logging |
193
+ | `sessionId` | `LLMTAP_SESSION_ID` | -- | Session grouping |
194
+
195
+ ### `wrapVercelAI(ai)`
196
+
197
+ Wraps the Vercel AI SDK for framework-level tracing across any underlying provider.
198
+
199
+ ### `shutdown()`
200
+
201
+ Flushes all buffered spans and shuts down the SDK. Call before process exit in serverless environments.
202
+
203
+ ```typescript
204
+ import { shutdown } from "@llmtap/sdk";
205
+ await shutdown();
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Architecture
211
+
212
+ ```
213
+ Your Application
214
+ |
215
+ | wrap(client) -- ES Proxy intercepts LLM calls
216
+ |
217
+ v
218
+ @llmtap/sdk ───────────> @llmtap/collector ───────────> @llmtap/dashboard
219
+ Proxy-based Fastify + SQLite React + Vite
220
+ instrumentation REST API + SSE Real-time UI
221
+ | |
222
+ | Batched HTTP POST | SSE push on new spans Connects via SSE
223
+ | to /v1/spans | GET /v1/traces, /v1/stats and REST API
224
+ v v
225
+ ┌─────────────┐
226
+ │ SQLite DB │ Optional: OTLP export to
227
+ │ (WAL mode) │ ───> Datadog, Grafana, Jaeger
228
+ └─────────────┘
229
+ ```
230
+
231
+ | Package | Description |
232
+ |---------|-------------|
233
+ | `llmtap` | CLI entry point -- `npx llmtap` starts collector + dashboard |
234
+ | `@llmtap/sdk` | ES Proxy-based instrumentation for LLM clients |
235
+ | `@llmtap/collector` | Fastify server, SQLite storage, SSE, REST API |
236
+ | `@llmtap/dashboard` | React + Vite + Tailwind SPA with real-time updates |
237
+ | `@llmtap/shared` | Types, constants, pricing data, OTLP converter |
238
+
239
+ ---
240
+
241
+ ## CLI
242
+
243
+ ```bash
244
+ npx llmtap # Start collector + dashboard
245
+ npx llmtap --demo # Start with sample data
246
+ npx llmtap --port 8080 # Custom port
247
+ npx llmtap --retention 7d # Auto-delete old data
248
+ npx llmtap --host 0.0.0.0 # Expose to network
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Comparison
254
+
255
+ | | LLMTap | LangSmith | Helicone | Langfuse |
256
+ |---|---|---|---|---|
257
+ | **Setup** | `npx` + 2 lines | SDK + cloud account | Proxy + cloud | SDK + self-host or cloud |
258
+ | **Data location** | Your machine | Their cloud | Their cloud | Your infra or theirs |
259
+ | **Pricing** | Free, no limits | $39/seat/mo | $79/mo | Free (self-host) |
260
+ | **Instrumentation** | `wrap(client)` | Framework-specific | Proxy gateway | SDK callbacks |
261
+
262
+ LLMTap is a developer tool -- fast to start, private by default, zero friction. Use it during development and prototyping. When you need production infrastructure, export your traces via OTLP to the platform of your choice.
263
+
264
+ ---
265
+
266
+ ## Development
267
+
268
+ ```bash
269
+ git clone https://github.com/DivyaanshuXD/LLMTap.git
270
+ cd llmtap
271
+ pnpm install
272
+ pnpm build # Build all packages
273
+ pnpm test # Run all tests (Vitest)
274
+ ```
275
+
276
+ TypeScript monorepo with pnpm workspaces and Turborepo. Packages build with tsup, the dashboard builds with Vite.
277
+
278
+ ## Contributing
279
+
280
+ Contributions are welcome.
281
+
282
+ 1. Fork the repository
283
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
284
+ 3. Make your changes and add tests
285
+ 4. Run `pnpm build && pnpm test` to verify
286
+ 5. Open a pull request
287
+
288
+ Please open an issue first for large changes so we can discuss the approach.
289
+
290
+ ## License
291
+
292
+ MIT
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __dirname = path.dirname(fileURLToPath(import.meta.url));
14
14
  async function startCommand(options) {
15
15
  const port = parseInt(options.port, 10);
16
16
  const retentionDays = options.retention ? parseInt(options.retention, 10) : void 0;
17
- const bundledPath = path.resolve(__dirname, "..", "dashboard");
17
+ const bundledPath = path.resolve(__dirname, "dashboard");
18
18
  const monorepoPath = path.resolve(__dirname, "..", "..", "dashboard", "dist");
19
19
  const dashboardPath = fs.existsSync(bundledPath) ? bundledPath : monorepoPath;
20
20
  console.log("");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/commands/reset.ts","../src/commands/export.ts","../src/commands/status.ts","../src/commands/tail.ts","../src/commands/doctor.ts","../src/commands/stats.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { startCommand } from \"./commands/start.js\";\nimport { resetCommand } from \"./commands/reset.js\";\nimport { exportCommand } from \"./commands/export.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { tailCommand } from \"./commands/tail.js\";\nimport { doctorCommand } from \"./commands/doctor.js\";\nimport { statsCommand } from \"./commands/stats.js\";\nimport { VERSION } from \"@llmtap/shared\";\n\nconst program = new Command();\n\nprogram\n .name(\"llmtap\")\n .description(\"DevTools for AI Agents - See every LLM call, trace agent workflows, track costs\")\n .version(VERSION);\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"Start the LLMTap collector and dashboard\")\n .option(\"-p, --port <port>\", \"Port number\", \"4781\")\n .option(\"-H, --host <host>\", \"Host to bind to (use 0.0.0.0 to expose to network)\", \"127.0.0.1\")\n .option(\"-q, --quiet\", \"Suppress server logs\")\n .option(\"--demo\", \"Seed demo data on startup\")\n .option(\"--no-open\", \"Don't open browser automatically\")\n .option(\"-r, --retention <days>\", \"Auto-delete data older than N days (0 = keep forever)\")\n .action(startCommand);\n\nprogram\n .command(\"status\")\n .description(\"Show collector status, database info, and span count\")\n .action(statusCommand);\n\nprogram\n .command(\"reset\")\n .description(\"Clear all stored data\")\n .action(resetCommand);\n\nprogram\n .command(\"export\")\n .description(\"Export traces as JSON, CSV, or OTLP\")\n .option(\"-o, --output <path>\", \"Output file path\", \"llmtap-export.json\")\n .option(\"-f, --format <format>\", \"Output format (json, csv, or otlp)\", \"json\")\n .option(\"-l, --limit <count>\", \"Number of traces/spans to export\", \"100\")\n .option(\"-e, --endpoint <url>\", \"OTLP endpoint to forward spans to (e.g. http://localhost:4318/v1/traces)\")\n .option(\"-s, --service <name>\", \"service.name for OTLP export\", \"llmtap\")\n .action(exportCommand);\n\nprogram\n .command(\"tail\")\n .description(\"Stream traces to terminal in real-time\")\n .option(\"-f, --format <format>\", \"Output format (pretty or json)\", \"pretty\")\n .action(tailCommand);\n\nprogram\n .command(\"doctor\")\n .description(\"Diagnose common setup issues\")\n .action(doctorCommand);\n\nprogram\n .command(\"stats\")\n .description(\"Show quick terminal stats (cost, models, errors)\")\n .option(\"-p, --period <hours>\", \"Time period in hours\", \"24\")\n .option(\"--host <url>\", \"Collector URL\", \"http://localhost:4781\")\n .action(statsCommand);\n\nprogram.parse();\n","import path from \"node:path\";\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport open from \"open\";\nimport { startServer, getOtlpEndpoint } from \"@llmtap/collector\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ninterface StartOptions {\n port: string;\n host: string;\n quiet?: boolean;\n open?: boolean;\n demo?: boolean;\n retention?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const port = parseInt(options.port, 10);\n const retentionDays = options.retention\n ? parseInt(options.retention, 10)\n : undefined;\n\n // Resolve dashboard dist path\n // 1. Bundled inside CLI dist (for npm publish): dist/dashboard/\n // 2. Monorepo sibling (for local dev): ../dashboard/dist/\n const bundledPath = path.resolve(__dirname, \"..\", \"dashboard\");\n const monorepoPath = path.resolve(__dirname, \"..\", \"..\", \"dashboard\", \"dist\");\n const dashboardPath = fs.existsSync(bundledPath) ? bundledPath : monorepoPath;\n\n console.log(\"\");\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.gray(\" - DevTools for AI Agents\"));\n console.log(\"\");\n\n try {\n const host = options.host;\n\n if (host === \"0.0.0.0\") {\n console.log(chalk.yellow(\" ⚠ Binding to 0.0.0.0 — the collector will be accessible from your network\"));\n console.log(\"\");\n }\n\n const address = await startServer({\n port,\n host,\n dashboardPath,\n quiet: options.quiet,\n demo: options.demo,\n retentionDays,\n });\n\n const url = `http://${host === \"0.0.0.0\" ? \"localhost\" : host}:${port}`;\n\n console.log(chalk.green(\" Ready!\"));\n console.log(\"\");\n console.log(` ${chalk.gray(\"Dashboard:\")} ${chalk.cyan(url)}`);\n console.log(` ${chalk.gray(\"API:\")} ${chalk.cyan(`${url}/v1`)}`);\n console.log(` ${chalk.gray(\"Health:\")} ${chalk.cyan(`${url}/health`)}`);\n if (retentionDays && retentionDays > 0) {\n console.log(` ${chalk.gray(\"Retention:\")} ${chalk.yellow(`${retentionDays} days`)}`);\n }\n const otlpTarget = getOtlpEndpoint();\n if (otlpTarget) {\n console.log(` ${chalk.gray(\"OTLP:\")} ${chalk.cyan(otlpTarget)} ${chalk.green(\"(auto-forwarding)\")}`);\n }\n console.log(\"\");\n console.log(chalk.gray(\" Press Ctrl+C to stop\"));\n console.log(\"\");\n\n // Open browser\n if (options.open !== false) {\n try {\n await open(url);\n } catch {\n // Ignore if browser can't be opened\n }\n }\n } catch (err: unknown) {\n const error = err as Error;\n if (error.message?.includes(\"EADDRINUSE\")) {\n console.error(chalk.red(` Port ${port} is already in use.`));\n console.error(chalk.gray(` Try: npx llmtap --port ${port + 1}`));\n } else {\n console.error(chalk.red(` Failed to start: ${error.message}`));\n }\n process.exit(1);\n }\n}\n","import chalk from \"chalk\";\nimport { resetDb } from \"@llmtap/collector\";\n\nexport async function resetCommand(): Promise<void> {\n try {\n resetDb();\n console.log(chalk.green(\" Data cleared successfully.\"));\n } catch (err: unknown) {\n const error = err as Error;\n console.error(chalk.red(` Failed to reset: ${error.message}`));\n process.exit(1);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { getDb } from \"@llmtap/collector\";\nimport { spansToOtlp } from \"@llmtap/shared\";\nimport type { Span } from \"@llmtap/shared\";\n\ninterface ExportOptions {\n output: string;\n limit: string;\n format: string;\n endpoint?: string;\n service?: string;\n}\n\nfunction safeParse(v: unknown): unknown {\n if (!v || typeof v !== \"string\") return undefined;\n try { return JSON.parse(v); } catch { return undefined; }\n}\n\nfunction rowToSpan(row: Record<string, unknown>): Span {\n return {\n ...row,\n parentSpanId: (row.parentSpanId as string) ?? undefined,\n endTime: (row.endTime as number) ?? undefined,\n duration: (row.duration as number) ?? undefined,\n responseModel: (row.responseModel as string) ?? undefined,\n temperature: (row.temperature as number) ?? undefined,\n maxTokens: (row.maxTokens as number) ?? undefined,\n topP: (row.topP as number) ?? undefined,\n inputMessages: safeParse(row.inputMessages) as Span[\"inputMessages\"],\n outputMessages: safeParse(row.outputMessages) as Span[\"outputMessages\"],\n toolCalls: safeParse(row.toolCalls) as Span[\"toolCalls\"],\n tags: safeParse(row.tags) as Record<string, string> | undefined,\n errorType: (row.errorType as string) ?? undefined,\n errorMessage: (row.errorMessage as string) ?? undefined,\n sessionId: (row.sessionId as string) ?? undefined,\n userId: (row.userId as string) ?? undefined,\n } as Span;\n}\n\nexport async function exportCommand(options: ExportOptions): Promise<void> {\n try {\n const db = getDb();\n const limit = parseInt(options.limit, 10);\n const format = options.format ?? \"json\";\n\n if (format === \"otlp\") {\n return await exportOtlp(db, limit, options);\n }\n\n // Get traces\n const traces = db\n .prepare(\n `\n SELECT\n traceId,\n MIN(name) as name,\n MIN(startTime) as startTime,\n MAX(endTime) as endTime,\n CASE WHEN SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) > 0\n THEN 'error' ELSE 'ok' END as status,\n COUNT(*) as spanCount,\n COALESCE(SUM(totalTokens), 0) as totalTokens,\n COALESCE(SUM(totalCost), 0) as totalCost\n FROM spans\n GROUP BY traceId\n ORDER BY startTime DESC\n LIMIT ?\n `\n )\n .all(limit) as Array<Record<string, unknown>>;\n\n // Get spans for each trace\n const getSpans = db.prepare(\"SELECT * FROM spans WHERE traceId = ? ORDER BY startTime ASC\");\n const exportData = traces.map((trace) => ({\n ...trace,\n spans: (getSpans.all(trace.traceId) as Array<Record<string, unknown>>).map((span) => ({\n ...span,\n inputMessages: safeParse(span.inputMessages),\n outputMessages: safeParse(span.outputMessages),\n toolCalls: safeParse(span.toolCalls),\n tags: safeParse(span.tags),\n })),\n }));\n\n let output: string;\n let ext: string;\n\n if (format === \"csv\") {\n ext = \"csv\";\n const headers = [\n \"traceId\", \"name\", \"status\", \"spanCount\", \"totalTokens\", \"totalCost\",\n \"startTime\", \"endTime\",\n ];\n const rows = traces.map((t) =>\n headers.map((h) => {\n const val = t[h];\n const s = String(val ?? \"\");\n return s.includes(\",\") || s.includes('\"') || s.includes(\"\\n\") ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n }).join(\",\")\n );\n output = [headers.join(\",\"), ...rows].join(\"\\n\");\n } else {\n ext = \"json\";\n output = JSON.stringify(exportData, null, 2);\n }\n\n const defaultOutput = options.output === \"llmtap-export.json\" && format === \"csv\"\n ? \"llmtap-export.csv\"\n : options.output;\n const outputPath = path.resolve(defaultOutput);\n fs.writeFileSync(outputPath, output);\n\n console.log(chalk.green(` Exported ${traces.length} traces as ${ext.toUpperCase()} to ${outputPath}`));\n } catch (err: unknown) {\n const error = err as Error;\n console.error(chalk.red(` Export failed: ${error.message}`));\n process.exit(1);\n }\n}\n\nasync function exportOtlp(\n db: ReturnType<typeof getDb>,\n limit: number,\n options: ExportOptions\n): Promise<void> {\n const rows = db\n .prepare(\"SELECT * FROM spans ORDER BY startTime DESC LIMIT ?\")\n .all(limit) as Array<Record<string, unknown>>;\n\n const spans = rows.map(rowToSpan);\n const otlp = spansToOtlp(spans, options.service ?? \"llmtap\");\n\n // If --endpoint is provided, forward to OTLP collector\n if (options.endpoint) {\n console.log(chalk.blue(` Forwarding ${spans.length} spans to ${options.endpoint}...`));\n try {\n const res = await fetch(options.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(otlp),\n signal: AbortSignal.timeout(30000),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n console.error(chalk.red(` OTLP endpoint returned ${res.status}: ${body.slice(0, 200)}`));\n process.exit(1);\n }\n console.log(chalk.green(` Successfully forwarded ${spans.length} spans to OTLP endpoint`));\n } catch (err) {\n console.error(chalk.red(` Failed to reach OTLP endpoint: ${err instanceof Error ? err.message : String(err)}`));\n process.exit(1);\n }\n return;\n }\n\n // Otherwise write to file\n const outputPath = path.resolve(\n options.output === \"llmtap-export.json\" ? \"llmtap-export.otlp.json\" : options.output\n );\n fs.writeFileSync(outputPath, JSON.stringify(otlp, null, 2));\n console.log(chalk.green(` Exported ${spans.length} spans as OTLP JSON to ${outputPath}`));\n console.log(chalk.dim(` Import into Jaeger, Grafana Tempo, Datadog, or any OTLP-compatible backend`));\n}\n","import chalk from \"chalk\";\r\n\r\nexport async function statusCommand(): Promise<void> {\r\n try {\r\n const res = await fetch(\"http://localhost:4781/v1/db-info\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n\r\n if (!res.ok) {\r\n console.error(chalk.red(\" Collector responded with an error.\"));\r\n process.exit(1);\r\n }\r\n\r\n const info = (await res.json()) as {\r\n path: string;\r\n sizeBytes: number;\r\n spanCount: number;\r\n traceCount: number;\r\n oldestSpan: number | null;\r\n newestSpan: number | null;\r\n walMode: string;\r\n };\r\n\r\n const formatBytes = (bytes: number) => {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\r\n const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\r\n return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;\r\n };\r\n\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.green(\" — Running\"));\r\n console.log(\"\");\r\n console.log(` ${chalk.gray(\"Spans:\")} ${chalk.white(info.spanCount.toLocaleString())}`);\r\n console.log(` ${chalk.gray(\"Traces:\")} ${chalk.white(info.traceCount.toLocaleString())}`);\r\n console.log(` ${chalk.gray(\"DB size:\")} ${chalk.white(formatBytes(info.sizeBytes))}`);\r\n console.log(` ${chalk.gray(\"WAL mode:\")} ${chalk.white(info.walMode.toUpperCase())}`);\r\n console.log(` ${chalk.gray(\"DB path:\")} ${chalk.white(info.path)}`);\r\n\r\n if (info.oldestSpan && info.newestSpan) {\r\n const oldest = new Date(info.oldestSpan).toLocaleString();\r\n const newest = new Date(info.newestSpan).toLocaleString();\r\n console.log(` ${chalk.gray(\"Data range:\")} ${chalk.white(`${oldest} — ${newest}`)}`);\r\n }\r\n console.log(\"\");\r\n } catch {\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.red(\" — Not running\"));\r\n console.log(\"\");\r\n console.log(chalk.gray(\" Start the collector with: \") + chalk.cyan(\"npx llmtap\"));\r\n console.log(\"\");\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\ninterface TailOptions {\r\n format: string;\r\n}\r\n\r\nexport async function tailCommand(options: TailOptions): Promise<void> {\r\n const format = options.format ?? \"pretty\";\r\n const url = \"http://localhost:4781/v1/stream\";\r\n\r\n console.log(\"\");\r\n console.log(\r\n chalk.bold.hex(\"#6366f1\")(\" LLMTap\") +\r\n chalk.gray(\" — Streaming traces in real-time\")\r\n );\r\n console.log(chalk.gray(\" Press Ctrl+C to stop\"));\r\n console.log(\"\");\r\n\r\n try {\r\n const res = await fetch(url, {\r\n headers: { Accept: \"text/event-stream\" },\r\n });\r\n\r\n if (!res.ok || !res.body) {\r\n console.error(chalk.red(\" Could not connect to collector.\"));\r\n console.error(chalk.gray(\" Make sure the collector is running: npx llmtap\"));\r\n process.exit(1);\r\n }\r\n\r\n const decoder = new TextDecoder();\r\n const reader = res.body.getReader();\r\n let buffer = \"\";\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n const lines = buffer.split(\"\\n\");\r\n buffer = lines.pop() ?? \"\";\r\n\r\n for (const line of lines) {\r\n if (!line.startsWith(\"data: \")) continue;\r\n const json = line.slice(6).trim();\r\n if (!json) continue;\r\n\r\n try {\r\n const span = JSON.parse(json) as {\r\n spanId: string;\r\n traceId: string;\r\n name: string;\r\n providerName: string;\r\n requestModel: string;\r\n duration?: number;\r\n totalTokens: number;\r\n totalCost: number;\r\n status: string;\r\n errorMessage?: string;\r\n };\r\n\r\n if (format === \"json\") {\r\n console.log(json);\r\n } else {\r\n const dur = span.duration ? `${span.duration}ms` : \"...\";\r\n const cost =\r\n span.totalCost > 0 ? `$${span.totalCost.toFixed(4)}` : \"$0\";\r\n const statusIcon =\r\n span.status === \"error\" ? chalk.red(\"ERR\") : chalk.green(\"OK \");\r\n\r\n console.log(\r\n ` ${statusIcon} ${chalk.gray(dur.padStart(7))} ${chalk.cyan(span.providerName.padEnd(10))} ${chalk.white(span.requestModel.padEnd(24))} ${chalk.yellow(String(span.totalTokens).padStart(6) + \" tok\")} ${chalk.green(cost.padStart(8))} ${chalk.gray(span.name)}`\r\n );\r\n if (span.errorMessage) {\r\n console.log(\r\n ` ${chalk.red(\"→ \" + span.errorMessage.slice(0, 120))}`\r\n );\r\n }\r\n }\r\n } catch {\r\n // Skip malformed events\r\n }\r\n }\r\n }\r\n } catch {\r\n console.error(chalk.red(\" Could not connect to collector.\"));\r\n console.error(chalk.gray(\" Make sure the collector is running: npx llmtap\"));\r\n process.exit(1);\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\nexport async function doctorCommand(): Promise<void> {\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap Doctor\"));\r\n console.log(chalk.gray(\" Checking your setup...\"));\r\n console.log(\"\");\r\n\r\n const checks: { label: string; status: \"ok\" | \"warn\" | \"fail\"; detail: string }[] = [];\r\n\r\n // Check 1: Node.js version\r\n const nodeVersion = process.version;\r\n const major = parseInt(nodeVersion.slice(1), 10);\r\n if (major >= 18) {\r\n checks.push({ label: \"Node.js version\", status: \"ok\", detail: nodeVersion });\r\n } else {\r\n checks.push({ label: \"Node.js version\", status: \"fail\", detail: `${nodeVersion} (requires >= 18)` });\r\n }\r\n\r\n // Check 2: Collector running\r\n try {\r\n const res = await fetch(\"http://localhost:4781/health\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (res.ok) {\r\n checks.push({ label: \"Collector\", status: \"ok\", detail: \"Running on port 4781\" });\r\n } else {\r\n checks.push({ label: \"Collector\", status: \"fail\", detail: `Responded with HTTP ${res.status}` });\r\n }\r\n } catch {\r\n checks.push({ label: \"Collector\", status: \"warn\", detail: \"Not running (start with: npx llmtap)\" });\r\n }\r\n\r\n // Check 3: Database accessible\r\n try {\r\n const res = await fetch(\"http://localhost:4781/v1/db-info\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (res.ok) {\r\n const info = (await res.json()) as { sizeBytes: number; spanCount: number; walMode: string };\r\n checks.push({\r\n label: \"Database\",\r\n status: info.walMode === \"wal\" ? \"ok\" : \"warn\",\r\n detail: `${info.spanCount} spans, WAL=${info.walMode.toUpperCase()}`,\r\n });\r\n }\r\n } catch {\r\n checks.push({ label: \"Database\", status: \"warn\", detail: \"Cannot check (collector not running)\" });\r\n }\r\n\r\n // Check 4: SDK installed\r\n try {\r\n await import(\"@llmtap/sdk\");\r\n checks.push({ label: \"@llmtap/sdk\", status: \"ok\", detail: \"Installed\" });\r\n } catch {\r\n checks.push({ label: \"@llmtap/sdk\", status: \"warn\", detail: \"Not found in current project (install with: npm i @llmtap/sdk)\" });\r\n }\r\n\r\n // Check 5: Port available\r\n if (checks.find((c) => c.label === \"Collector\")?.status !== \"ok\") {\r\n try {\r\n // Try connecting to port 4781 to see if something else is using it\r\n const res = await fetch(\"http://localhost:4781\", {\r\n signal: AbortSignal.timeout(1000),\r\n });\r\n if (!res.ok) {\r\n checks.push({ label: \"Port 4781\", status: \"warn\", detail: \"Something is running but not LLMTap\" });\r\n }\r\n } catch {\r\n checks.push({ label: \"Port 4781\", status: \"ok\", detail: \"Available\" });\r\n }\r\n }\r\n\r\n // Display results\r\n for (const check of checks) {\r\n const icon =\r\n check.status === \"ok\"\r\n ? chalk.green(\" ✓\")\r\n : check.status === \"warn\"\r\n ? chalk.yellow(\" !\")\r\n : chalk.red(\" ✗\");\r\n const label = chalk.white(check.label.padEnd(20));\r\n const detail =\r\n check.status === \"ok\"\r\n ? chalk.gray(check.detail)\r\n : check.status === \"warn\"\r\n ? chalk.yellow(check.detail)\r\n : chalk.red(check.detail);\r\n console.log(`${icon} ${label} ${detail}`);\r\n }\r\n\r\n const failCount = checks.filter((c) => c.status === \"fail\").length;\r\n const warnCount = checks.filter((c) => c.status === \"warn\").length;\r\n console.log(\"\");\r\n if (failCount > 0) {\r\n console.log(chalk.red(` ${failCount} issue(s) found. Fix them to use LLMTap.`));\r\n } else if (warnCount > 0) {\r\n console.log(chalk.yellow(` ${warnCount} warning(s). Everything should still work.`));\r\n } else {\r\n console.log(chalk.green(\" All checks passed!\"));\r\n }\r\n console.log(\"\");\r\n}\r\n","import chalk from \"chalk\";\r\n\r\ninterface StatsResponse {\r\n totalTraces: number;\r\n totalSpans: number;\r\n totalTokens: number;\r\n totalCost: number;\r\n avgDuration: number;\r\n errorCount: number;\r\n errorRate: number;\r\n byProvider: { provider: string; spanCount: number; totalTokens: number; totalCost: number; avgDuration: number }[];\r\n byModel: { model: string; provider: string; spanCount: number; totalTokens: number; totalCost: number; avgDuration: number }[];\r\n}\r\n\r\nexport async function statsCommand(options: { period?: string; host?: string }): Promise<void> {\r\n const period = Number(options.period ?? \"24\");\r\n const host = options.host ?? \"http://localhost:4781\";\r\n\r\n try {\r\n const res = await fetch(`${host}/v1/stats?period=${period}`);\r\n if (!res.ok) {\r\n console.error(chalk.red(`Error: Collector returned HTTP ${res.status}`));\r\n console.error(chalk.dim(\"Is the collector running? Try: npx llmtap start\"));\r\n process.exit(1);\r\n }\r\n\r\n const stats = (await res.json()) as StatsResponse;\r\n\r\n console.log(\"\");\r\n console.log(chalk.bold.white(` LLMTap Stats — Last ${period}h`));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n console.log(\"\");\r\n\r\n // Summary\r\n const errorPct = (stats.errorRate * 100).toFixed(1);\r\n console.log(` ${chalk.dim(\"Traces\")} ${chalk.bold.white(String(stats.totalTraces))}`);\r\n console.log(` ${chalk.dim(\"Spans\")} ${chalk.bold.white(String(stats.totalSpans))}`);\r\n console.log(` ${chalk.dim(\"Tokens\")} ${chalk.bold.white(stats.totalTokens.toLocaleString())}`);\r\n console.log(` ${chalk.dim(\"Total Cost\")} ${chalk.bold.green(\"$\" + stats.totalCost.toFixed(4))}`);\r\n console.log(` ${chalk.dim(\"Avg Latency\")} ${chalk.white(formatMs(stats.avgDuration))}`);\r\n console.log(\r\n ` ${chalk.dim(\"Error Rate\")} ${\r\n stats.errorRate > 0.05\r\n ? chalk.bold.red(errorPct + \"%\")\r\n : chalk.green(errorPct + \"%\")\r\n } ${chalk.dim(`(${stats.errorCount} errors)`)}`\r\n );\r\n\r\n // Top providers\r\n if (stats.byProvider.length > 0) {\r\n console.log(\"\");\r\n console.log(chalk.bold.white(\" Top Providers\"));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n for (const p of stats.byProvider.slice(0, 5)) {\r\n const bar = makeBar(p.totalCost, stats.totalCost, 20);\r\n console.log(\r\n ` ${chalk.cyan(p.provider.padEnd(12))} ${chalk.dim(String(p.spanCount).padStart(5) + \" calls\")} ${chalk.green(\"$\" + p.totalCost.toFixed(4).padStart(8))} ${chalk.dim(bar)}`\r\n );\r\n }\r\n }\r\n\r\n // Top models\r\n if (stats.byModel.length > 0) {\r\n console.log(\"\");\r\n console.log(chalk.bold.white(\" Top Models\"));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n for (const m of stats.byModel.slice(0, 8)) {\r\n const bar = makeBar(m.totalCost, stats.totalCost, 20);\r\n console.log(\r\n ` ${chalk.white(m.model.padEnd(28).slice(0, 28))} ${chalk.dim(String(m.spanCount).padStart(5) + \" calls\")} ${chalk.green(\"$\" + m.totalCost.toFixed(4).padStart(8))} ${chalk.dim(bar)}`\r\n );\r\n }\r\n }\r\n\r\n console.log(\"\");\r\n } catch (err) {\r\n if (err instanceof TypeError && (err as NodeJS.ErrnoException).cause) {\r\n console.error(chalk.red(\"Error: Cannot connect to collector\"));\r\n console.error(chalk.dim(\"Is the collector running? Try: npx llmtap start\"));\r\n } else {\r\n console.error(chalk.red(\"Error:\"), err instanceof Error ? err.message : err);\r\n }\r\n process.exit(1);\r\n }\r\n}\r\n\r\nfunction formatMs(ms: number): string {\r\n if (ms < 1000) return `${Math.round(ms)}ms`;\r\n return `${(ms / 1000).toFixed(2)}s`;\r\n}\r\n\r\nfunction makeBar(value: number, total: number, width: number): string {\r\n if (total <= 0) return \"\";\r\n const filled = Math.max(Math.round((value / total) * width), 1);\r\n return \"█\".repeat(filled) + \"░\".repeat(width - filled);\r\n}\r\n"],"mappings":"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","names":["chalk","fs","path","chalk","chalk","chalk","chalk","chalk"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/commands/reset.ts","../src/commands/export.ts","../src/commands/status.ts","../src/commands/tail.ts","../src/commands/doctor.ts","../src/commands/stats.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { startCommand } from \"./commands/start.js\";\nimport { resetCommand } from \"./commands/reset.js\";\nimport { exportCommand } from \"./commands/export.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { tailCommand } from \"./commands/tail.js\";\nimport { doctorCommand } from \"./commands/doctor.js\";\nimport { statsCommand } from \"./commands/stats.js\";\nimport { VERSION } from \"@llmtap/shared\";\n\nconst program = new Command();\n\nprogram\n .name(\"llmtap\")\n .description(\"DevTools for AI Agents - See every LLM call, trace agent workflows, track costs\")\n .version(VERSION);\n\nprogram\n .command(\"start\", { isDefault: true })\n .description(\"Start the LLMTap collector and dashboard\")\n .option(\"-p, --port <port>\", \"Port number\", \"4781\")\n .option(\"-H, --host <host>\", \"Host to bind to (use 0.0.0.0 to expose to network)\", \"127.0.0.1\")\n .option(\"-q, --quiet\", \"Suppress server logs\")\n .option(\"--demo\", \"Seed demo data on startup\")\n .option(\"--no-open\", \"Don't open browser automatically\")\n .option(\"-r, --retention <days>\", \"Auto-delete data older than N days (0 = keep forever)\")\n .action(startCommand);\n\nprogram\n .command(\"status\")\n .description(\"Show collector status, database info, and span count\")\n .action(statusCommand);\n\nprogram\n .command(\"reset\")\n .description(\"Clear all stored data\")\n .action(resetCommand);\n\nprogram\n .command(\"export\")\n .description(\"Export traces as JSON, CSV, or OTLP\")\n .option(\"-o, --output <path>\", \"Output file path\", \"llmtap-export.json\")\n .option(\"-f, --format <format>\", \"Output format (json, csv, or otlp)\", \"json\")\n .option(\"-l, --limit <count>\", \"Number of traces/spans to export\", \"100\")\n .option(\"-e, --endpoint <url>\", \"OTLP endpoint to forward spans to (e.g. http://localhost:4318/v1/traces)\")\n .option(\"-s, --service <name>\", \"service.name for OTLP export\", \"llmtap\")\n .action(exportCommand);\n\nprogram\n .command(\"tail\")\n .description(\"Stream traces to terminal in real-time\")\n .option(\"-f, --format <format>\", \"Output format (pretty or json)\", \"pretty\")\n .action(tailCommand);\n\nprogram\n .command(\"doctor\")\n .description(\"Diagnose common setup issues\")\n .action(doctorCommand);\n\nprogram\n .command(\"stats\")\n .description(\"Show quick terminal stats (cost, models, errors)\")\n .option(\"-p, --period <hours>\", \"Time period in hours\", \"24\")\n .option(\"--host <url>\", \"Collector URL\", \"http://localhost:4781\")\n .action(statsCommand);\n\nprogram.parse();\n","import path from \"node:path\";\nimport fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport open from \"open\";\nimport { startServer, getOtlpEndpoint } from \"@llmtap/collector\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\ninterface StartOptions {\n port: string;\n host: string;\n quiet?: boolean;\n open?: boolean;\n demo?: boolean;\n retention?: string;\n}\n\nexport async function startCommand(options: StartOptions): Promise<void> {\n const port = parseInt(options.port, 10);\n const retentionDays = options.retention\n ? parseInt(options.retention, 10)\n : undefined;\n\n // Resolve dashboard dist path\n // 1. Bundled inside CLI dist (for npm publish): dist/dashboard/\n // 2. Monorepo sibling (for local dev): ../../dashboard/dist/\n const bundledPath = path.resolve(__dirname, \"dashboard\");\n const monorepoPath = path.resolve(__dirname, \"..\", \"..\", \"dashboard\", \"dist\");\n const dashboardPath = fs.existsSync(bundledPath) ? bundledPath : monorepoPath;\n\n console.log(\"\");\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.gray(\" - DevTools for AI Agents\"));\n console.log(\"\");\n\n try {\n const host = options.host;\n\n if (host === \"0.0.0.0\") {\n console.log(chalk.yellow(\" ⚠ Binding to 0.0.0.0 — the collector will be accessible from your network\"));\n console.log(\"\");\n }\n\n const address = await startServer({\n port,\n host,\n dashboardPath,\n quiet: options.quiet,\n demo: options.demo,\n retentionDays,\n });\n\n const url = `http://${host === \"0.0.0.0\" ? \"localhost\" : host}:${port}`;\n\n console.log(chalk.green(\" Ready!\"));\n console.log(\"\");\n console.log(` ${chalk.gray(\"Dashboard:\")} ${chalk.cyan(url)}`);\n console.log(` ${chalk.gray(\"API:\")} ${chalk.cyan(`${url}/v1`)}`);\n console.log(` ${chalk.gray(\"Health:\")} ${chalk.cyan(`${url}/health`)}`);\n if (retentionDays && retentionDays > 0) {\n console.log(` ${chalk.gray(\"Retention:\")} ${chalk.yellow(`${retentionDays} days`)}`);\n }\n const otlpTarget = getOtlpEndpoint();\n if (otlpTarget) {\n console.log(` ${chalk.gray(\"OTLP:\")} ${chalk.cyan(otlpTarget)} ${chalk.green(\"(auto-forwarding)\")}`);\n }\n console.log(\"\");\n console.log(chalk.gray(\" Press Ctrl+C to stop\"));\n console.log(\"\");\n\n // Open browser\n if (options.open !== false) {\n try {\n await open(url);\n } catch {\n // Ignore if browser can't be opened\n }\n }\n } catch (err: unknown) {\n const error = err as Error;\n if (error.message?.includes(\"EADDRINUSE\")) {\n console.error(chalk.red(` Port ${port} is already in use.`));\n console.error(chalk.gray(` Try: npx llmtap --port ${port + 1}`));\n } else {\n console.error(chalk.red(` Failed to start: ${error.message}`));\n }\n process.exit(1);\n }\n}\n","import chalk from \"chalk\";\nimport { resetDb } from \"@llmtap/collector\";\n\nexport async function resetCommand(): Promise<void> {\n try {\n resetDb();\n console.log(chalk.green(\" Data cleared successfully.\"));\n } catch (err: unknown) {\n const error = err as Error;\n console.error(chalk.red(` Failed to reset: ${error.message}`));\n process.exit(1);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport { getDb } from \"@llmtap/collector\";\nimport { spansToOtlp } from \"@llmtap/shared\";\nimport type { Span } from \"@llmtap/shared\";\n\ninterface ExportOptions {\n output: string;\n limit: string;\n format: string;\n endpoint?: string;\n service?: string;\n}\n\nfunction safeParse(v: unknown): unknown {\n if (!v || typeof v !== \"string\") return undefined;\n try { return JSON.parse(v); } catch { return undefined; }\n}\n\nfunction rowToSpan(row: Record<string, unknown>): Span {\n return {\n ...row,\n parentSpanId: (row.parentSpanId as string) ?? undefined,\n endTime: (row.endTime as number) ?? undefined,\n duration: (row.duration as number) ?? undefined,\n responseModel: (row.responseModel as string) ?? undefined,\n temperature: (row.temperature as number) ?? undefined,\n maxTokens: (row.maxTokens as number) ?? undefined,\n topP: (row.topP as number) ?? undefined,\n inputMessages: safeParse(row.inputMessages) as Span[\"inputMessages\"],\n outputMessages: safeParse(row.outputMessages) as Span[\"outputMessages\"],\n toolCalls: safeParse(row.toolCalls) as Span[\"toolCalls\"],\n tags: safeParse(row.tags) as Record<string, string> | undefined,\n errorType: (row.errorType as string) ?? undefined,\n errorMessage: (row.errorMessage as string) ?? undefined,\n sessionId: (row.sessionId as string) ?? undefined,\n userId: (row.userId as string) ?? undefined,\n } as Span;\n}\n\nexport async function exportCommand(options: ExportOptions): Promise<void> {\n try {\n const db = getDb();\n const limit = parseInt(options.limit, 10);\n const format = options.format ?? \"json\";\n\n if (format === \"otlp\") {\n return await exportOtlp(db, limit, options);\n }\n\n // Get traces\n const traces = db\n .prepare(\n `\n SELECT\n traceId,\n MIN(name) as name,\n MIN(startTime) as startTime,\n MAX(endTime) as endTime,\n CASE WHEN SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) > 0\n THEN 'error' ELSE 'ok' END as status,\n COUNT(*) as spanCount,\n COALESCE(SUM(totalTokens), 0) as totalTokens,\n COALESCE(SUM(totalCost), 0) as totalCost\n FROM spans\n GROUP BY traceId\n ORDER BY startTime DESC\n LIMIT ?\n `\n )\n .all(limit) as Array<Record<string, unknown>>;\n\n // Get spans for each trace\n const getSpans = db.prepare(\"SELECT * FROM spans WHERE traceId = ? ORDER BY startTime ASC\");\n const exportData = traces.map((trace) => ({\n ...trace,\n spans: (getSpans.all(trace.traceId) as Array<Record<string, unknown>>).map((span) => ({\n ...span,\n inputMessages: safeParse(span.inputMessages),\n outputMessages: safeParse(span.outputMessages),\n toolCalls: safeParse(span.toolCalls),\n tags: safeParse(span.tags),\n })),\n }));\n\n let output: string;\n let ext: string;\n\n if (format === \"csv\") {\n ext = \"csv\";\n const headers = [\n \"traceId\", \"name\", \"status\", \"spanCount\", \"totalTokens\", \"totalCost\",\n \"startTime\", \"endTime\",\n ];\n const rows = traces.map((t) =>\n headers.map((h) => {\n const val = t[h];\n const s = String(val ?? \"\");\n return s.includes(\",\") || s.includes('\"') || s.includes(\"\\n\") ? `\"${s.replace(/\"/g, '\"\"')}\"` : s;\n }).join(\",\")\n );\n output = [headers.join(\",\"), ...rows].join(\"\\n\");\n } else {\n ext = \"json\";\n output = JSON.stringify(exportData, null, 2);\n }\n\n const defaultOutput = options.output === \"llmtap-export.json\" && format === \"csv\"\n ? \"llmtap-export.csv\"\n : options.output;\n const outputPath = path.resolve(defaultOutput);\n fs.writeFileSync(outputPath, output);\n\n console.log(chalk.green(` Exported ${traces.length} traces as ${ext.toUpperCase()} to ${outputPath}`));\n } catch (err: unknown) {\n const error = err as Error;\n console.error(chalk.red(` Export failed: ${error.message}`));\n process.exit(1);\n }\n}\n\nasync function exportOtlp(\n db: ReturnType<typeof getDb>,\n limit: number,\n options: ExportOptions\n): Promise<void> {\n const rows = db\n .prepare(\"SELECT * FROM spans ORDER BY startTime DESC LIMIT ?\")\n .all(limit) as Array<Record<string, unknown>>;\n\n const spans = rows.map(rowToSpan);\n const otlp = spansToOtlp(spans, options.service ?? \"llmtap\");\n\n // If --endpoint is provided, forward to OTLP collector\n if (options.endpoint) {\n console.log(chalk.blue(` Forwarding ${spans.length} spans to ${options.endpoint}...`));\n try {\n const res = await fetch(options.endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(otlp),\n signal: AbortSignal.timeout(30000),\n });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n console.error(chalk.red(` OTLP endpoint returned ${res.status}: ${body.slice(0, 200)}`));\n process.exit(1);\n }\n console.log(chalk.green(` Successfully forwarded ${spans.length} spans to OTLP endpoint`));\n } catch (err) {\n console.error(chalk.red(` Failed to reach OTLP endpoint: ${err instanceof Error ? err.message : String(err)}`));\n process.exit(1);\n }\n return;\n }\n\n // Otherwise write to file\n const outputPath = path.resolve(\n options.output === \"llmtap-export.json\" ? \"llmtap-export.otlp.json\" : options.output\n );\n fs.writeFileSync(outputPath, JSON.stringify(otlp, null, 2));\n console.log(chalk.green(` Exported ${spans.length} spans as OTLP JSON to ${outputPath}`));\n console.log(chalk.dim(` Import into Jaeger, Grafana Tempo, Datadog, or any OTLP-compatible backend`));\n}\n","import chalk from \"chalk\";\r\n\r\nexport async function statusCommand(): Promise<void> {\r\n try {\r\n const res = await fetch(\"http://localhost:4781/v1/db-info\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n\r\n if (!res.ok) {\r\n console.error(chalk.red(\" Collector responded with an error.\"));\r\n process.exit(1);\r\n }\r\n\r\n const info = (await res.json()) as {\r\n path: string;\r\n sizeBytes: number;\r\n spanCount: number;\r\n traceCount: number;\r\n oldestSpan: number | null;\r\n newestSpan: number | null;\r\n walMode: string;\r\n };\r\n\r\n const formatBytes = (bytes: number) => {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\"];\r\n const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);\r\n return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;\r\n };\r\n\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.green(\" — Running\"));\r\n console.log(\"\");\r\n console.log(` ${chalk.gray(\"Spans:\")} ${chalk.white(info.spanCount.toLocaleString())}`);\r\n console.log(` ${chalk.gray(\"Traces:\")} ${chalk.white(info.traceCount.toLocaleString())}`);\r\n console.log(` ${chalk.gray(\"DB size:\")} ${chalk.white(formatBytes(info.sizeBytes))}`);\r\n console.log(` ${chalk.gray(\"WAL mode:\")} ${chalk.white(info.walMode.toUpperCase())}`);\r\n console.log(` ${chalk.gray(\"DB path:\")} ${chalk.white(info.path)}`);\r\n\r\n if (info.oldestSpan && info.newestSpan) {\r\n const oldest = new Date(info.oldestSpan).toLocaleString();\r\n const newest = new Date(info.newestSpan).toLocaleString();\r\n console.log(` ${chalk.gray(\"Data range:\")} ${chalk.white(`${oldest} — ${newest}`)}`);\r\n }\r\n console.log(\"\");\r\n } catch {\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap\") + chalk.red(\" — Not running\"));\r\n console.log(\"\");\r\n console.log(chalk.gray(\" Start the collector with: \") + chalk.cyan(\"npx llmtap\"));\r\n console.log(\"\");\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\ninterface TailOptions {\r\n format: string;\r\n}\r\n\r\nexport async function tailCommand(options: TailOptions): Promise<void> {\r\n const format = options.format ?? \"pretty\";\r\n const url = \"http://localhost:4781/v1/stream\";\r\n\r\n console.log(\"\");\r\n console.log(\r\n chalk.bold.hex(\"#6366f1\")(\" LLMTap\") +\r\n chalk.gray(\" — Streaming traces in real-time\")\r\n );\r\n console.log(chalk.gray(\" Press Ctrl+C to stop\"));\r\n console.log(\"\");\r\n\r\n try {\r\n const res = await fetch(url, {\r\n headers: { Accept: \"text/event-stream\" },\r\n });\r\n\r\n if (!res.ok || !res.body) {\r\n console.error(chalk.red(\" Could not connect to collector.\"));\r\n console.error(chalk.gray(\" Make sure the collector is running: npx llmtap\"));\r\n process.exit(1);\r\n }\r\n\r\n const decoder = new TextDecoder();\r\n const reader = res.body.getReader();\r\n let buffer = \"\";\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n const lines = buffer.split(\"\\n\");\r\n buffer = lines.pop() ?? \"\";\r\n\r\n for (const line of lines) {\r\n if (!line.startsWith(\"data: \")) continue;\r\n const json = line.slice(6).trim();\r\n if (!json) continue;\r\n\r\n try {\r\n const span = JSON.parse(json) as {\r\n spanId: string;\r\n traceId: string;\r\n name: string;\r\n providerName: string;\r\n requestModel: string;\r\n duration?: number;\r\n totalTokens: number;\r\n totalCost: number;\r\n status: string;\r\n errorMessage?: string;\r\n };\r\n\r\n if (format === \"json\") {\r\n console.log(json);\r\n } else {\r\n const dur = span.duration ? `${span.duration}ms` : \"...\";\r\n const cost =\r\n span.totalCost > 0 ? `$${span.totalCost.toFixed(4)}` : \"$0\";\r\n const statusIcon =\r\n span.status === \"error\" ? chalk.red(\"ERR\") : chalk.green(\"OK \");\r\n\r\n console.log(\r\n ` ${statusIcon} ${chalk.gray(dur.padStart(7))} ${chalk.cyan(span.providerName.padEnd(10))} ${chalk.white(span.requestModel.padEnd(24))} ${chalk.yellow(String(span.totalTokens).padStart(6) + \" tok\")} ${chalk.green(cost.padStart(8))} ${chalk.gray(span.name)}`\r\n );\r\n if (span.errorMessage) {\r\n console.log(\r\n ` ${chalk.red(\"→ \" + span.errorMessage.slice(0, 120))}`\r\n );\r\n }\r\n }\r\n } catch {\r\n // Skip malformed events\r\n }\r\n }\r\n }\r\n } catch {\r\n console.error(chalk.red(\" Could not connect to collector.\"));\r\n console.error(chalk.gray(\" Make sure the collector is running: npx llmtap\"));\r\n process.exit(1);\r\n }\r\n}\r\n","import chalk from \"chalk\";\r\n\r\nexport async function doctorCommand(): Promise<void> {\r\n console.log(\"\");\r\n console.log(chalk.bold.hex(\"#6366f1\")(\" LLMTap Doctor\"));\r\n console.log(chalk.gray(\" Checking your setup...\"));\r\n console.log(\"\");\r\n\r\n const checks: { label: string; status: \"ok\" | \"warn\" | \"fail\"; detail: string }[] = [];\r\n\r\n // Check 1: Node.js version\r\n const nodeVersion = process.version;\r\n const major = parseInt(nodeVersion.slice(1), 10);\r\n if (major >= 18) {\r\n checks.push({ label: \"Node.js version\", status: \"ok\", detail: nodeVersion });\r\n } else {\r\n checks.push({ label: \"Node.js version\", status: \"fail\", detail: `${nodeVersion} (requires >= 18)` });\r\n }\r\n\r\n // Check 2: Collector running\r\n try {\r\n const res = await fetch(\"http://localhost:4781/health\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (res.ok) {\r\n checks.push({ label: \"Collector\", status: \"ok\", detail: \"Running on port 4781\" });\r\n } else {\r\n checks.push({ label: \"Collector\", status: \"fail\", detail: `Responded with HTTP ${res.status}` });\r\n }\r\n } catch {\r\n checks.push({ label: \"Collector\", status: \"warn\", detail: \"Not running (start with: npx llmtap)\" });\r\n }\r\n\r\n // Check 3: Database accessible\r\n try {\r\n const res = await fetch(\"http://localhost:4781/v1/db-info\", {\r\n signal: AbortSignal.timeout(3000),\r\n });\r\n if (res.ok) {\r\n const info = (await res.json()) as { sizeBytes: number; spanCount: number; walMode: string };\r\n checks.push({\r\n label: \"Database\",\r\n status: info.walMode === \"wal\" ? \"ok\" : \"warn\",\r\n detail: `${info.spanCount} spans, WAL=${info.walMode.toUpperCase()}`,\r\n });\r\n }\r\n } catch {\r\n checks.push({ label: \"Database\", status: \"warn\", detail: \"Cannot check (collector not running)\" });\r\n }\r\n\r\n // Check 4: SDK installed\r\n try {\r\n await import(\"@llmtap/sdk\");\r\n checks.push({ label: \"@llmtap/sdk\", status: \"ok\", detail: \"Installed\" });\r\n } catch {\r\n checks.push({ label: \"@llmtap/sdk\", status: \"warn\", detail: \"Not found in current project (install with: npm i @llmtap/sdk)\" });\r\n }\r\n\r\n // Check 5: Port available\r\n if (checks.find((c) => c.label === \"Collector\")?.status !== \"ok\") {\r\n try {\r\n // Try connecting to port 4781 to see if something else is using it\r\n const res = await fetch(\"http://localhost:4781\", {\r\n signal: AbortSignal.timeout(1000),\r\n });\r\n if (!res.ok) {\r\n checks.push({ label: \"Port 4781\", status: \"warn\", detail: \"Something is running but not LLMTap\" });\r\n }\r\n } catch {\r\n checks.push({ label: \"Port 4781\", status: \"ok\", detail: \"Available\" });\r\n }\r\n }\r\n\r\n // Display results\r\n for (const check of checks) {\r\n const icon =\r\n check.status === \"ok\"\r\n ? chalk.green(\" ✓\")\r\n : check.status === \"warn\"\r\n ? chalk.yellow(\" !\")\r\n : chalk.red(\" ✗\");\r\n const label = chalk.white(check.label.padEnd(20));\r\n const detail =\r\n check.status === \"ok\"\r\n ? chalk.gray(check.detail)\r\n : check.status === \"warn\"\r\n ? chalk.yellow(check.detail)\r\n : chalk.red(check.detail);\r\n console.log(`${icon} ${label} ${detail}`);\r\n }\r\n\r\n const failCount = checks.filter((c) => c.status === \"fail\").length;\r\n const warnCount = checks.filter((c) => c.status === \"warn\").length;\r\n console.log(\"\");\r\n if (failCount > 0) {\r\n console.log(chalk.red(` ${failCount} issue(s) found. Fix them to use LLMTap.`));\r\n } else if (warnCount > 0) {\r\n console.log(chalk.yellow(` ${warnCount} warning(s). Everything should still work.`));\r\n } else {\r\n console.log(chalk.green(\" All checks passed!\"));\r\n }\r\n console.log(\"\");\r\n}\r\n","import chalk from \"chalk\";\r\n\r\ninterface StatsResponse {\r\n totalTraces: number;\r\n totalSpans: number;\r\n totalTokens: number;\r\n totalCost: number;\r\n avgDuration: number;\r\n errorCount: number;\r\n errorRate: number;\r\n byProvider: { provider: string; spanCount: number; totalTokens: number; totalCost: number; avgDuration: number }[];\r\n byModel: { model: string; provider: string; spanCount: number; totalTokens: number; totalCost: number; avgDuration: number }[];\r\n}\r\n\r\nexport async function statsCommand(options: { period?: string; host?: string }): Promise<void> {\r\n const period = Number(options.period ?? \"24\");\r\n const host = options.host ?? \"http://localhost:4781\";\r\n\r\n try {\r\n const res = await fetch(`${host}/v1/stats?period=${period}`);\r\n if (!res.ok) {\r\n console.error(chalk.red(`Error: Collector returned HTTP ${res.status}`));\r\n console.error(chalk.dim(\"Is the collector running? Try: npx llmtap start\"));\r\n process.exit(1);\r\n }\r\n\r\n const stats = (await res.json()) as StatsResponse;\r\n\r\n console.log(\"\");\r\n console.log(chalk.bold.white(` LLMTap Stats — Last ${period}h`));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n console.log(\"\");\r\n\r\n // Summary\r\n const errorPct = (stats.errorRate * 100).toFixed(1);\r\n console.log(` ${chalk.dim(\"Traces\")} ${chalk.bold.white(String(stats.totalTraces))}`);\r\n console.log(` ${chalk.dim(\"Spans\")} ${chalk.bold.white(String(stats.totalSpans))}`);\r\n console.log(` ${chalk.dim(\"Tokens\")} ${chalk.bold.white(stats.totalTokens.toLocaleString())}`);\r\n console.log(` ${chalk.dim(\"Total Cost\")} ${chalk.bold.green(\"$\" + stats.totalCost.toFixed(4))}`);\r\n console.log(` ${chalk.dim(\"Avg Latency\")} ${chalk.white(formatMs(stats.avgDuration))}`);\r\n console.log(\r\n ` ${chalk.dim(\"Error Rate\")} ${\r\n stats.errorRate > 0.05\r\n ? chalk.bold.red(errorPct + \"%\")\r\n : chalk.green(errorPct + \"%\")\r\n } ${chalk.dim(`(${stats.errorCount} errors)`)}`\r\n );\r\n\r\n // Top providers\r\n if (stats.byProvider.length > 0) {\r\n console.log(\"\");\r\n console.log(chalk.bold.white(\" Top Providers\"));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n for (const p of stats.byProvider.slice(0, 5)) {\r\n const bar = makeBar(p.totalCost, stats.totalCost, 20);\r\n console.log(\r\n ` ${chalk.cyan(p.provider.padEnd(12))} ${chalk.dim(String(p.spanCount).padStart(5) + \" calls\")} ${chalk.green(\"$\" + p.totalCost.toFixed(4).padStart(8))} ${chalk.dim(bar)}`\r\n );\r\n }\r\n }\r\n\r\n // Top models\r\n if (stats.byModel.length > 0) {\r\n console.log(\"\");\r\n console.log(chalk.bold.white(\" Top Models\"));\r\n console.log(chalk.dim(\" ─────────────────────────────────\"));\r\n for (const m of stats.byModel.slice(0, 8)) {\r\n const bar = makeBar(m.totalCost, stats.totalCost, 20);\r\n console.log(\r\n ` ${chalk.white(m.model.padEnd(28).slice(0, 28))} ${chalk.dim(String(m.spanCount).padStart(5) + \" calls\")} ${chalk.green(\"$\" + m.totalCost.toFixed(4).padStart(8))} ${chalk.dim(bar)}`\r\n );\r\n }\r\n }\r\n\r\n console.log(\"\");\r\n } catch (err) {\r\n if (err instanceof TypeError && (err as NodeJS.ErrnoException).cause) {\r\n console.error(chalk.red(\"Error: Cannot connect to collector\"));\r\n console.error(chalk.dim(\"Is the collector running? Try: npx llmtap start\"));\r\n } else {\r\n console.error(chalk.red(\"Error:\"), err instanceof Error ? err.message : err);\r\n }\r\n process.exit(1);\r\n }\r\n}\r\n\r\nfunction formatMs(ms: number): string {\r\n if (ms < 1000) return `${Math.round(ms)}ms`;\r\n return `${(ms / 1000).toFixed(2)}s`;\r\n}\r\n\r\nfunction makeBar(value: number, total: number, width: number): string {\r\n if (total <= 0) return \"\";\r\n const filled = Math.max(Math.round((value / total) * width), 1);\r\n return \"█\".repeat(filled) + \"░\".repeat(width - filled);\r\n}\r\n"],"mappings":"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","names":["chalk","fs","path","chalk","chalk","chalk","chalk","chalk"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmtap",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "DevTools for AI Agents - See every LLM call, trace agent workflows, track costs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,8 +13,8 @@
13
13
  "chalk": "^5.4.0",
14
14
  "commander": "^13.0.0",
15
15
  "open": "^10.1.0",
16
- "@llmtap/collector": "0.1.0",
17
- "@llmtap/shared": "0.1.0"
16
+ "@llmtap/collector": "0.1.1",
17
+ "@llmtap/shared": "0.1.1"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/node": "^25.4.0",
@@ -38,7 +38,7 @@
38
38
  "license": "MIT",
39
39
  "repository": {
40
40
  "type": "git",
41
- "url": "https://github.com/llmtap/llmtap",
41
+ "url": "https://github.com/DivyaanshuXD/LLMTap",
42
42
  "directory": "packages/cli"
43
43
  },
44
44
  "scripts": {