@orka-js/devtools 1.2.1 → 1.3.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/dist/index.cjs CHANGED
@@ -343,11 +343,16 @@ function resetCollector() {
343
343
  }
344
344
 
345
345
  // src/server.ts
346
+ var import_fs = require("fs");
347
+ var import_url = require("url");
348
+ var import_path = require("path");
349
+ var import_meta = {};
346
350
  var DevToolsServer = class {
347
351
  collector;
348
352
  config;
349
353
  server;
350
354
  clients = /* @__PURE__ */ new Set();
355
+ dashboardHTML;
351
356
  constructor(collector, config = {}) {
352
357
  this.collector = collector;
353
358
  this.config = {
@@ -355,6 +360,19 @@ var DevToolsServer = class {
355
360
  host: config.host ?? "localhost",
356
361
  cors: config.cors ?? true
357
362
  };
363
+ this.dashboardHTML = this.loadDashboardHTML();
364
+ }
365
+ /**
366
+ * Load dashboard HTML from file
367
+ */
368
+ loadDashboardHTML() {
369
+ try {
370
+ const __filename = (0, import_url.fileURLToPath)(import_meta.url);
371
+ const __dirname = (0, import_path.dirname)(__filename);
372
+ return (0, import_fs.readFileSync)((0, import_path.join)(__dirname, "dashboard.html"), "utf-8");
373
+ } catch {
374
+ return this.getInlineDashboardHTML();
375
+ }
358
376
  }
359
377
  /**
360
378
  * Start the DevTools server
@@ -469,7 +487,7 @@ var DevToolsServer = class {
469
487
  });
470
488
  });
471
489
  app.get("/", (_req, res) => {
472
- res.send(this.getDashboardHTML());
490
+ res.send(this.dashboardHTML);
473
491
  });
474
492
  }
475
493
  /**
@@ -484,180 +502,10 @@ var DevToolsServer = class {
484
502
  }
485
503
  }
486
504
  /**
487
- * Get embedded dashboard HTML
505
+ * Inline fallback dashboard HTML
488
506
  */
489
- getDashboardHTML() {
490
- return `<!DOCTYPE html>
491
- <html lang="en">
492
- <head>
493
- <meta charset="UTF-8">
494
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
495
- <title>OrkaJS DevTools</title>
496
- <script src="https://cdn.tailwindcss.com"></script>
497
- <style>
498
- .tree-line { border-left: 2px solid #e2e8f0; }
499
- .dark .tree-line { border-left-color: #334155; }
500
- </style>
501
- </head>
502
- <body class="bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-white min-h-screen">
503
- <div id="app" class="max-w-7xl mx-auto p-6">
504
- <header class="flex items-center justify-between mb-8">
505
- <div class="flex items-center gap-3">
506
- <div class="w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center">
507
- <svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
508
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
509
- </svg>
510
- </div>
511
- <div>
512
- <h1 class="text-2xl font-bold">OrkaJS DevTools</h1>
513
- <p class="text-sm text-slate-500">Real-time LLM observability</p>
514
- </div>
515
- </div>
516
- <div class="flex items-center gap-4">
517
- <span id="status" class="flex items-center gap-2 text-sm">
518
- <span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
519
- Connected
520
- </span>
521
- <button onclick="clearTraces()" class="px-3 py-1.5 text-sm bg-red-500/10 text-red-500 rounded-lg hover:bg-red-500/20">
522
- Clear
523
- </button>
524
- <button onclick="exportTraces()" class="px-3 py-1.5 text-sm bg-purple-500/10 text-purple-500 rounded-lg hover:bg-purple-500/20">
525
- Export
526
- </button>
527
- </div>
528
- </header>
529
-
530
- <!-- Metrics -->
531
- <div id="metrics" class="grid grid-cols-4 gap-4 mb-8">
532
- <div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm">
533
- <p class="text-sm text-slate-500 mb-1">Total Runs</p>
534
- <p id="metric-runs" class="text-2xl font-bold">0</p>
535
- </div>
536
- <div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm">
537
- <p class="text-sm text-slate-500 mb-1">Avg Latency</p>
538
- <p id="metric-latency" class="text-2xl font-bold">0ms</p>
539
- </div>
540
- <div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm">
541
- <p class="text-sm text-slate-500 mb-1">Total Tokens</p>
542
- <p id="metric-tokens" class="text-2xl font-bold">0</p>
543
- </div>
544
- <div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm">
545
- <p class="text-sm text-slate-500 mb-1">Error Rate</p>
546
- <p id="metric-errors" class="text-2xl font-bold">0%</p>
547
- </div>
548
- </div>
549
-
550
- <!-- Sessions & Traces -->
551
- <div class="grid grid-cols-3 gap-6">
552
- <div class="col-span-1">
553
- <h2 class="text-lg font-semibold mb-4">Sessions</h2>
554
- <div id="sessions" class="space-y-2"></div>
555
- </div>
556
- <div class="col-span-2">
557
- <h2 class="text-lg font-semibold mb-4">Trace Viewer</h2>
558
- <div id="traces" class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm min-h-[400px]">
559
- <p class="text-slate-500 text-center py-8">Select a session to view traces</p>
560
- </div>
561
- </div>
562
- </div>
563
- </div>
564
-
565
- <script>
566
- let selectedSession = null;
567
-
568
- // SSE connection
569
- const events = new EventSource('/api/events');
570
- events.onmessage = (e) => {
571
- const event = JSON.parse(e.data);
572
- console.log('Event:', event);
573
- refreshData();
574
- };
575
- events.onerror = () => {
576
- document.getElementById('status').innerHTML = '<span class="w-2 h-2 bg-red-500 rounded-full"></span> Disconnected';
577
- };
578
-
579
- async function refreshData() {
580
- // Fetch metrics
581
- const metrics = await fetch('/api/metrics').then(r => r.json());
582
- document.getElementById('metric-runs').textContent = metrics.totalRuns;
583
- document.getElementById('metric-latency').textContent = Math.round(metrics.avgLatencyMs) + 'ms';
584
- document.getElementById('metric-tokens').textContent = metrics.totalTokens.toLocaleString();
585
- document.getElementById('metric-errors').textContent = (metrics.errorRate * 100).toFixed(1) + '%';
586
-
587
- // Fetch sessions
588
- const sessions = await fetch('/api/sessions').then(r => r.json());
589
- renderSessions(sessions);
590
-
591
- if (selectedSession) {
592
- const session = await fetch('/api/sessions/' + selectedSession).then(r => r.json());
593
- renderTraces(session.runs);
594
- }
595
- }
596
-
597
- function renderSessions(sessions) {
598
- const container = document.getElementById('sessions');
599
- container.innerHTML = sessions.map(s => \`
600
- <div onclick="selectSession('\${s.id}')" class="p-3 rounded-lg cursor-pointer \${selectedSession === s.id ? 'bg-purple-500/20 border border-purple-500' : 'bg-white dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700'}">
601
- <p class="font-medium">\${s.name || 'Session'}</p>
602
- <p class="text-xs text-slate-500">\${s.runs.length} runs \u2022 \${new Date(s.startTime).toLocaleTimeString()}</p>
603
- </div>
604
- \`).join('');
605
- }
606
-
607
- function selectSession(id) {
608
- selectedSession = id;
609
- refreshData();
610
- }
611
-
612
- function renderTraces(runs, depth = 0) {
613
- if (!runs || runs.length === 0) {
614
- document.getElementById('traces').innerHTML = '<p class="text-slate-500 text-center py-8">No traces in this session</p>';
615
- return;
616
- }
617
-
618
- const html = runs.map(run => renderRun(run, depth)).join('');
619
- document.getElementById('traces').innerHTML = html;
620
- }
621
-
622
- function renderRun(run, depth) {
623
- const statusColor = run.status === 'success' ? 'bg-green-500' : run.status === 'error' ? 'bg-red-500' : 'bg-yellow-500';
624
- const typeColors = {
625
- llm: 'text-purple-500',
626
- agent: 'text-blue-500',
627
- tool: 'text-orange-500',
628
- retrieval: 'text-green-500',
629
- chain: 'text-pink-500',
630
- };
631
-
632
- return \`
633
- <div class="mb-2" style="margin-left: \${depth * 20}px">
634
- <div class="flex items-center gap-2 p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700">
635
- <span class="w-2 h-2 rounded-full \${statusColor}"></span>
636
- <span class="text-xs font-medium \${typeColors[run.type] || 'text-slate-500'}">\${run.type.toUpperCase()}</span>
637
- <span class="font-medium">\${run.name}</span>
638
- <span class="text-xs text-slate-500 ml-auto">\${run.latencyMs ? run.latencyMs + 'ms' : 'running...'}</span>
639
- \${run.metadata?.totalTokens ? '<span class="text-xs text-slate-400">' + run.metadata.totalTokens + ' tokens</span>' : ''}
640
- </div>
641
- \${run.children.map(c => renderRun(c, depth + 1)).join('')}
642
- </div>
643
- \`;
644
- }
645
-
646
- async function clearTraces() {
647
- await fetch('/api/sessions', { method: 'DELETE' });
648
- selectedSession = null;
649
- refreshData();
650
- }
651
-
652
- function exportTraces() {
653
- window.open('/api/export', '_blank');
654
- }
655
-
656
- // Initial load
657
- refreshData();
658
- </script>
659
- </body>
660
- </html>`;
507
+ getInlineDashboardHTML() {
508
+ return '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>OrkaJS DevTools</title><script src="https://cdn.tailwindcss.com"></script><script>tailwind.config={darkMode:"class"}</script><style>.tree-line{border-left:2px solid #e2e8f0}.dark .tree-line{border-left-color:#334155}.scrollbar-thin::-webkit-scrollbar{width:6px}.scrollbar-thin::-webkit-scrollbar-thumb{background:#64748b;border-radius:3px}</style></head><body class="bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-white min-h-screen"><div id="app" class="max-w-7xl mx-auto p-6"><header class="flex items-center justify-between mb-8"><div class="flex items-center gap-3"><img src="https://devtools.orkajs.com/orka-devtools.png" alt="OrkaJS" class="w-10 h-10 rounded-lg" onerror="this.style.display=\'none\'"><div><h1 class="text-2xl font-bold">OrkaJS DevTools</h1><p class="text-sm text-slate-500">Real-time LLM observability</p></div></div><div class="flex items-center gap-3"><span id="status" class="flex items-center gap-2 text-sm px-3 py-1.5 bg-green-500/10 rounded-lg"><span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span><span class="text-green-600 dark:text-green-400">Live</span></span><button onclick="toggleTheme()" class="p-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700"><svg id="themeIcon" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg></button><button onclick="clearTraces()" class="px-3 py-2 text-sm bg-red-500/10 text-red-600 rounded-lg hover:bg-red-500/20">Clear</button><button onclick="exportTraces()" class="px-3 py-2 text-sm bg-purple-500/10 text-purple-600 rounded-lg hover:bg-purple-500/20">Export</button></div></header><div class="grid grid-cols-4 gap-4 mb-8"><div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm"><p class="text-sm text-slate-500 mb-1">Total Runs</p><p id="metric-runs" class="text-2xl font-bold">0</p></div><div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm"><p class="text-sm text-slate-500 mb-1">Avg Latency</p><p id="metric-latency" class="text-2xl font-bold">0ms</p></div><div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm"><p class="text-sm text-slate-500 mb-1">Total Tokens</p><p id="metric-tokens" class="text-2xl font-bold">0</p></div><div class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm"><p class="text-sm text-slate-500 mb-1">Error Rate</p><p id="metric-errors" class="text-2xl font-bold">0%</p></div></div><div class="grid grid-cols-3 gap-6"><div class="col-span-1"><h2 class="text-lg font-semibold mb-4">Sessions</h2><div id="sessions" class="space-y-2"></div></div><div class="col-span-2"><h2 class="text-lg font-semibold mb-4">Trace Viewer</h2><div id="traces" class="bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm min-h-[400px]"><p class="text-slate-500 text-center py-8">Select a session to view traces</p></div></div></div></div><script>let selectedSession=null;function initTheme(){const t=localStorage.getItem("orka-devtools-theme");("dark"===t||!t&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&document.documentElement.classList.add("dark")}initTheme();function toggleTheme(){const t=document.documentElement.classList.toggle("dark");localStorage.setItem("orka-devtools-theme",t?"dark":"light")}const events=new EventSource("/api/events");events.onmessage=e=>{refreshData()};events.onerror=()=>{document.getElementById("status").innerHTML=\'<span class="w-2 h-2 bg-red-500 rounded-full"></span><span class="text-red-600">Disconnected</span>\'};async function refreshData(){const[t,e]=await Promise.all([fetch("/api/metrics").then(t=>t.json()),fetch("/api/sessions").then(t=>t.json())]);document.getElementById("metric-runs").textContent=t.totalRuns;document.getElementById("metric-latency").textContent=Math.round(t.avgLatencyMs)+"ms";document.getElementById("metric-tokens").textContent=t.totalTokens.toLocaleString();document.getElementById("metric-errors").textContent=(100*t.errorRate).toFixed(1)+"%";renderSessions(e);if(selectedSession){const t=e.find(t=>t.id===selectedSession);t&&renderTraces(t.runs)}}function renderSessions(t){document.getElementById("sessions").innerHTML=t.map(t=>`<div onclick="selectSession(\'${t.id}\')" class="p-3 rounded-lg cursor-pointer ${selectedSession===t.id?"bg-purple-500/20 border border-purple-500":"bg-white dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700"}"><p class="font-medium">${t.name||"Session"}</p><p class="text-xs text-slate-500">${t.runs.length} runs</p></div>`).join("")}function selectSession(t){selectedSession=t;refreshData()}function renderTraces(t){if(!t||0===t.length)return void(document.getElementById("traces").innerHTML=\'<p class="text-slate-500 text-center py-8">No traces</p>\');document.getElementById("traces").innerHTML=t.map(t=>renderRun(t,0)).join("")}function renderRun(t,e){const s={success:"bg-green-500",error:"bg-red-500",running:"bg-yellow-500"},n={llm:"text-purple-500",agent:"text-blue-500",tool:"text-orange-500"};return`<div class="mb-2" style="margin-left:${20*e}px"><div class="flex items-center gap-2 p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700"><span class="w-2 h-2 rounded-full ${s[t.status]||"bg-slate-400"}"></span><span class="text-xs font-medium ${n[t.type]||"text-slate-500"}">${t.type.toUpperCase()}</span><span class="font-medium">${t.name}</span><span class="text-xs text-slate-500 ml-auto">${t.latencyMs?t.latencyMs+"ms":"..."}</span></div>${(t.children||[]).map(t=>renderRun(t,e+1)).join("")}</div>`}async function clearTraces(){confirm("Clear all traces?")&&(await fetch("/api/sessions",{method:"DELETE"}),selectedSession=null,refreshData())}function exportTraces(){window.open("/api/export","_blank")}refreshData()</script></body></html>';
661
509
  }
662
510
  };
663
511
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/collector.ts","../src/server.ts","../src/tracer-hook.ts","../src/opentelemetry.ts","../src/replay.ts"],"sourcesContent":["import { TraceCollector, getCollector } from './collector.js';\nimport { DevToolsServer } from './server.js';\nimport type { DevToolsConfig, TraceRunType, TraceMetadata } from './types.js';\n\nexport * from './types.js';\nexport { TraceCollector, getCollector, resetCollector } from './collector.js';\nexport { DevToolsServer } from './server.js';\nexport { createDevToolsHook, createTracerWithDevTools } from './tracer-hook.js';\nexport { OpenTelemetryExporter, createOTLPExporter, type OpenTelemetryConfig } from './opentelemetry.js';\nexport { ReplayDebugger, createReplayDebugger, getReplayDebugger, type RunComparison, type TestCase } from './replay.js';\n\n/**\n * Start the DevTools dashboard\n */\nexport async function devtools(config: DevToolsConfig = {}): Promise<{\n collector: TraceCollector;\n server: DevToolsServer;\n stop: () => Promise<void>;\n}> {\n const collector = getCollector(config);\n const server = new DevToolsServer(collector, config);\n \n await server.start();\n\n // Open browser if requested\n if (config.open !== false) {\n const url = `http://${config.host ?? 'localhost'}:${config.port ?? 3001}`;\n try {\n const { exec } = await import('child_process');\n const command = process.platform === 'darwin' ? 'open' :\n process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${command} ${url}`);\n } catch {\n // Ignore if can't open browser\n }\n }\n\n return {\n collector,\n server,\n stop: () => server.stop(),\n };\n}\n\n/**\n * Create a trace wrapper for any function\n */\nexport function withTrace<T extends (...args: unknown[]) => unknown>(\n fn: T,\n options: {\n name?: string;\n type?: TraceRunType;\n collector?: TraceCollector;\n } = {}\n): T {\n const collector = options.collector ?? getCollector();\n const name = options.name ?? fn.name ?? 'anonymous';\n const type = options.type ?? 'custom';\n\n return (async (...args: unknown[]) => {\n const runId = collector.startRun(type, name, args);\n try {\n const result = await fn(...args);\n collector.endRun(runId, result);\n return result;\n } catch (error) {\n collector.errorRun(runId, error as Error);\n throw error;\n }\n }) as T;\n}\n\n/**\n * Decorator for tracing class methods\n */\nexport function Trace(options: {\n name?: string;\n type?: TraceRunType;\n} = {}) {\n return function (\n _target: unknown,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value;\n const name = options.name ?? propertyKey;\n const type = options.type ?? 'custom';\n\n descriptor.value = async function (...args: unknown[]) {\n const collector = getCollector();\n const runId = collector.startRun(type, name, args);\n try {\n const result = await originalMethod.apply(this, args);\n collector.endRun(runId, result);\n return result;\n } catch (error) {\n collector.errorRun(runId, error as Error);\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Manual tracing helpers\n */\nexport const trace = {\n start(type: TraceRunType, name: string, input?: unknown, metadata?: TraceMetadata): string {\n return getCollector().startRun(type, name, input, metadata);\n },\n \n end(runId: string, output?: unknown, metadata?: TraceMetadata): void {\n getCollector().endRun(runId, output, metadata);\n },\n \n error(runId: string, error: Error | string): void {\n getCollector().errorRun(runId, error);\n },\n \n session(name?: string): string {\n return getCollector().startSession(name);\n },\n \n endSession(sessionId?: string): void {\n getCollector().endSession(sessionId);\n },\n\n /**\n * Wrap an async function with tracing\n */\n async wrap<T>(\n type: TraceRunType,\n name: string,\n fn: () => Promise<T>,\n metadata?: TraceMetadata\n ): Promise<T> {\n const runId = getCollector().startRun(type, name, undefined, metadata);\n try {\n const result = await fn();\n getCollector().endRun(runId, result);\n return result;\n } catch (error) {\n getCollector().errorRun(runId, error as Error);\n throw error;\n }\n },\n};\n","import { generateId } from '@orka-js/core';\nimport type {\n TraceRun,\n TraceRunType,\n TraceSession,\n TraceMetrics,\n TraceMetadata,\n TraceEvent,\n DevToolsConfig,\n} from './types.js';\n\n/**\n * TraceCollector - Collects and manages trace data for DevTools\n */\nexport class TraceCollector {\n private sessions: Map<string, TraceSession> = new Map();\n private activeSessionId?: string;\n private runStack: Map<string, TraceRun[]> = new Map();\n private maxTraces: number;\n private retentionMs: number;\n private listeners: Set<(event: TraceEvent) => void> = new Set();\n\n constructor(config: DevToolsConfig = {}) {\n this.maxTraces = config.maxTraces ?? 1000;\n this.retentionMs = config.retentionMs ?? 24 * 60 * 60 * 1000; // 24 hours\n }\n\n /**\n * Start a new trace session\n */\n startSession(name?: string): string {\n const sessionId = generateId();\n const session: TraceSession = {\n id: sessionId,\n name: name ?? `Session ${this.sessions.size + 1}`,\n startTime: Date.now(),\n runs: [],\n };\n\n this.sessions.set(sessionId, session);\n this.activeSessionId = sessionId;\n this.runStack.set(sessionId, []);\n\n this.emit({\n type: 'session:start',\n timestamp: Date.now(),\n sessionId,\n });\n\n this.cleanup();\n return sessionId;\n }\n\n /**\n * End the current session\n */\n endSession(sessionId?: string): void {\n const id = sessionId ?? this.activeSessionId;\n if (!id) return;\n\n const session = this.sessions.get(id);\n if (session) {\n session.endTime = Date.now();\n this.emit({\n type: 'session:end',\n timestamp: Date.now(),\n sessionId: id,\n });\n }\n\n if (this.activeSessionId === id) {\n this.activeSessionId = undefined;\n }\n }\n\n /**\n * Start a new trace run\n */\n startRun(\n type: TraceRunType,\n name: string,\n input?: unknown,\n metadata?: TraceMetadata\n ): string {\n const sessionId = this.activeSessionId ?? this.startSession();\n const session = this.sessions.get(sessionId)!;\n const stack = this.runStack.get(sessionId)!;\n\n const run: TraceRun = {\n id: generateId(),\n parentId: stack.length > 0 ? stack[stack.length - 1].id : undefined,\n type,\n name,\n startTime: Date.now(),\n status: 'running',\n input,\n metadata,\n children: [],\n };\n\n // Add to parent's children or session root\n if (stack.length > 0) {\n stack[stack.length - 1].children.push(run);\n } else {\n session.runs.push(run);\n }\n\n stack.push(run);\n\n this.emit({\n type: 'run:start',\n timestamp: Date.now(),\n sessionId,\n run,\n });\n\n return run.id;\n }\n\n /**\n * End a trace run\n */\n endRun(runId: string, output?: unknown, metadata?: TraceMetadata): void {\n const sessionId = this.activeSessionId;\n if (!sessionId) return;\n\n const stack = this.runStack.get(sessionId);\n if (!stack) return;\n\n const runIndex = stack.findIndex(r => r.id === runId);\n if (runIndex === -1) return;\n\n const run = stack[runIndex];\n run.endTime = Date.now();\n run.latencyMs = run.endTime - run.startTime;\n run.status = 'success';\n run.output = output;\n\n if (metadata) {\n run.metadata = { ...run.metadata, ...metadata };\n }\n\n // Pop from stack\n stack.splice(runIndex, 1);\n\n this.emit({\n type: 'run:end',\n timestamp: Date.now(),\n sessionId,\n run,\n });\n }\n\n /**\n * Mark a run as errored\n */\n errorRun(runId: string, error: Error | string): void {\n const sessionId = this.activeSessionId;\n if (!sessionId) return;\n\n const stack = this.runStack.get(sessionId);\n if (!stack) return;\n\n const runIndex = stack.findIndex(r => r.id === runId);\n if (runIndex === -1) return;\n\n const run = stack[runIndex];\n run.endTime = Date.now();\n run.latencyMs = run.endTime - run.startTime;\n run.status = 'error';\n run.error = error instanceof Error ? error.message : error;\n\n stack.splice(runIndex, 1);\n\n this.emit({\n type: 'run:error',\n timestamp: Date.now(),\n sessionId,\n run,\n error: run.error,\n });\n }\n\n /**\n * Get all sessions\n */\n getSessions(): TraceSession[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Get a specific session\n */\n getSession(sessionId: string): TraceSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n /**\n * Get metrics for a session or all sessions\n */\n getMetrics(sessionId?: string): TraceMetrics {\n const sessions = sessionId\n ? [this.sessions.get(sessionId)].filter(Boolean) as TraceSession[]\n : Array.from(this.sessions.values());\n\n const metrics: TraceMetrics = {\n totalRuns: 0,\n totalLatencyMs: 0,\n avgLatencyMs: 0,\n totalTokens: 0,\n totalCost: 0,\n errorRate: 0,\n runsByType: {} as Record<TraceRunType, number>,\n tokensByModel: {},\n costByModel: {},\n };\n\n let errorCount = 0;\n\n const processRun = (run: TraceRun) => {\n metrics.totalRuns++;\n metrics.totalLatencyMs += run.latencyMs ?? 0;\n\n if (run.status === 'error') errorCount++;\n\n // Count by type\n metrics.runsByType[run.type] = (metrics.runsByType[run.type] ?? 0) + 1;\n\n // Aggregate token/cost metrics\n if (run.metadata) {\n const { totalTokens, cost, model } = run.metadata;\n if (totalTokens) {\n metrics.totalTokens += totalTokens;\n if (model) {\n metrics.tokensByModel[model] = (metrics.tokensByModel[model] ?? 0) + totalTokens;\n }\n }\n if (cost) {\n metrics.totalCost += cost;\n if (model) {\n metrics.costByModel[model] = (metrics.costByModel[model] ?? 0) + cost;\n }\n }\n }\n\n // Process children\n for (const child of run.children) {\n processRun(child);\n }\n };\n\n for (const session of sessions) {\n for (const run of session.runs) {\n processRun(run);\n }\n }\n\n metrics.avgLatencyMs = metrics.totalRuns > 0\n ? metrics.totalLatencyMs / metrics.totalRuns\n : 0;\n metrics.errorRate = metrics.totalRuns > 0\n ? errorCount / metrics.totalRuns\n : 0;\n\n return metrics;\n }\n\n /**\n * Find a run by ID\n */\n findRun(runId: string, sessionId?: string): TraceRun | undefined {\n const sessions = sessionId\n ? [this.sessions.get(sessionId)].filter(Boolean) as TraceSession[]\n : Array.from(this.sessions.values());\n\n const findInRuns = (runs: TraceRun[]): TraceRun | undefined => {\n for (const run of runs) {\n if (run.id === runId) return run;\n const found = findInRuns(run.children);\n if (found) return found;\n }\n return undefined;\n };\n\n for (const session of sessions) {\n const found = findInRuns(session.runs);\n if (found) return found;\n }\n\n return undefined;\n }\n\n /**\n * Subscribe to trace events\n */\n subscribe(listener: (event: TraceEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Emit an event to all listeners\n */\n private emit(event: TraceEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Cleanup old sessions\n */\n private cleanup(): void {\n const now = Date.now();\n const cutoff = now - this.retentionMs;\n\n for (const [id, session] of this.sessions) {\n if (session.endTime && session.endTime < cutoff) {\n this.sessions.delete(id);\n this.runStack.delete(id);\n }\n }\n\n // Limit total sessions\n if (this.sessions.size > this.maxTraces) {\n const sorted = Array.from(this.sessions.entries())\n .sort((a, b) => a[1].startTime - b[1].startTime);\n \n const toDelete = sorted.slice(0, this.sessions.size - this.maxTraces);\n for (const [id] of toDelete) {\n this.sessions.delete(id);\n this.runStack.delete(id);\n }\n }\n }\n\n /**\n * Clear all traces\n */\n clear(): void {\n this.sessions.clear();\n this.runStack.clear();\n this.activeSessionId = undefined;\n }\n\n /**\n * Export traces as JSON\n */\n export(): string {\n return JSON.stringify({\n sessions: Array.from(this.sessions.values()),\n exportedAt: new Date().toISOString(),\n }, null, 2);\n }\n\n /**\n * Import traces from JSON\n */\n import(json: string): void {\n const data = JSON.parse(json) as { sessions: TraceSession[] };\n for (const session of data.sessions) {\n this.sessions.set(session.id, session);\n }\n }\n}\n\n// Global collector instance\nlet globalCollector: TraceCollector | undefined;\n\nexport function getCollector(config?: DevToolsConfig): TraceCollector {\n if (!globalCollector) {\n globalCollector = new TraceCollector(config);\n }\n return globalCollector;\n}\n\nexport function resetCollector(): void {\n globalCollector = undefined;\n}\n","import type { TraceCollector } from './collector.js';\nimport type { DevToolsConfig, TraceEvent } from './types.js';\nimport type { Application, Request, Response, NextFunction } from 'express';\nimport type { Server, ServerResponse } from 'http';\n\n/**\n * DevTools Server - Express server for the DevTools dashboard\n */\nexport class DevToolsServer {\n private collector: TraceCollector;\n private config: Required<Pick<DevToolsConfig, 'port' | 'host' | 'cors'>>;\n private server?: Server;\n private clients: Set<ServerResponse> = new Set();\n\n constructor(collector: TraceCollector, config: DevToolsConfig = {}) {\n this.collector = collector;\n this.config = {\n port: config.port ?? 3001,\n host: config.host ?? 'localhost',\n cors: config.cors ?? true,\n };\n }\n\n /**\n * Start the DevTools server\n */\n async start(): Promise<void> {\n // Dynamic import for optional express dependency\n let expressModule: { default: () => Application; json: () => unknown };\n let httpModule: typeof import('http');\n \n try {\n expressModule = await import('express') as unknown as { default: () => Application; json: () => unknown };\n httpModule = await import('http');\n } catch {\n throw new Error(\n 'Express is required for DevTools server. Install it with: npm install express'\n );\n }\n\n const app = expressModule.default();\n\n // CORS middleware\n if (this.config.cors) {\n app.use((_req: Request, res: Response, next: NextFunction) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE');\n next();\n });\n }\n\n app.use(expressModule.json() as unknown as (req: Request, res: Response, next: NextFunction) => void);\n\n // API Routes\n this.setupRoutes(app);\n\n // Create HTTP server\n this.server = httpModule.createServer(app);\n\n // Start listening\n await new Promise<void>((resolve) => {\n this.server!.listen(this.config.port, this.config.host, () => {\n console.log(`\\n🔍 OrkaJS DevTools running at http://${this.config.host}:${this.config.port}\\n`);\n resolve();\n });\n });\n\n // Subscribe to trace events for SSE\n this.collector.subscribe((event) => {\n this.broadcastEvent(event);\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n if (this.server) {\n await new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Setup API routes\n */\n private setupRoutes(app: Application): void {\n // Health check\n app.get('/api/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', timestamp: Date.now() });\n });\n\n // Get all sessions\n app.get('/api/sessions', (_req: Request, res: Response) => {\n const sessions = this.collector.getSessions();\n res.json(sessions);\n });\n\n // Get a specific session\n app.get('/api/sessions/:id', (req: Request, res: Response) => {\n const session = this.collector.getSession(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session);\n });\n\n // Get metrics\n app.get('/api/metrics', (req: Request, res: Response) => {\n const sessionId = req.query.sessionId as string | undefined;\n const metrics = this.collector.getMetrics(sessionId);\n res.json(metrics);\n });\n\n // Find a run\n app.get('/api/runs/:id', (req: Request, res: Response) => {\n const sessionId = req.query.sessionId as string | undefined;\n const run = this.collector.findRun(req.params.id, sessionId);\n if (!run) {\n res.status(404).json({ error: 'Run not found' });\n return;\n }\n res.json(run);\n });\n\n // Clear all traces\n app.delete('/api/sessions', (_req: Request, res: Response) => {\n this.collector.clear();\n res.json({ success: true });\n });\n\n // Export traces\n app.get('/api/export', (_req: Request, res: Response) => {\n const data = this.collector.export();\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Content-Disposition', 'attachment; filename=orka-traces.json');\n res.send(data);\n });\n\n // Import traces\n app.post('/api/import', (req: Request, res: Response) => {\n try {\n this.collector.import(JSON.stringify(req.body));\n res.json({ success: true });\n } catch {\n res.status(400).json({ error: 'Invalid trace data' });\n }\n });\n\n // Server-Sent Events for real-time updates\n app.get('/api/events', (req: Request, res: Response) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n this.clients.add(res as unknown as ServerResponse);\n\n req.on('close', () => {\n this.clients.delete(res as unknown as ServerResponse);\n });\n });\n\n // Serve static UI (if available)\n app.get('/', (_req: Request, res: Response) => {\n res.send(this.getDashboardHTML());\n });\n }\n\n /**\n * Broadcast event to all SSE clients\n */\n private broadcastEvent(event: TraceEvent): void {\n const data = JSON.stringify(event);\n for (const client of this.clients) {\n client.write(`data: ${data}\\n\\n`);\n }\n }\n\n /**\n * Get embedded dashboard HTML\n */\n private getDashboardHTML(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>OrkaJS DevTools</title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <style>\n .tree-line { border-left: 2px solid #e2e8f0; }\n .dark .tree-line { border-left-color: #334155; }\n </style>\n</head>\n<body class=\"bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-white min-h-screen\">\n <div id=\"app\" class=\"max-w-7xl mx-auto p-6\">\n <header class=\"flex items-center justify-between mb-8\">\n <div class=\"flex items-center gap-3\">\n <div class=\"w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-lg flex items-center justify-center\">\n <svg class=\"w-6 h-6 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"/>\n </svg>\n </div>\n <div>\n <h1 class=\"text-2xl font-bold\">OrkaJS DevTools</h1>\n <p class=\"text-sm text-slate-500\">Real-time LLM observability</p>\n </div>\n </div>\n <div class=\"flex items-center gap-4\">\n <span id=\"status\" class=\"flex items-center gap-2 text-sm\">\n <span class=\"w-2 h-2 bg-green-500 rounded-full animate-pulse\"></span>\n Connected\n </span>\n <button onclick=\"clearTraces()\" class=\"px-3 py-1.5 text-sm bg-red-500/10 text-red-500 rounded-lg hover:bg-red-500/20\">\n Clear\n </button>\n <button onclick=\"exportTraces()\" class=\"px-3 py-1.5 text-sm bg-purple-500/10 text-purple-500 rounded-lg hover:bg-purple-500/20\">\n Export\n </button>\n </div>\n </header>\n\n <!-- Metrics -->\n <div id=\"metrics\" class=\"grid grid-cols-4 gap-4 mb-8\">\n <div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\">\n <p class=\"text-sm text-slate-500 mb-1\">Total Runs</p>\n <p id=\"metric-runs\" class=\"text-2xl font-bold\">0</p>\n </div>\n <div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\">\n <p class=\"text-sm text-slate-500 mb-1\">Avg Latency</p>\n <p id=\"metric-latency\" class=\"text-2xl font-bold\">0ms</p>\n </div>\n <div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\">\n <p class=\"text-sm text-slate-500 mb-1\">Total Tokens</p>\n <p id=\"metric-tokens\" class=\"text-2xl font-bold\">0</p>\n </div>\n <div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\">\n <p class=\"text-sm text-slate-500 mb-1\">Error Rate</p>\n <p id=\"metric-errors\" class=\"text-2xl font-bold\">0%</p>\n </div>\n </div>\n\n <!-- Sessions & Traces -->\n <div class=\"grid grid-cols-3 gap-6\">\n <div class=\"col-span-1\">\n <h2 class=\"text-lg font-semibold mb-4\">Sessions</h2>\n <div id=\"sessions\" class=\"space-y-2\"></div>\n </div>\n <div class=\"col-span-2\">\n <h2 class=\"text-lg font-semibold mb-4\">Trace Viewer</h2>\n <div id=\"traces\" class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm min-h-[400px]\">\n <p class=\"text-slate-500 text-center py-8\">Select a session to view traces</p>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n let selectedSession = null;\n\n // SSE connection\n const events = new EventSource('/api/events');\n events.onmessage = (e) => {\n const event = JSON.parse(e.data);\n console.log('Event:', event);\n refreshData();\n };\n events.onerror = () => {\n document.getElementById('status').innerHTML = '<span class=\"w-2 h-2 bg-red-500 rounded-full\"></span> Disconnected';\n };\n\n async function refreshData() {\n // Fetch metrics\n const metrics = await fetch('/api/metrics').then(r => r.json());\n document.getElementById('metric-runs').textContent = metrics.totalRuns;\n document.getElementById('metric-latency').textContent = Math.round(metrics.avgLatencyMs) + 'ms';\n document.getElementById('metric-tokens').textContent = metrics.totalTokens.toLocaleString();\n document.getElementById('metric-errors').textContent = (metrics.errorRate * 100).toFixed(1) + '%';\n\n // Fetch sessions\n const sessions = await fetch('/api/sessions').then(r => r.json());\n renderSessions(sessions);\n\n if (selectedSession) {\n const session = await fetch('/api/sessions/' + selectedSession).then(r => r.json());\n renderTraces(session.runs);\n }\n }\n\n function renderSessions(sessions) {\n const container = document.getElementById('sessions');\n container.innerHTML = sessions.map(s => \\`\n <div onclick=\"selectSession('\\${s.id}')\" class=\"p-3 rounded-lg cursor-pointer \\${selectedSession === s.id ? 'bg-purple-500/20 border border-purple-500' : 'bg-white dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700'}\">\n <p class=\"font-medium\">\\${s.name || 'Session'}</p>\n <p class=\"text-xs text-slate-500\">\\${s.runs.length} runs • \\${new Date(s.startTime).toLocaleTimeString()}</p>\n </div>\n \\`).join('');\n }\n\n function selectSession(id) {\n selectedSession = id;\n refreshData();\n }\n\n function renderTraces(runs, depth = 0) {\n if (!runs || runs.length === 0) {\n document.getElementById('traces').innerHTML = '<p class=\"text-slate-500 text-center py-8\">No traces in this session</p>';\n return;\n }\n\n const html = runs.map(run => renderRun(run, depth)).join('');\n document.getElementById('traces').innerHTML = html;\n }\n\n function renderRun(run, depth) {\n const statusColor = run.status === 'success' ? 'bg-green-500' : run.status === 'error' ? 'bg-red-500' : 'bg-yellow-500';\n const typeColors = {\n llm: 'text-purple-500',\n agent: 'text-blue-500',\n tool: 'text-orange-500',\n retrieval: 'text-green-500',\n chain: 'text-pink-500',\n };\n\n return \\`\n <div class=\"mb-2\" style=\"margin-left: \\${depth * 20}px\">\n <div class=\"flex items-center gap-2 p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700\">\n <span class=\"w-2 h-2 rounded-full \\${statusColor}\"></span>\n <span class=\"text-xs font-medium \\${typeColors[run.type] || 'text-slate-500'}\">\\${run.type.toUpperCase()}</span>\n <span class=\"font-medium\">\\${run.name}</span>\n <span class=\"text-xs text-slate-500 ml-auto\">\\${run.latencyMs ? run.latencyMs + 'ms' : 'running...'}</span>\n \\${run.metadata?.totalTokens ? '<span class=\"text-xs text-slate-400\">' + run.metadata.totalTokens + ' tokens</span>' : ''}\n </div>\n \\${run.children.map(c => renderRun(c, depth + 1)).join('')}\n </div>\n \\`;\n }\n\n async function clearTraces() {\n await fetch('/api/sessions', { method: 'DELETE' });\n selectedSession = null;\n refreshData();\n }\n\n function exportTraces() {\n window.open('/api/export', '_blank');\n }\n\n // Initial load\n refreshData();\n </script>\n</body>\n</html>`;\n }\n}\n","import { getCollector } from './collector.js';\nimport type { TraceRunType, TraceMetadata } from './types.js';\n\n/**\n * Hook interface matching @orka-js/observability\n */\ninterface ObservabilityHook {\n onTraceStart?: (trace: TracerTrace) => void;\n onTraceEnd?: (trace: TracerTrace) => void;\n onEvent?: (event: TracerEvent) => void;\n onError?: (error: Error, context?: Record<string, unknown>) => void;\n}\n\ninterface TracerTrace {\n id: string;\n name: string;\n startTime: number;\n endTime?: number;\n totalLatencyMs?: number;\n totalTokens?: number;\n events: TracerEvent[];\n metadata?: Record<string, unknown>;\n}\n\ninterface TracerEvent {\n id: string;\n traceId: string;\n type: string;\n name: string;\n startTime?: number;\n endTime?: number;\n latencyMs?: number;\n input?: unknown;\n output?: unknown;\n usage?: {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n };\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Map Tracer event types to DevTools run types\n */\nfunction mapEventType(type: string): TraceRunType {\n const typeMap: Record<string, TraceRunType> = {\n 'llm': 'llm',\n 'llm_call': 'llm',\n 'embedding': 'embedding',\n 'retrieval': 'retrieval',\n 'tool': 'tool',\n 'tool_call': 'tool',\n 'agent': 'agent',\n 'agent_step': 'agent',\n 'chain': 'chain',\n 'workflow': 'workflow',\n 'graph': 'graph',\n 'node': 'node',\n };\n return typeMap[type.toLowerCase()] ?? 'custom';\n}\n\n/**\n * Create a DevTools hook for the Tracer\n * \n * Usage:\n * ```ts\n * import { Tracer } from '@orka-js/observability';\n * import { createDevToolsHook, devtools } from '@orka-js/devtools';\n * \n * await devtools();\n * \n * const tracer = new Tracer({\n * hooks: [createDevToolsHook()]\n * });\n * ```\n */\nexport function createDevToolsHook(): ObservabilityHook {\n const collector = getCollector();\n const traceToSession = new Map<string, string>();\n const eventToRun = new Map<string, string>();\n\n return {\n onTraceStart(trace: TracerTrace) {\n // Start a new session for each trace\n const sessionId = collector.startSession(trace.name);\n traceToSession.set(trace.id, sessionId);\n },\n\n onTraceEnd(trace: TracerTrace) {\n const sessionId = traceToSession.get(trace.id);\n if (sessionId) {\n collector.endSession(sessionId);\n traceToSession.delete(trace.id);\n }\n },\n\n onEvent(event: TracerEvent) {\n const runType = mapEventType(event.type);\n \n // Build metadata\n const metadata: TraceMetadata = {\n ...event.metadata,\n };\n\n if (event.usage) {\n metadata.promptTokens = event.usage.promptTokens;\n metadata.completionTokens = event.usage.completionTokens;\n metadata.totalTokens = event.usage.totalTokens;\n }\n\n // If event has both start and end time, create a completed run\n if (event.startTime && event.endTime) {\n const runId = collector.startRun(runType, event.name, event.input, metadata);\n collector.endRun(runId, event.output, {\n ...metadata,\n // Override latency calculation since we have actual times\n });\n eventToRun.set(event.id, runId);\n } else if (event.startTime && !event.endTime) {\n // Event is starting\n const runId = collector.startRun(runType, event.name, event.input, metadata);\n eventToRun.set(event.id, runId);\n } else {\n // Event is ending (find matching start)\n const runId = eventToRun.get(event.id);\n if (runId) {\n collector.endRun(runId, event.output, metadata);\n eventToRun.delete(event.id);\n }\n }\n },\n\n onError(error: Error, context?: Record<string, unknown>) {\n // Find the most recent run and mark it as errored\n const sessions = collector.getSessions();\n if (sessions.length > 0) {\n const lastSession = sessions[sessions.length - 1];\n if (lastSession.runs.length > 0) {\n const findRunningRun = (runs: Array<{ id: string; status: string; children: unknown[] }>): string | undefined => {\n for (const run of runs) {\n if (run.status === 'running') return run.id;\n const childRun = findRunningRun(run.children as Array<{ id: string; status: string; children: unknown[] }>);\n if (childRun) return childRun;\n }\n return undefined;\n };\n\n const runningRunId = findRunningRun(lastSession.runs as Array<{ id: string; status: string; children: unknown[] }>);\n if (runningRunId) {\n collector.errorRun(runningRunId, error);\n }\n }\n }\n\n // Log context if provided\n if (context) {\n console.error('[DevTools] Error context:', context);\n }\n },\n };\n}\n\n/**\n * Convenience function to create a Tracer with DevTools integration\n */\nexport function createTracerWithDevTools(options: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n maxTraces?: number;\n traceTtlMs?: number;\n} = {}): {\n hook: ObservabilityHook;\n config: typeof options & { hooks: ObservabilityHook[] };\n} {\n const hook = createDevToolsHook();\n return {\n hook,\n config: {\n ...options,\n hooks: [hook],\n },\n };\n}\n","import type { TraceRun, TraceRunType, TraceEvent } from './types.js';\nimport { getCollector } from './collector.js';\n\n/**\n * OpenTelemetry configuration\n */\nexport interface OpenTelemetryConfig {\n endpoint: string;\n serviceName?: string;\n serviceVersion?: string;\n headers?: Record<string, string>;\n batchSize?: number;\n flushIntervalMs?: number;\n enabled?: boolean;\n}\n\n/**\n * OTLP Span representation\n */\ninterface OTLPSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes: Array<{ key: string; value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean } }>;\n status: { code: number; message?: string };\n}\n\n/**\n * OpenTelemetry Exporter for DevTools traces\n */\nexport class OpenTelemetryExporter {\n private config: Required<OpenTelemetryConfig>;\n private spanBuffer: OTLPSpan[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private unsubscribe?: () => void;\n\n constructor(config: OpenTelemetryConfig) {\n this.config = {\n endpoint: config.endpoint,\n serviceName: config.serviceName ?? 'orkajs-app',\n serviceVersion: config.serviceVersion ?? '1.0.0',\n headers: config.headers ?? {},\n batchSize: config.batchSize ?? 100,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n enabled: config.enabled ?? true,\n };\n }\n\n /**\n * Start the exporter - subscribes to trace events\n */\n start(): void {\n if (!this.config.enabled) return;\n\n const collector = getCollector();\n \n this.unsubscribe = collector.subscribe((event: TraceEvent) => {\n if (event.type === 'run:end' && event.run) {\n this.addSpan(event.run, event.sessionId);\n }\n });\n\n // Start flush timer\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushIntervalMs);\n }\n\n /**\n * Stop the exporter\n */\n async stop(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n // Final flush\n await this.flush();\n }\n\n /**\n * Convert a TraceRun to OTLP span\n */\n private addSpan(run: TraceRun, traceId: string): void {\n const span = this.runToSpan(run, traceId);\n this.spanBuffer.push(span);\n\n // Also add child spans\n for (const child of run.children) {\n this.addSpan(child, traceId);\n }\n\n // Flush if buffer is full\n if (this.spanBuffer.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n /**\n * Convert TraceRun to OTLP Span format\n */\n private runToSpan(run: TraceRun, traceId: string): OTLPSpan {\n const attributes: OTLPSpan['attributes'] = [\n { key: 'orka.run.type', value: { stringValue: run.type } },\n { key: 'orka.run.name', value: { stringValue: run.name } },\n { key: 'orka.run.status', value: { stringValue: run.status } },\n ];\n\n // Add metadata as attributes\n if (run.metadata) {\n if (run.metadata.model) {\n attributes.push({ key: 'llm.model', value: { stringValue: run.metadata.model } });\n }\n if (run.metadata.provider) {\n attributes.push({ key: 'llm.provider', value: { stringValue: run.metadata.provider } });\n }\n if (run.metadata.totalTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.total', value: { intValue: String(run.metadata.totalTokens) } });\n }\n if (run.metadata.promptTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.prompt', value: { intValue: String(run.metadata.promptTokens) } });\n }\n if (run.metadata.completionTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.completion', value: { intValue: String(run.metadata.completionTokens) } });\n }\n if (run.metadata.cost !== undefined) {\n attributes.push({ key: 'llm.cost', value: { doubleValue: run.metadata.cost } });\n }\n if (run.metadata.toolName) {\n attributes.push({ key: 'tool.name', value: { stringValue: run.metadata.toolName } });\n }\n }\n\n // Add latency\n if (run.latencyMs !== undefined) {\n attributes.push({ key: 'orka.latency_ms', value: { intValue: String(run.latencyMs) } });\n }\n\n // Add error if present\n if (run.error) {\n attributes.push({ key: 'error.message', value: { stringValue: run.error } });\n }\n\n return {\n traceId: this.toHex(traceId, 32),\n spanId: this.toHex(run.id, 16),\n parentSpanId: run.parentId ? this.toHex(run.parentId, 16) : undefined,\n name: `${run.type}/${run.name}`,\n kind: this.getSpanKind(run.type),\n startTimeUnixNano: String(run.startTime * 1_000_000),\n endTimeUnixNano: String((run.endTime ?? run.startTime) * 1_000_000),\n attributes,\n status: {\n code: run.status === 'error' ? 2 : 1,\n message: run.error,\n },\n };\n }\n\n /**\n * Get OTLP span kind based on run type\n */\n private getSpanKind(type: TraceRunType): number {\n switch (type) {\n case 'llm':\n case 'embedding':\n return 3; // CLIENT\n case 'tool':\n return 3; // CLIENT\n case 'agent':\n case 'chain':\n case 'workflow':\n case 'graph':\n return 0; // INTERNAL\n default:\n return 0; // INTERNAL\n }\n }\n\n /**\n * Convert string ID to hex format\n */\n private toHex(id: string, length: number): string {\n // Simple hash to hex conversion\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n const char = id.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n const hex = Math.abs(hash).toString(16).padStart(length, '0');\n return hex.slice(0, length);\n }\n\n /**\n * Flush spans to OTLP endpoint\n */\n async flush(): Promise<void> {\n if (this.spanBuffer.length === 0) return;\n\n const spans = [...this.spanBuffer];\n this.spanBuffer = [];\n\n const payload = {\n resourceSpans: [{\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: this.config.serviceName } },\n { key: 'service.version', value: { stringValue: this.config.serviceVersion } },\n { key: 'telemetry.sdk.name', value: { stringValue: '@orka-js/devtools' } },\n { key: 'telemetry.sdk.version', value: { stringValue: '1.1.0' } },\n ],\n },\n scopeSpans: [{\n scope: {\n name: '@orka-js/devtools',\n version: '1.1.0',\n },\n spans,\n }],\n }],\n };\n\n try {\n const response = await fetch(`${this.config.endpoint}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n console.error(`[DevTools] OTLP export failed: ${response.status} ${response.statusText}`);\n // Re-add spans to buffer for retry\n this.spanBuffer.unshift(...spans);\n }\n } catch (error) {\n console.error('[DevTools] OTLP export error:', error);\n // Re-add spans to buffer for retry\n this.spanBuffer.unshift(...spans);\n }\n }\n}\n\n/**\n * Create and start an OpenTelemetry exporter\n */\nexport function createOTLPExporter(config: OpenTelemetryConfig): OpenTelemetryExporter {\n const exporter = new OpenTelemetryExporter(config);\n exporter.start();\n return exporter;\n}\n","import type { TraceRun, ReplayOptions, ReplayResult } from './types.js';\nimport { getCollector } from './collector.js';\n\n/**\n * Replay Debugger - Replay traces with modified inputs\n */\nexport class ReplayDebugger {\n /**\n * Replay a trace run with optionally modified input\n */\n async replay(options: ReplayOptions): Promise<ReplayResult> {\n const collector = getCollector();\n const originalRun = collector.findRun(options.runId, options.sessionId);\n\n if (!originalRun) {\n throw new Error(`Run not found: ${options.runId}`);\n }\n\n // Get the modified input\n const modifiedInput = options.modifyInput \n ? options.modifyInput(originalRun.input)\n : originalRun.input;\n\n // Create a new session for the replay\n const replaySessionId = collector.startSession(`Replay: ${originalRun.name}`);\n\n // Start the replayed run\n const replayRunId = collector.startRun(\n originalRun.type,\n `replay:${originalRun.name}`,\n modifiedInput,\n {\n ...originalRun.metadata,\n replayOf: originalRun.id,\n originalSessionId: options.sessionId,\n }\n );\n\n // For now, we just copy the output (actual replay would re-execute)\n // In a real implementation, this would call the original function\n const replayedOutput = originalRun.output;\n\n collector.endRun(replayRunId, replayedOutput, originalRun.metadata);\n collector.endSession(replaySessionId);\n\n // Get the replayed run\n const replayedRun = collector.findRun(replayRunId);\n\n if (!replayedRun) {\n throw new Error('Failed to create replayed run');\n }\n\n // Calculate diff\n const inputChanged = JSON.stringify(originalRun.input) !== JSON.stringify(modifiedInput);\n const outputChanged = JSON.stringify(originalRun.output) !== JSON.stringify(replayedRun.output);\n const latencyDiff = (replayedRun.latencyMs ?? 0) - (originalRun.latencyMs ?? 0);\n\n return {\n originalRun,\n replayedRun,\n diff: {\n inputChanged,\n outputChanged,\n latencyDiff,\n },\n };\n }\n\n /**\n * Fork a trace to create a new branch for experimentation\n */\n fork(runId: string, sessionId?: string): string {\n const collector = getCollector();\n const originalRun = collector.findRun(runId, sessionId);\n\n if (!originalRun) {\n throw new Error(`Run not found: ${runId}`);\n }\n\n // Create a new session for the fork\n const forkSessionId = collector.startSession(`Fork: ${originalRun.name}`);\n\n // Deep clone the run tree\n this.cloneRunTree(originalRun, forkSessionId);\n\n collector.endSession(forkSessionId);\n\n return forkSessionId;\n }\n\n /**\n * Clone a run and its children\n */\n private cloneRunTree(run: TraceRun, _sessionId: string, parentId?: string): string {\n const collector = getCollector();\n\n const newRunId = collector.startRun(\n run.type,\n `fork:${run.name}`,\n run.input,\n {\n ...run.metadata,\n forkedFrom: run.id,\n originalParentId: parentId,\n }\n );\n\n // Clone children\n for (const child of run.children) {\n this.cloneRunTree(child, _sessionId, newRunId);\n }\n\n // End the run with original output\n if (run.status === 'error') {\n collector.errorRun(newRunId, run.error ?? 'Unknown error');\n } else {\n collector.endRun(newRunId, run.output, run.metadata);\n }\n\n return newRunId;\n }\n\n /**\n * Compare two runs and return detailed diff\n */\n compare(runId1: string, runId2: string, sessionId?: string): RunComparison {\n const collector = getCollector();\n const run1 = collector.findRun(runId1, sessionId);\n const run2 = collector.findRun(runId2, sessionId);\n\n if (!run1 || !run2) {\n throw new Error('One or both runs not found');\n }\n\n return {\n run1: {\n id: run1.id,\n name: run1.name,\n type: run1.type,\n latencyMs: run1.latencyMs,\n status: run1.status,\n tokenCount: run1.metadata?.totalTokens as number | undefined,\n },\n run2: {\n id: run2.id,\n name: run2.name,\n type: run2.type,\n latencyMs: run2.latencyMs,\n status: run2.status,\n tokenCount: run2.metadata?.totalTokens as number | undefined,\n },\n diff: {\n latencyDiff: (run2.latencyMs ?? 0) - (run1.latencyMs ?? 0),\n latencyDiffPercent: run1.latencyMs \n ? ((run2.latencyMs ?? 0) - run1.latencyMs) / run1.latencyMs * 100 \n : 0,\n tokenDiff: ((run2.metadata?.totalTokens as number) ?? 0) - ((run1.metadata?.totalTokens as number) ?? 0),\n statusChanged: run1.status !== run2.status,\n inputChanged: JSON.stringify(run1.input) !== JSON.stringify(run2.input),\n outputChanged: JSON.stringify(run1.output) !== JSON.stringify(run2.output),\n },\n };\n }\n\n /**\n * Export a run as a reproducible test case\n */\n exportTestCase(runId: string, sessionId?: string): TestCase {\n const collector = getCollector();\n const run = collector.findRun(runId, sessionId);\n\n if (!run) {\n throw new Error(`Run not found: ${runId}`);\n }\n\n return {\n name: `test_${run.name}_${run.id.slice(0, 8)}`,\n description: `Exported from DevTools trace ${run.id}`,\n type: run.type,\n input: run.input,\n expectedOutput: run.output,\n metadata: run.metadata,\n assertions: [\n { type: 'status', expected: run.status },\n { type: 'latency_max', expected: (run.latencyMs ?? 0) * 1.5 },\n ],\n createdAt: new Date().toISOString(),\n };\n }\n}\n\n/**\n * Run comparison result\n */\nexport interface RunComparison {\n run1: RunSummary;\n run2: RunSummary;\n diff: {\n latencyDiff: number;\n latencyDiffPercent: number;\n tokenDiff: number;\n statusChanged: boolean;\n inputChanged: boolean;\n outputChanged: boolean;\n };\n}\n\ninterface RunSummary {\n id: string;\n name: string;\n type: string;\n latencyMs?: number;\n status: string;\n tokenCount?: number;\n}\n\n/**\n * Exported test case\n */\nexport interface TestCase {\n name: string;\n description: string;\n type: string;\n input: unknown;\n expectedOutput: unknown;\n metadata?: Record<string, unknown>;\n assertions: Array<{\n type: string;\n expected: unknown;\n }>;\n createdAt: string;\n}\n\n/**\n * Create a replay debugger instance\n */\nexport function createReplayDebugger(): ReplayDebugger {\n return new ReplayDebugger();\n}\n\n// Singleton instance\nlet replayDebugger: ReplayDebugger | undefined;\n\n/**\n * Get the global replay debugger instance\n */\nexport function getReplayDebugger(): ReplayDebugger {\n if (!replayDebugger) {\n replayDebugger = new ReplayDebugger();\n }\n return replayDebugger;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA2B;AAcpB,IAAM,iBAAN,MAAqB;AAAA,EAClB,WAAsC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA,WAAoC,oBAAI,IAAI;AAAA,EAC5C;AAAA,EACA;AAAA,EACA,YAA8C,oBAAI,IAAI;AAAA,EAE9D,YAAY,SAAyB,CAAC,GAAG;AACvC,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,cAAc,OAAO,eAAe,KAAK,KAAK,KAAK;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,MAAuB;AAClC,UAAM,gBAAY,wBAAW;AAC7B,UAAM,UAAwB;AAAA,MAC5B,IAAI;AAAA,MACJ,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO,CAAC;AAAA,MAC/C,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM,CAAC;AAAA,IACT;AAEA,SAAK,SAAS,IAAI,WAAW,OAAO;AACpC,SAAK,kBAAkB;AACvB,SAAK,SAAS,IAAI,WAAW,CAAC,CAAC;AAE/B,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,IACF,CAAC;AAED,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAA0B;AACnC,UAAM,KAAK,aAAa,KAAK;AAC7B,QAAI,CAAC,GAAI;AAET,UAAM,UAAU,KAAK,SAAS,IAAI,EAAE;AACpC,QAAI,SAAS;AACX,cAAQ,UAAU,KAAK,IAAI;AAC3B,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,WAAW,KAAK,IAAI;AAAA,QACpB,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,oBAAoB,IAAI;AAC/B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SACE,MACA,MACA,OACA,UACQ;AACR,UAAM,YAAY,KAAK,mBAAmB,KAAK,aAAa;AAC5D,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AAEzC,UAAM,MAAgB;AAAA,MACpB,QAAI,wBAAW;AAAA,MACf,UAAU,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,EAAE,KAAK;AAAA,MAC1D;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAGA,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,MAAM,SAAS,CAAC,EAAE,SAAS,KAAK,GAAG;AAAA,IAC3C,OAAO;AACL,cAAQ,KAAK,KAAK,GAAG;AAAA,IACvB;AAEA,UAAM,KAAK,GAAG;AAEd,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAAe,QAAkB,UAAgC;AACtE,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAW,MAAM,UAAU,OAAK,EAAE,OAAO,KAAK;AACpD,QAAI,aAAa,GAAI;AAErB,UAAM,MAAM,MAAM,QAAQ;AAC1B,QAAI,UAAU,KAAK,IAAI;AACvB,QAAI,YAAY,IAAI,UAAU,IAAI;AAClC,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,QAAI,UAAU;AACZ,UAAI,WAAW,EAAE,GAAG,IAAI,UAAU,GAAG,SAAS;AAAA,IAChD;AAGA,UAAM,OAAO,UAAU,CAAC;AAExB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,OAAe,OAA6B;AACnD,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAW,MAAM,UAAU,OAAK,EAAE,OAAO,KAAK;AACpD,QAAI,aAAa,GAAI;AAErB,UAAM,MAAM,MAAM,QAAQ;AAC1B,QAAI,UAAU,KAAK,IAAI;AACvB,QAAI,YAAY,IAAI,UAAU,IAAI;AAClC,QAAI,SAAS;AACb,QAAI,QAAQ,iBAAiB,QAAQ,MAAM,UAAU;AAErD,UAAM,OAAO,UAAU,CAAC;AAExB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,MACA,OAAO,IAAI;AAAA,IACb,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,cAA8B;AAC5B,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAA6C;AACtD,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAAkC;AAC3C,UAAM,WAAW,YACb,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,EAAE,OAAO,OAAO,IAC7C,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAErC,UAAM,UAAwB;AAAA,MAC5B,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,aAAa;AAAA,MACb,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAY,CAAC;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,aAAa,CAAC;AAAA,IAChB;AAEA,QAAI,aAAa;AAEjB,UAAM,aAAa,CAAC,QAAkB;AACpC,cAAQ;AACR,cAAQ,kBAAkB,IAAI,aAAa;AAE3C,UAAI,IAAI,WAAW,QAAS;AAG5B,cAAQ,WAAW,IAAI,IAAI,KAAK,QAAQ,WAAW,IAAI,IAAI,KAAK,KAAK;AAGrE,UAAI,IAAI,UAAU;AAChB,cAAM,EAAE,aAAa,MAAM,MAAM,IAAI,IAAI;AACzC,YAAI,aAAa;AACf,kBAAQ,eAAe;AACvB,cAAI,OAAO;AACT,oBAAQ,cAAc,KAAK,KAAK,QAAQ,cAAc,KAAK,KAAK,KAAK;AAAA,UACvE;AAAA,QACF;AACA,YAAI,MAAM;AACR,kBAAQ,aAAa;AACrB,cAAI,OAAO;AACT,oBAAQ,YAAY,KAAK,KAAK,QAAQ,YAAY,KAAK,KAAK,KAAK;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,SAAS,IAAI,UAAU;AAChC,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,iBAAW,OAAO,QAAQ,MAAM;AAC9B,mBAAW,GAAG;AAAA,MAChB;AAAA,IACF;AAEA,YAAQ,eAAe,QAAQ,YAAY,IACvC,QAAQ,iBAAiB,QAAQ,YACjC;AACJ,YAAQ,YAAY,QAAQ,YAAY,IACpC,aAAa,QAAQ,YACrB;AAEJ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,OAAe,WAA0C;AAC/D,UAAM,WAAW,YACb,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,EAAE,OAAO,OAAO,IAC7C,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAErC,UAAM,aAAa,CAAC,SAA2C;AAC7D,iBAAW,OAAO,MAAM;AACtB,YAAI,IAAI,OAAO,MAAO,QAAO;AAC7B,cAAM,QAAQ,WAAW,IAAI,QAAQ;AACrC,YAAI,MAAO,QAAO;AAAA,MACpB;AACA,aAAO;AAAA,IACT;AAEA,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAI,MAAO,QAAO;AAAA,IACpB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,UAAmD;AAC3D,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKQ,KAAK,OAAyB;AACpC,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI;AACF,iBAAS,KAAK;AAAA,MAChB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAgB;AACtB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,KAAK;AAE1B,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,UAAU;AACzC,UAAI,QAAQ,WAAW,QAAQ,UAAU,QAAQ;AAC/C,aAAK,SAAS,OAAO,EAAE;AACvB,aAAK,SAAS,OAAO,EAAE;AAAA,MACzB;AAAA,IACF;AAGA,QAAI,KAAK,SAAS,OAAO,KAAK,WAAW;AACvC,YAAM,SAAS,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAC9C,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS;AAEjD,YAAM,WAAW,OAAO,MAAM,GAAG,KAAK,SAAS,OAAO,KAAK,SAAS;AACpE,iBAAW,CAAC,EAAE,KAAK,UAAU;AAC3B,aAAK,SAAS,OAAO,EAAE;AACvB,aAAK,SAAS,OAAO,EAAE;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,MAAM;AACpB,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAiB;AACf,WAAO,KAAK,UAAU;AAAA,MACpB,UAAU,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,MAC3C,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC,GAAG,MAAM,CAAC;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAoB;AACzB,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,eAAW,WAAW,KAAK,UAAU;AACnC,WAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;AAAA,IACvC;AAAA,EACF;AACF;AAGA,IAAI;AAEG,SAAS,aAAa,QAAyC;AACpE,MAAI,CAAC,iBAAiB;AACpB,sBAAkB,IAAI,eAAe,MAAM;AAAA,EAC7C;AACA,SAAO;AACT;AAEO,SAAS,iBAAuB;AACrC,oBAAkB;AACpB;;;ACtXO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAA+B,oBAAI,IAAI;AAAA,EAE/C,YAAY,WAA2B,SAAyB,CAAC,GAAG;AAClE,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,MACZ,MAAM,OAAO,QAAQ;AAAA,MACrB,MAAM,OAAO,QAAQ;AAAA,MACrB,MAAM,OAAO,QAAQ;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAE3B,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,sBAAgB,MAAM,OAAO,SAAS;AACtC,mBAAa,MAAM,OAAO,MAAM;AAAA,IAClC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,cAAc,QAAQ;AAGlC,QAAI,KAAK,OAAO,MAAM;AACpB,UAAI,IAAI,CAAC,MAAe,KAAe,SAAuB;AAC5D,YAAI,OAAO,+BAA+B,GAAG;AAC7C,YAAI,OAAO,gCAAgC,cAAc;AACzD,YAAI,OAAO,gCAAgC,mBAAmB;AAC9D,aAAK;AAAA,MACP,CAAC;AAAA,IACH;AAEA,QAAI,IAAI,cAAc,KAAK,CAAyE;AAGpG,SAAK,YAAY,GAAG;AAGpB,SAAK,SAAS,WAAW,aAAa,GAAG;AAGzC,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,WAAK,OAAQ,OAAO,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,MAAM;AAC5D,gBAAQ,IAAI;AAAA,8CAA0C,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI;AAAA,CAAI;AAC9F,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,UAAU;AAClC,WAAK,eAAe,KAAK;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAK,OAAQ,MAAM,CAAC,QAAQ;AAC1B,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,SAAQ;AAAA,QACf,CAAC;AAAA,MACH,CAAC;AACD,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,KAAwB;AAE1C,QAAI,IAAI,eAAe,CAAC,MAAe,QAAkB;AACvD,UAAI,KAAK,EAAE,QAAQ,MAAM,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IAClD,CAAC;AAGD,QAAI,IAAI,iBAAiB,CAAC,MAAe,QAAkB;AACzD,YAAM,WAAW,KAAK,UAAU,YAAY;AAC5C,UAAI,KAAK,QAAQ;AAAA,IACnB,CAAC;AAGD,QAAI,IAAI,qBAAqB,CAAC,KAAc,QAAkB;AAC5D,YAAM,UAAU,KAAK,UAAU,WAAW,IAAI,OAAO,EAAE;AACvD,UAAI,CAAC,SAAS;AACZ,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,UAAI,KAAK,OAAO;AAAA,IAClB,CAAC;AAGD,QAAI,IAAI,gBAAgB,CAAC,KAAc,QAAkB;AACvD,YAAM,YAAY,IAAI,MAAM;AAC5B,YAAM,UAAU,KAAK,UAAU,WAAW,SAAS;AACnD,UAAI,KAAK,OAAO;AAAA,IAClB,CAAC;AAGD,QAAI,IAAI,iBAAiB,CAAC,KAAc,QAAkB;AACxD,YAAM,YAAY,IAAI,MAAM;AAC5B,YAAM,MAAM,KAAK,UAAU,QAAQ,IAAI,OAAO,IAAI,SAAS;AAC3D,UAAI,CAAC,KAAK;AACR,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,MACF;AACA,UAAI,KAAK,GAAG;AAAA,IACd,CAAC;AAGD,QAAI,OAAO,iBAAiB,CAAC,MAAe,QAAkB;AAC5D,WAAK,UAAU,MAAM;AACrB,UAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,IAC5B,CAAC;AAGD,QAAI,IAAI,eAAe,CAAC,MAAe,QAAkB;AACvD,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,UAAI,UAAU,gBAAgB,kBAAkB;AAChD,UAAI,UAAU,uBAAuB,uCAAuC;AAC5E,UAAI,KAAK,IAAI;AAAA,IACf,CAAC;AAGD,QAAI,KAAK,eAAe,CAAC,KAAc,QAAkB;AACvD,UAAI;AACF,aAAK,UAAU,OAAO,KAAK,UAAU,IAAI,IAAI,CAAC;AAC9C,YAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,MAC5B,QAAQ;AACN,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAAA,MACtD;AAAA,IACF,CAAC;AAGD,QAAI,IAAI,eAAe,CAAC,KAAc,QAAkB;AACtD,UAAI,UAAU,gBAAgB,mBAAmB;AACjD,UAAI,UAAU,iBAAiB,UAAU;AACzC,UAAI,UAAU,cAAc,YAAY;AAExC,WAAK,QAAQ,IAAI,GAAgC;AAEjD,UAAI,GAAG,SAAS,MAAM;AACpB,aAAK,QAAQ,OAAO,GAAgC;AAAA,MACtD,CAAC;AAAA,IACH,CAAC;AAGD,QAAI,IAAI,KAAK,CAAC,MAAe,QAAkB;AAC7C,UAAI,KAAK,KAAK,iBAAiB,CAAC;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAyB;AAC9C,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,eAAW,UAAU,KAAK,SAAS;AACjC,aAAO,MAAM,SAAS,IAAI;AAAA;AAAA,CAAM;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAA2B;AACjC,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2KT;AACF;;;AC5TA,SAAS,aAAa,MAA4B;AAChD,QAAM,UAAwC;AAAA,IAC5C,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS;AAAA,IACT,cAAc;AAAA,IACd,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACA,SAAO,QAAQ,KAAK,YAAY,CAAC,KAAK;AACxC;AAiBO,SAAS,qBAAwC;AACtD,QAAM,YAAY,aAAa;AAC/B,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,QAAM,aAAa,oBAAI,IAAoB;AAE3C,SAAO;AAAA,IACL,aAAaA,QAAoB;AAE/B,YAAM,YAAY,UAAU,aAAaA,OAAM,IAAI;AACnD,qBAAe,IAAIA,OAAM,IAAI,SAAS;AAAA,IACxC;AAAA,IAEA,WAAWA,QAAoB;AAC7B,YAAM,YAAY,eAAe,IAAIA,OAAM,EAAE;AAC7C,UAAI,WAAW;AACb,kBAAU,WAAW,SAAS;AAC9B,uBAAe,OAAOA,OAAM,EAAE;AAAA,MAChC;AAAA,IACF;AAAA,IAEA,QAAQ,OAAoB;AAC1B,YAAM,UAAU,aAAa,MAAM,IAAI;AAGvC,YAAM,WAA0B;AAAA,QAC9B,GAAG,MAAM;AAAA,MACX;AAEA,UAAI,MAAM,OAAO;AACf,iBAAS,eAAe,MAAM,MAAM;AACpC,iBAAS,mBAAmB,MAAM,MAAM;AACxC,iBAAS,cAAc,MAAM,MAAM;AAAA,MACrC;AAGA,UAAI,MAAM,aAAa,MAAM,SAAS;AACpC,cAAM,QAAQ,UAAU,SAAS,SAAS,MAAM,MAAM,MAAM,OAAO,QAAQ;AAC3E,kBAAU,OAAO,OAAO,MAAM,QAAQ;AAAA,UACpC,GAAG;AAAA;AAAA,QAEL,CAAC;AACD,mBAAW,IAAI,MAAM,IAAI,KAAK;AAAA,MAChC,WAAW,MAAM,aAAa,CAAC,MAAM,SAAS;AAE5C,cAAM,QAAQ,UAAU,SAAS,SAAS,MAAM,MAAM,MAAM,OAAO,QAAQ;AAC3E,mBAAW,IAAI,MAAM,IAAI,KAAK;AAAA,MAChC,OAAO;AAEL,cAAM,QAAQ,WAAW,IAAI,MAAM,EAAE;AACrC,YAAI,OAAO;AACT,oBAAU,OAAO,OAAO,MAAM,QAAQ,QAAQ;AAC9C,qBAAW,OAAO,MAAM,EAAE;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,IAEA,QAAQ,OAAc,SAAmC;AAEvD,YAAM,WAAW,UAAU,YAAY;AACvC,UAAI,SAAS,SAAS,GAAG;AACvB,cAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,YAAI,YAAY,KAAK,SAAS,GAAG;AAC/B,gBAAM,iBAAiB,CAAC,SAAyF;AAC/G,uBAAW,OAAO,MAAM;AACtB,kBAAI,IAAI,WAAW,UAAW,QAAO,IAAI;AACzC,oBAAM,WAAW,eAAe,IAAI,QAAsE;AAC1G,kBAAI,SAAU,QAAO;AAAA,YACvB;AACA,mBAAO;AAAA,UACT;AAEA,gBAAM,eAAe,eAAe,YAAY,IAAkE;AAClH,cAAI,cAAc;AAChB,sBAAU,SAAS,cAAc,KAAK;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAGA,UAAI,SAAS;AACX,gBAAQ,MAAM,6BAA6B,OAAO;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,yBAAyB,UAIrC,CAAC,GAGH;AACA,QAAM,OAAO,mBAAmB;AAChC,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,MACN,GAAG;AAAA,MACH,OAAO,CAAC,IAAI;AAAA,IACd;AAAA,EACF;AACF;;;ACrJO,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EACA,aAAyB,CAAC;AAAA,EAC1B;AAAA,EACA;AAAA,EAER,YAAY,QAA6B;AACvC,SAAK,SAAS;AAAA,MACZ,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO,eAAe;AAAA,MACnC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,SAAS,OAAO,WAAW,CAAC;AAAA,MAC5B,WAAW,OAAO,aAAa;AAAA,MAC/B,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,SAAS,OAAO,WAAW;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,UAAM,YAAY,aAAa;AAE/B,SAAK,cAAc,UAAU,UAAU,CAAC,UAAsB;AAC5D,UAAI,MAAM,SAAS,aAAa,MAAM,KAAK;AACzC,aAAK,QAAQ,MAAM,KAAK,MAAM,SAAS;AAAA,MACzC;AAAA,IACF,CAAC;AAGD,SAAK,aAAa,YAAY,MAAM;AAClC,WAAK,MAAM;AAAA,IACb,GAAG,KAAK,OAAO,eAAe;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AAEA,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY;AACjB,WAAK,cAAc;AAAA,IACrB;AAGA,UAAM,KAAK,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQ,KAAe,SAAuB;AACpD,UAAM,OAAO,KAAK,UAAU,KAAK,OAAO;AACxC,SAAK,WAAW,KAAK,IAAI;AAGzB,eAAW,SAAS,IAAI,UAAU;AAChC,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAGA,QAAI,KAAK,WAAW,UAAU,KAAK,OAAO,WAAW;AACnD,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU,KAAe,SAA2B;AAC1D,UAAM,aAAqC;AAAA,MACzC,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE;AAAA,MACzD,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE;AAAA,MACzD,EAAE,KAAK,mBAAmB,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE;AAAA,IAC/D;AAGA,QAAI,IAAI,UAAU;AAChB,UAAI,IAAI,SAAS,OAAO;AACtB,mBAAW,KAAK,EAAE,KAAK,aAAa,OAAO,EAAE,aAAa,IAAI,SAAS,MAAM,EAAE,CAAC;AAAA,MAClF;AACA,UAAI,IAAI,SAAS,UAAU;AACzB,mBAAW,KAAK,EAAE,KAAK,gBAAgB,OAAO,EAAE,aAAa,IAAI,SAAS,SAAS,EAAE,CAAC;AAAA,MACxF;AACA,UAAI,IAAI,SAAS,gBAAgB,QAAW;AAC1C,mBAAW,KAAK,EAAE,KAAK,oBAAoB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,WAAW,EAAE,EAAE,CAAC;AAAA,MACpG;AACA,UAAI,IAAI,SAAS,iBAAiB,QAAW;AAC3C,mBAAW,KAAK,EAAE,KAAK,qBAAqB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,YAAY,EAAE,EAAE,CAAC;AAAA,MACtG;AACA,UAAI,IAAI,SAAS,qBAAqB,QAAW;AAC/C,mBAAW,KAAK,EAAE,KAAK,yBAAyB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,gBAAgB,EAAE,EAAE,CAAC;AAAA,MAC9G;AACA,UAAI,IAAI,SAAS,SAAS,QAAW;AACnC,mBAAW,KAAK,EAAE,KAAK,YAAY,OAAO,EAAE,aAAa,IAAI,SAAS,KAAK,EAAE,CAAC;AAAA,MAChF;AACA,UAAI,IAAI,SAAS,UAAU;AACzB,mBAAW,KAAK,EAAE,KAAK,aAAa,OAAO,EAAE,aAAa,IAAI,SAAS,SAAS,EAAE,CAAC;AAAA,MACrF;AAAA,IACF;AAGA,QAAI,IAAI,cAAc,QAAW;AAC/B,iBAAW,KAAK,EAAE,KAAK,mBAAmB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,EAAE,EAAE,CAAC;AAAA,IACxF;AAGA,QAAI,IAAI,OAAO;AACb,iBAAW,KAAK,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,MAAM,EAAE,CAAC;AAAA,IAC7E;AAEA,WAAO;AAAA,MACL,SAAS,KAAK,MAAM,SAAS,EAAE;AAAA,MAC/B,QAAQ,KAAK,MAAM,IAAI,IAAI,EAAE;AAAA,MAC7B,cAAc,IAAI,WAAW,KAAK,MAAM,IAAI,UAAU,EAAE,IAAI;AAAA,MAC5D,MAAM,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC7B,MAAM,KAAK,YAAY,IAAI,IAAI;AAAA,MAC/B,mBAAmB,OAAO,IAAI,YAAY,GAAS;AAAA,MACnD,iBAAiB,QAAQ,IAAI,WAAW,IAAI,aAAa,GAAS;AAAA,MAClE;AAAA,MACA,QAAQ;AAAA,QACN,MAAM,IAAI,WAAW,UAAU,IAAI;AAAA,QACnC,SAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAA4B;AAC9C,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,MAAM,IAAY,QAAwB;AAEhD,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,YAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,cAAS,QAAQ,KAAK,OAAQ;AAC9B,aAAO,OAAO;AAAA,IAChB;AACA,UAAM,MAAM,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,QAAQ,GAAG;AAC5D,WAAO,IAAI,MAAM,GAAG,MAAM;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,WAAW,WAAW,EAAG;AAElC,UAAM,QAAQ,CAAC,GAAG,KAAK,UAAU;AACjC,SAAK,aAAa,CAAC;AAEnB,UAAM,UAAU;AAAA,MACd,eAAe,CAAC;AAAA,QACd,UAAU;AAAA,UACR,YAAY;AAAA,YACV,EAAE,KAAK,gBAAgB,OAAO,EAAE,aAAa,KAAK,OAAO,YAAY,EAAE;AAAA,YACvE,EAAE,KAAK,mBAAmB,OAAO,EAAE,aAAa,KAAK,OAAO,eAAe,EAAE;AAAA,YAC7E,EAAE,KAAK,sBAAsB,OAAO,EAAE,aAAa,oBAAoB,EAAE;AAAA,YACzE,EAAE,KAAK,yBAAyB,OAAO,EAAE,aAAa,QAAQ,EAAE;AAAA,UAClE;AAAA,QACF;AAAA,QACA,YAAY,CAAC;AAAA,UACX,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAG,KAAK,OAAO;AAAA,QACjB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,gBAAQ,MAAM,kCAAkC,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAExF,aAAK,WAAW,QAAQ,GAAG,KAAK;AAAA,MAClC;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,iCAAiC,KAAK;AAEpD,WAAK,WAAW,QAAQ,GAAG,KAAK;AAAA,IAClC;AAAA,EACF;AACF;AAKO,SAAS,mBAAmB,QAAoD;AACrF,QAAM,WAAW,IAAI,sBAAsB,MAAM;AACjD,WAAS,MAAM;AACf,SAAO;AACT;;;ACjQO,IAAM,iBAAN,MAAqB;AAAA;AAAA;AAAA;AAAA,EAI1B,MAAM,OAAO,SAA+C;AAC1D,UAAM,YAAY,aAAa;AAC/B,UAAM,cAAc,UAAU,QAAQ,QAAQ,OAAO,QAAQ,SAAS;AAEtE,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,kBAAkB,QAAQ,KAAK,EAAE;AAAA,IACnD;AAGA,UAAM,gBAAgB,QAAQ,cAC1B,QAAQ,YAAY,YAAY,KAAK,IACrC,YAAY;AAGhB,UAAM,kBAAkB,UAAU,aAAa,WAAW,YAAY,IAAI,EAAE;AAG5E,UAAM,cAAc,UAAU;AAAA,MAC5B,YAAY;AAAA,MACZ,UAAU,YAAY,IAAI;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,GAAG,YAAY;AAAA,QACf,UAAU,YAAY;AAAA,QACtB,mBAAmB,QAAQ;AAAA,MAC7B;AAAA,IACF;AAIA,UAAM,iBAAiB,YAAY;AAEnC,cAAU,OAAO,aAAa,gBAAgB,YAAY,QAAQ;AAClE,cAAU,WAAW,eAAe;AAGpC,UAAM,cAAc,UAAU,QAAQ,WAAW;AAEjD,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAGA,UAAM,eAAe,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,UAAU,aAAa;AACvF,UAAM,gBAAgB,KAAK,UAAU,YAAY,MAAM,MAAM,KAAK,UAAU,YAAY,MAAM;AAC9F,UAAM,eAAe,YAAY,aAAa,MAAM,YAAY,aAAa;AAE7E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,OAAe,WAA4B;AAC9C,UAAM,YAAY,aAAa;AAC/B,UAAM,cAAc,UAAU,QAAQ,OAAO,SAAS;AAEtD,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,kBAAkB,KAAK,EAAE;AAAA,IAC3C;AAGA,UAAM,gBAAgB,UAAU,aAAa,SAAS,YAAY,IAAI,EAAE;AAGxE,SAAK,aAAa,aAAa,aAAa;AAE5C,cAAU,WAAW,aAAa;AAElC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,KAAe,YAAoB,UAA2B;AACjF,UAAM,YAAY,aAAa;AAE/B,UAAM,WAAW,UAAU;AAAA,MACzB,IAAI;AAAA,MACJ,QAAQ,IAAI,IAAI;AAAA,MAChB,IAAI;AAAA,MACJ;AAAA,QACE,GAAG,IAAI;AAAA,QACP,YAAY,IAAI;AAAA,QAChB,kBAAkB;AAAA,MACpB;AAAA,IACF;AAGA,eAAW,SAAS,IAAI,UAAU;AAChC,WAAK,aAAa,OAAO,YAAY,QAAQ;AAAA,IAC/C;AAGA,QAAI,IAAI,WAAW,SAAS;AAC1B,gBAAU,SAAS,UAAU,IAAI,SAAS,eAAe;AAAA,IAC3D,OAAO;AACL,gBAAU,OAAO,UAAU,IAAI,QAAQ,IAAI,QAAQ;AAAA,IACrD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,QAAgB,QAAgB,WAAmC;AACzE,UAAM,YAAY,aAAa;AAC/B,UAAM,OAAO,UAAU,QAAQ,QAAQ,SAAS;AAChD,UAAM,OAAO,UAAU,QAAQ,QAAQ,SAAS;AAEhD,QAAI,CAAC,QAAQ,CAAC,MAAM;AAClB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,YAAY,KAAK,UAAU;AAAA,MAC7B;AAAA,MACA,MAAM;AAAA,QACJ,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,YAAY,KAAK,UAAU;AAAA,MAC7B;AAAA,MACA,MAAM;AAAA,QACJ,cAAc,KAAK,aAAa,MAAM,KAAK,aAAa;AAAA,QACxD,oBAAoB,KAAK,cACnB,KAAK,aAAa,KAAK,KAAK,aAAa,KAAK,YAAY,MAC5D;AAAA,QACJ,YAAa,KAAK,UAAU,eAA0B,MAAO,KAAK,UAAU,eAA0B;AAAA,QACtG,eAAe,KAAK,WAAW,KAAK;AAAA,QACpC,cAAc,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK;AAAA,QACtE,eAAe,KAAK,UAAU,KAAK,MAAM,MAAM,KAAK,UAAU,KAAK,MAAM;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,OAAe,WAA8B;AAC1D,UAAM,YAAY,aAAa;AAC/B,UAAM,MAAM,UAAU,QAAQ,OAAO,SAAS;AAE9C,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,kBAAkB,KAAK,EAAE;AAAA,IAC3C;AAEA,WAAO;AAAA,MACL,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC;AAAA,MAC5C,aAAa,gCAAgC,IAAI,EAAE;AAAA,MACnD,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,MACX,gBAAgB,IAAI;AAAA,MACpB,UAAU,IAAI;AAAA,MACd,YAAY;AAAA,QACV,EAAE,MAAM,UAAU,UAAU,IAAI,OAAO;AAAA,QACvC,EAAE,MAAM,eAAe,WAAW,IAAI,aAAa,KAAK,IAAI;AAAA,MAC9D;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAAA,EACF;AACF;AA+CO,SAAS,uBAAuC;AACrD,SAAO,IAAI,eAAe;AAC5B;AAGA,IAAI;AAKG,SAAS,oBAAoC;AAClD,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,IAAI,eAAe;AAAA,EACtC;AACA,SAAO;AACT;;;AL7OA,eAAsB,SAAS,SAAyB,CAAC,GAItD;AACD,QAAM,YAAY,aAAa,MAAM;AACrC,QAAM,SAAS,IAAI,eAAe,WAAW,MAAM;AAEnD,QAAM,OAAO,MAAM;AAGnB,MAAI,OAAO,SAAS,OAAO;AACzB,UAAM,MAAM,UAAU,OAAO,QAAQ,WAAW,IAAI,OAAO,QAAQ,IAAI;AACvE,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAe;AAC7C,YAAM,UAAU,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAAU;AACzD,WAAK,GAAG,OAAO,IAAI,GAAG,EAAE;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,MAAM,OAAO,KAAK;AAAA,EAC1B;AACF;AAKO,SAAS,UACd,IACA,UAII,CAAC,GACF;AACH,QAAM,YAAY,QAAQ,aAAa,aAAa;AACpD,QAAM,OAAO,QAAQ,QAAQ,GAAG,QAAQ;AACxC,QAAM,OAAO,QAAQ,QAAQ;AAE7B,UAAQ,UAAU,SAAoB;AACpC,UAAM,QAAQ,UAAU,SAAS,MAAM,MAAM,IAAI;AACjD,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,gBAAU,OAAO,OAAO,MAAM;AAC9B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,gBAAU,SAAS,OAAO,KAAc;AACxC,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAKO,SAAS,MAAM,UAGlB,CAAC,GAAG;AACN,SAAO,SACL,SACA,aACA,YACA;AACA,UAAM,iBAAiB,WAAW;AAClC,UAAM,OAAO,QAAQ,QAAQ;AAC7B,UAAM,OAAO,QAAQ,QAAQ;AAE7B,eAAW,QAAQ,kBAAmB,MAAiB;AACrD,YAAM,YAAY,aAAa;AAC/B,YAAM,QAAQ,UAAU,SAAS,MAAM,MAAM,IAAI;AACjD,UAAI;AACF,cAAM,SAAS,MAAM,eAAe,MAAM,MAAM,IAAI;AACpD,kBAAU,OAAO,OAAO,MAAM;AAC9B,eAAO;AAAA,MACT,SAAS,OAAO;AACd,kBAAU,SAAS,OAAO,KAAc;AACxC,cAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,IAAM,QAAQ;AAAA,EACnB,MAAM,MAAoB,MAAc,OAAiB,UAAkC;AACzF,WAAO,aAAa,EAAE,SAAS,MAAM,MAAM,OAAO,QAAQ;AAAA,EAC5D;AAAA,EAEA,IAAI,OAAe,QAAkB,UAAgC;AACnE,iBAAa,EAAE,OAAO,OAAO,QAAQ,QAAQ;AAAA,EAC/C;AAAA,EAEA,MAAM,OAAe,OAA6B;AAChD,iBAAa,EAAE,SAAS,OAAO,KAAK;AAAA,EACtC;AAAA,EAEA,QAAQ,MAAuB;AAC7B,WAAO,aAAa,EAAE,aAAa,IAAI;AAAA,EACzC;AAAA,EAEA,WAAW,WAA0B;AACnC,iBAAa,EAAE,WAAW,SAAS;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KACJ,MACA,MACA,IACA,UACY;AACZ,UAAM,QAAQ,aAAa,EAAE,SAAS,MAAM,MAAM,QAAW,QAAQ;AACrE,QAAI;AACF,YAAM,SAAS,MAAM,GAAG;AACxB,mBAAa,EAAE,OAAO,OAAO,MAAM;AACnC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,mBAAa,EAAE,SAAS,OAAO,KAAc;AAC7C,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":["trace"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/collector.ts","../src/server.ts","../src/tracer-hook.ts","../src/opentelemetry.ts","../src/replay.ts"],"sourcesContent":["import { TraceCollector, getCollector } from './collector.js';\nimport { DevToolsServer } from './server.js';\nimport type { DevToolsConfig, TraceRunType, TraceMetadata } from './types.js';\n\nexport * from './types.js';\nexport { TraceCollector, getCollector, resetCollector } from './collector.js';\nexport { DevToolsServer } from './server.js';\nexport { createDevToolsHook, createTracerWithDevTools } from './tracer-hook.js';\nexport { OpenTelemetryExporter, createOTLPExporter, type OpenTelemetryConfig } from './opentelemetry.js';\nexport { ReplayDebugger, createReplayDebugger, getReplayDebugger, type RunComparison, type TestCase } from './replay.js';\n\n/**\n * Start the DevTools dashboard\n */\nexport async function devtools(config: DevToolsConfig = {}): Promise<{\n collector: TraceCollector;\n server: DevToolsServer;\n stop: () => Promise<void>;\n}> {\n const collector = getCollector(config);\n const server = new DevToolsServer(collector, config);\n \n await server.start();\n\n // Open browser if requested\n if (config.open !== false) {\n const url = `http://${config.host ?? 'localhost'}:${config.port ?? 3001}`;\n try {\n const { exec } = await import('child_process');\n const command = process.platform === 'darwin' ? 'open' :\n process.platform === 'win32' ? 'start' : 'xdg-open';\n exec(`${command} ${url}`);\n } catch {\n // Ignore if can't open browser\n }\n }\n\n return {\n collector,\n server,\n stop: () => server.stop(),\n };\n}\n\n/**\n * Create a trace wrapper for any function\n */\nexport function withTrace<T extends (...args: unknown[]) => unknown>(\n fn: T,\n options: {\n name?: string;\n type?: TraceRunType;\n collector?: TraceCollector;\n } = {}\n): T {\n const collector = options.collector ?? getCollector();\n const name = options.name ?? fn.name ?? 'anonymous';\n const type = options.type ?? 'custom';\n\n return (async (...args: unknown[]) => {\n const runId = collector.startRun(type, name, args);\n try {\n const result = await fn(...args);\n collector.endRun(runId, result);\n return result;\n } catch (error) {\n collector.errorRun(runId, error as Error);\n throw error;\n }\n }) as T;\n}\n\n/**\n * Decorator for tracing class methods\n */\nexport function Trace(options: {\n name?: string;\n type?: TraceRunType;\n} = {}) {\n return function (\n _target: unknown,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n const originalMethod = descriptor.value;\n const name = options.name ?? propertyKey;\n const type = options.type ?? 'custom';\n\n descriptor.value = async function (...args: unknown[]) {\n const collector = getCollector();\n const runId = collector.startRun(type, name, args);\n try {\n const result = await originalMethod.apply(this, args);\n collector.endRun(runId, result);\n return result;\n } catch (error) {\n collector.errorRun(runId, error as Error);\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Manual tracing helpers\n */\nexport const trace = {\n start(type: TraceRunType, name: string, input?: unknown, metadata?: TraceMetadata): string {\n return getCollector().startRun(type, name, input, metadata);\n },\n \n end(runId: string, output?: unknown, metadata?: TraceMetadata): void {\n getCollector().endRun(runId, output, metadata);\n },\n \n error(runId: string, error: Error | string): void {\n getCollector().errorRun(runId, error);\n },\n \n session(name?: string): string {\n return getCollector().startSession(name);\n },\n \n endSession(sessionId?: string): void {\n getCollector().endSession(sessionId);\n },\n\n /**\n * Wrap an async function with tracing\n */\n async wrap<T>(\n type: TraceRunType,\n name: string,\n fn: () => Promise<T>,\n metadata?: TraceMetadata\n ): Promise<T> {\n const runId = getCollector().startRun(type, name, undefined, metadata);\n try {\n const result = await fn();\n getCollector().endRun(runId, result);\n return result;\n } catch (error) {\n getCollector().errorRun(runId, error as Error);\n throw error;\n }\n },\n};\n","import { generateId } from '@orka-js/core';\nimport type {\n TraceRun,\n TraceRunType,\n TraceSession,\n TraceMetrics,\n TraceMetadata,\n TraceEvent,\n DevToolsConfig,\n} from './types.js';\n\n/**\n * TraceCollector - Collects and manages trace data for DevTools\n */\nexport class TraceCollector {\n private sessions: Map<string, TraceSession> = new Map();\n private activeSessionId?: string;\n private runStack: Map<string, TraceRun[]> = new Map();\n private maxTraces: number;\n private retentionMs: number;\n private listeners: Set<(event: TraceEvent) => void> = new Set();\n\n constructor(config: DevToolsConfig = {}) {\n this.maxTraces = config.maxTraces ?? 1000;\n this.retentionMs = config.retentionMs ?? 24 * 60 * 60 * 1000; // 24 hours\n }\n\n /**\n * Start a new trace session\n */\n startSession(name?: string): string {\n const sessionId = generateId();\n const session: TraceSession = {\n id: sessionId,\n name: name ?? `Session ${this.sessions.size + 1}`,\n startTime: Date.now(),\n runs: [],\n };\n\n this.sessions.set(sessionId, session);\n this.activeSessionId = sessionId;\n this.runStack.set(sessionId, []);\n\n this.emit({\n type: 'session:start',\n timestamp: Date.now(),\n sessionId,\n });\n\n this.cleanup();\n return sessionId;\n }\n\n /**\n * End the current session\n */\n endSession(sessionId?: string): void {\n const id = sessionId ?? this.activeSessionId;\n if (!id) return;\n\n const session = this.sessions.get(id);\n if (session) {\n session.endTime = Date.now();\n this.emit({\n type: 'session:end',\n timestamp: Date.now(),\n sessionId: id,\n });\n }\n\n if (this.activeSessionId === id) {\n this.activeSessionId = undefined;\n }\n }\n\n /**\n * Start a new trace run\n */\n startRun(\n type: TraceRunType,\n name: string,\n input?: unknown,\n metadata?: TraceMetadata\n ): string {\n const sessionId = this.activeSessionId ?? this.startSession();\n const session = this.sessions.get(sessionId)!;\n const stack = this.runStack.get(sessionId)!;\n\n const run: TraceRun = {\n id: generateId(),\n parentId: stack.length > 0 ? stack[stack.length - 1].id : undefined,\n type,\n name,\n startTime: Date.now(),\n status: 'running',\n input,\n metadata,\n children: [],\n };\n\n // Add to parent's children or session root\n if (stack.length > 0) {\n stack[stack.length - 1].children.push(run);\n } else {\n session.runs.push(run);\n }\n\n stack.push(run);\n\n this.emit({\n type: 'run:start',\n timestamp: Date.now(),\n sessionId,\n run,\n });\n\n return run.id;\n }\n\n /**\n * End a trace run\n */\n endRun(runId: string, output?: unknown, metadata?: TraceMetadata): void {\n const sessionId = this.activeSessionId;\n if (!sessionId) return;\n\n const stack = this.runStack.get(sessionId);\n if (!stack) return;\n\n const runIndex = stack.findIndex(r => r.id === runId);\n if (runIndex === -1) return;\n\n const run = stack[runIndex];\n run.endTime = Date.now();\n run.latencyMs = run.endTime - run.startTime;\n run.status = 'success';\n run.output = output;\n\n if (metadata) {\n run.metadata = { ...run.metadata, ...metadata };\n }\n\n // Pop from stack\n stack.splice(runIndex, 1);\n\n this.emit({\n type: 'run:end',\n timestamp: Date.now(),\n sessionId,\n run,\n });\n }\n\n /**\n * Mark a run as errored\n */\n errorRun(runId: string, error: Error | string): void {\n const sessionId = this.activeSessionId;\n if (!sessionId) return;\n\n const stack = this.runStack.get(sessionId);\n if (!stack) return;\n\n const runIndex = stack.findIndex(r => r.id === runId);\n if (runIndex === -1) return;\n\n const run = stack[runIndex];\n run.endTime = Date.now();\n run.latencyMs = run.endTime - run.startTime;\n run.status = 'error';\n run.error = error instanceof Error ? error.message : error;\n\n stack.splice(runIndex, 1);\n\n this.emit({\n type: 'run:error',\n timestamp: Date.now(),\n sessionId,\n run,\n error: run.error,\n });\n }\n\n /**\n * Get all sessions\n */\n getSessions(): TraceSession[] {\n return Array.from(this.sessions.values());\n }\n\n /**\n * Get a specific session\n */\n getSession(sessionId: string): TraceSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n /**\n * Get metrics for a session or all sessions\n */\n getMetrics(sessionId?: string): TraceMetrics {\n const sessions = sessionId\n ? [this.sessions.get(sessionId)].filter(Boolean) as TraceSession[]\n : Array.from(this.sessions.values());\n\n const metrics: TraceMetrics = {\n totalRuns: 0,\n totalLatencyMs: 0,\n avgLatencyMs: 0,\n totalTokens: 0,\n totalCost: 0,\n errorRate: 0,\n runsByType: {} as Record<TraceRunType, number>,\n tokensByModel: {},\n costByModel: {},\n };\n\n let errorCount = 0;\n\n const processRun = (run: TraceRun) => {\n metrics.totalRuns++;\n metrics.totalLatencyMs += run.latencyMs ?? 0;\n\n if (run.status === 'error') errorCount++;\n\n // Count by type\n metrics.runsByType[run.type] = (metrics.runsByType[run.type] ?? 0) + 1;\n\n // Aggregate token/cost metrics\n if (run.metadata) {\n const { totalTokens, cost, model } = run.metadata;\n if (totalTokens) {\n metrics.totalTokens += totalTokens;\n if (model) {\n metrics.tokensByModel[model] = (metrics.tokensByModel[model] ?? 0) + totalTokens;\n }\n }\n if (cost) {\n metrics.totalCost += cost;\n if (model) {\n metrics.costByModel[model] = (metrics.costByModel[model] ?? 0) + cost;\n }\n }\n }\n\n // Process children\n for (const child of run.children) {\n processRun(child);\n }\n };\n\n for (const session of sessions) {\n for (const run of session.runs) {\n processRun(run);\n }\n }\n\n metrics.avgLatencyMs = metrics.totalRuns > 0\n ? metrics.totalLatencyMs / metrics.totalRuns\n : 0;\n metrics.errorRate = metrics.totalRuns > 0\n ? errorCount / metrics.totalRuns\n : 0;\n\n return metrics;\n }\n\n /**\n * Find a run by ID\n */\n findRun(runId: string, sessionId?: string): TraceRun | undefined {\n const sessions = sessionId\n ? [this.sessions.get(sessionId)].filter(Boolean) as TraceSession[]\n : Array.from(this.sessions.values());\n\n const findInRuns = (runs: TraceRun[]): TraceRun | undefined => {\n for (const run of runs) {\n if (run.id === runId) return run;\n const found = findInRuns(run.children);\n if (found) return found;\n }\n return undefined;\n };\n\n for (const session of sessions) {\n const found = findInRuns(session.runs);\n if (found) return found;\n }\n\n return undefined;\n }\n\n /**\n * Subscribe to trace events\n */\n subscribe(listener: (event: TraceEvent) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Emit an event to all listeners\n */\n private emit(event: TraceEvent): void {\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch {\n // Ignore listener errors\n }\n }\n }\n\n /**\n * Cleanup old sessions\n */\n private cleanup(): void {\n const now = Date.now();\n const cutoff = now - this.retentionMs;\n\n for (const [id, session] of this.sessions) {\n if (session.endTime && session.endTime < cutoff) {\n this.sessions.delete(id);\n this.runStack.delete(id);\n }\n }\n\n // Limit total sessions\n if (this.sessions.size > this.maxTraces) {\n const sorted = Array.from(this.sessions.entries())\n .sort((a, b) => a[1].startTime - b[1].startTime);\n \n const toDelete = sorted.slice(0, this.sessions.size - this.maxTraces);\n for (const [id] of toDelete) {\n this.sessions.delete(id);\n this.runStack.delete(id);\n }\n }\n }\n\n /**\n * Clear all traces\n */\n clear(): void {\n this.sessions.clear();\n this.runStack.clear();\n this.activeSessionId = undefined;\n }\n\n /**\n * Export traces as JSON\n */\n export(): string {\n return JSON.stringify({\n sessions: Array.from(this.sessions.values()),\n exportedAt: new Date().toISOString(),\n }, null, 2);\n }\n\n /**\n * Import traces from JSON\n */\n import(json: string): void {\n const data = JSON.parse(json) as { sessions: TraceSession[] };\n for (const session of data.sessions) {\n this.sessions.set(session.id, session);\n }\n }\n}\n\n// Global collector instance\nlet globalCollector: TraceCollector | undefined;\n\nexport function getCollector(config?: DevToolsConfig): TraceCollector {\n if (!globalCollector) {\n globalCollector = new TraceCollector(config);\n }\n return globalCollector;\n}\n\nexport function resetCollector(): void {\n globalCollector = undefined;\n}\n","import type { TraceCollector } from './collector.js';\nimport type { DevToolsConfig, TraceEvent } from './types.js';\nimport type { Application, Request, Response, NextFunction } from 'express';\nimport type { Server, ServerResponse } from 'http';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n/**\n * DevTools Server - Express server for the DevTools dashboard\n */\nexport class DevToolsServer {\n private collector: TraceCollector;\n private config: Required<Pick<DevToolsConfig, 'port' | 'host' | 'cors'>>;\n private server?: Server;\n private clients: Set<ServerResponse> = new Set();\n private dashboardHTML: string;\n\n constructor(collector: TraceCollector, config: DevToolsConfig = {}) {\n this.collector = collector;\n this.config = {\n port: config.port ?? 3001,\n host: config.host ?? 'localhost',\n cors: config.cors ?? true,\n };\n this.dashboardHTML = this.loadDashboardHTML();\n }\n\n /**\n * Load dashboard HTML from file\n */\n private loadDashboardHTML(): string {\n try {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return readFileSync(join(__dirname, 'dashboard.html'), 'utf-8');\n } catch {\n // Fallback to inline HTML if file not found\n return this.getInlineDashboardHTML();\n }\n }\n\n /**\n * Start the DevTools server\n */\n async start(): Promise<void> {\n // Dynamic import for optional express dependency\n let expressModule: { default: () => Application; json: () => unknown };\n let httpModule: typeof import('http');\n \n try {\n expressModule = await import('express') as unknown as { default: () => Application; json: () => unknown };\n httpModule = await import('http');\n } catch {\n throw new Error(\n 'Express is required for DevTools server. Install it with: npm install express'\n );\n }\n\n const app = expressModule.default();\n\n // CORS middleware\n if (this.config.cors) {\n app.use((_req: Request, res: Response, next: NextFunction) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE');\n next();\n });\n }\n\n app.use(expressModule.json() as unknown as (req: Request, res: Response, next: NextFunction) => void);\n\n // API Routes\n this.setupRoutes(app);\n\n // Create HTTP server\n this.server = httpModule.createServer(app);\n\n // Start listening\n await new Promise<void>((resolve) => {\n this.server!.listen(this.config.port, this.config.host, () => {\n console.log(`\\n🔍 OrkaJS DevTools running at http://${this.config.host}:${this.config.port}\\n`);\n resolve();\n });\n });\n\n // Subscribe to trace events for SSE\n this.collector.subscribe((event) => {\n this.broadcastEvent(event);\n });\n }\n\n /**\n * Stop the server\n */\n async stop(): Promise<void> {\n if (this.server) {\n await new Promise<void>((resolve, reject) => {\n this.server!.close((err) => {\n if (err) reject(err);\n else resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Setup API routes\n */\n private setupRoutes(app: Application): void {\n // Health check\n app.get('/api/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok', timestamp: Date.now() });\n });\n\n // Get all sessions\n app.get('/api/sessions', (_req: Request, res: Response) => {\n const sessions = this.collector.getSessions();\n res.json(sessions);\n });\n\n // Get a specific session\n app.get('/api/sessions/:id', (req: Request, res: Response) => {\n const session = this.collector.getSession(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session);\n });\n\n // Get metrics\n app.get('/api/metrics', (req: Request, res: Response) => {\n const sessionId = req.query.sessionId as string | undefined;\n const metrics = this.collector.getMetrics(sessionId);\n res.json(metrics);\n });\n\n // Find a run\n app.get('/api/runs/:id', (req: Request, res: Response) => {\n const sessionId = req.query.sessionId as string | undefined;\n const run = this.collector.findRun(req.params.id, sessionId);\n if (!run) {\n res.status(404).json({ error: 'Run not found' });\n return;\n }\n res.json(run);\n });\n\n // Clear all traces\n app.delete('/api/sessions', (_req: Request, res: Response) => {\n this.collector.clear();\n res.json({ success: true });\n });\n\n // Export traces\n app.get('/api/export', (_req: Request, res: Response) => {\n const data = this.collector.export();\n res.setHeader('Content-Type', 'application/json');\n res.setHeader('Content-Disposition', 'attachment; filename=orka-traces.json');\n res.send(data);\n });\n\n // Import traces\n app.post('/api/import', (req: Request, res: Response) => {\n try {\n this.collector.import(JSON.stringify(req.body));\n res.json({ success: true });\n } catch {\n res.status(400).json({ error: 'Invalid trace data' });\n }\n });\n\n // Server-Sent Events for real-time updates\n app.get('/api/events', (req: Request, res: Response) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n\n this.clients.add(res as unknown as ServerResponse);\n\n req.on('close', () => {\n this.clients.delete(res as unknown as ServerResponse);\n });\n });\n\n // Serve static UI (if available)\n app.get('/', (_req: Request, res: Response) => {\n res.send(this.dashboardHTML);\n });\n }\n\n /**\n * Broadcast event to all SSE clients\n */\n private broadcastEvent(event: TraceEvent): void {\n const data = JSON.stringify(event);\n for (const client of this.clients) {\n client.write(`data: ${data}\\n\\n`);\n }\n }\n\n /**\n * Inline fallback dashboard HTML\n */\n private getInlineDashboardHTML(): string {\n return '<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>OrkaJS DevTools</title><script src=\"https://cdn.tailwindcss.com\"><\\/script><script>tailwind.config={darkMode:\"class\"}<\\/script><style>.tree-line{border-left:2px solid #e2e8f0}.dark .tree-line{border-left-color:#334155}.scrollbar-thin::-webkit-scrollbar{width:6px}.scrollbar-thin::-webkit-scrollbar-thumb{background:#64748b;border-radius:3px}</style></head><body class=\"bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-white min-h-screen\"><div id=\"app\" class=\"max-w-7xl mx-auto p-6\"><header class=\"flex items-center justify-between mb-8\"><div class=\"flex items-center gap-3\"><img src=\"https://devtools.orkajs.com/orka-devtools.png\" alt=\"OrkaJS\" class=\"w-10 h-10 rounded-lg\" onerror=\"this.style.display=\\'none\\'\"><div><h1 class=\"text-2xl font-bold\">OrkaJS DevTools</h1><p class=\"text-sm text-slate-500\">Real-time LLM observability</p></div></div><div class=\"flex items-center gap-3\"><span id=\"status\" class=\"flex items-center gap-2 text-sm px-3 py-1.5 bg-green-500/10 rounded-lg\"><span class=\"w-2 h-2 bg-green-500 rounded-full animate-pulse\"></span><span class=\"text-green-600 dark:text-green-400\">Live</span></span><button onclick=\"toggleTheme()\" class=\"p-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700\"><svg id=\"themeIcon\" class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z\"/></svg></button><button onclick=\"clearTraces()\" class=\"px-3 py-2 text-sm bg-red-500/10 text-red-600 rounded-lg hover:bg-red-500/20\">Clear</button><button onclick=\"exportTraces()\" class=\"px-3 py-2 text-sm bg-purple-500/10 text-purple-600 rounded-lg hover:bg-purple-500/20\">Export</button></div></header><div class=\"grid grid-cols-4 gap-4 mb-8\"><div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\"><p class=\"text-sm text-slate-500 mb-1\">Total Runs</p><p id=\"metric-runs\" class=\"text-2xl font-bold\">0</p></div><div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\"><p class=\"text-sm text-slate-500 mb-1\">Avg Latency</p><p id=\"metric-latency\" class=\"text-2xl font-bold\">0ms</p></div><div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\"><p class=\"text-sm text-slate-500 mb-1\">Total Tokens</p><p id=\"metric-tokens\" class=\"text-2xl font-bold\">0</p></div><div class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm\"><p class=\"text-sm text-slate-500 mb-1\">Error Rate</p><p id=\"metric-errors\" class=\"text-2xl font-bold\">0%</p></div></div><div class=\"grid grid-cols-3 gap-6\"><div class=\"col-span-1\"><h2 class=\"text-lg font-semibold mb-4\">Sessions</h2><div id=\"sessions\" class=\"space-y-2\"></div></div><div class=\"col-span-2\"><h2 class=\"text-lg font-semibold mb-4\">Trace Viewer</h2><div id=\"traces\" class=\"bg-white dark:bg-slate-800 rounded-xl p-4 shadow-sm min-h-[400px]\"><p class=\"text-slate-500 text-center py-8\">Select a session to view traces</p></div></div></div></div><script>let selectedSession=null;function initTheme(){const t=localStorage.getItem(\"orka-devtools-theme\");(\"dark\"===t||!t&&window.matchMedia(\"(prefers-color-scheme: dark)\").matches)&&document.documentElement.classList.add(\"dark\")}initTheme();function toggleTheme(){const t=document.documentElement.classList.toggle(\"dark\");localStorage.setItem(\"orka-devtools-theme\",t?\"dark\":\"light\")}const events=new EventSource(\"/api/events\");events.onmessage=e=>{refreshData()};events.onerror=()=>{document.getElementById(\"status\").innerHTML=\\'<span class=\"w-2 h-2 bg-red-500 rounded-full\"></span><span class=\"text-red-600\">Disconnected</span>\\'};async function refreshData(){const[t,e]=await Promise.all([fetch(\"/api/metrics\").then(t=>t.json()),fetch(\"/api/sessions\").then(t=>t.json())]);document.getElementById(\"metric-runs\").textContent=t.totalRuns;document.getElementById(\"metric-latency\").textContent=Math.round(t.avgLatencyMs)+\"ms\";document.getElementById(\"metric-tokens\").textContent=t.totalTokens.toLocaleString();document.getElementById(\"metric-errors\").textContent=(100*t.errorRate).toFixed(1)+\"%\";renderSessions(e);if(selectedSession){const t=e.find(t=>t.id===selectedSession);t&&renderTraces(t.runs)}}function renderSessions(t){document.getElementById(\"sessions\").innerHTML=t.map(t=>`<div onclick=\"selectSession(\\'${t.id}\\')\" class=\"p-3 rounded-lg cursor-pointer ${selectedSession===t.id?\"bg-purple-500/20 border border-purple-500\":\"bg-white dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-700\"}\"><p class=\"font-medium\">${t.name||\"Session\"}</p><p class=\"text-xs text-slate-500\">${t.runs.length} runs</p></div>`).join(\"\")}function selectSession(t){selectedSession=t;refreshData()}function renderTraces(t){if(!t||0===t.length)return void(document.getElementById(\"traces\").innerHTML=\\'<p class=\"text-slate-500 text-center py-8\">No traces</p>\\');document.getElementById(\"traces\").innerHTML=t.map(t=>renderRun(t,0)).join(\"\")}function renderRun(t,e){const s={success:\"bg-green-500\",error:\"bg-red-500\",running:\"bg-yellow-500\"},n={llm:\"text-purple-500\",agent:\"text-blue-500\",tool:\"text-orange-500\"};return`<div class=\"mb-2\" style=\"margin-left:${20*e}px\"><div class=\"flex items-center gap-2 p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-700\"><span class=\"w-2 h-2 rounded-full ${s[t.status]||\"bg-slate-400\"}\"></span><span class=\"text-xs font-medium ${n[t.type]||\"text-slate-500\"}\">${t.type.toUpperCase()}</span><span class=\"font-medium\">${t.name}</span><span class=\"text-xs text-slate-500 ml-auto\">${t.latencyMs?t.latencyMs+\"ms\":\"...\"}</span></div>${(t.children||[]).map(t=>renderRun(t,e+1)).join(\"\")}</div>`}async function clearTraces(){confirm(\"Clear all traces?\")&&(await fetch(\"/api/sessions\",{method:\"DELETE\"}),selectedSession=null,refreshData())}function exportTraces(){window.open(\"/api/export\",\"_blank\")}refreshData()<\\/script></body></html>';\n }\n}\n","import { getCollector } from './collector.js';\nimport type { TraceRunType, TraceMetadata } from './types.js';\n\n/**\n * Hook interface matching @orka-js/observability\n */\ninterface ObservabilityHook {\n onTraceStart?: (trace: TracerTrace) => void;\n onTraceEnd?: (trace: TracerTrace) => void;\n onEvent?: (event: TracerEvent) => void;\n onError?: (error: Error, context?: Record<string, unknown>) => void;\n}\n\ninterface TracerTrace {\n id: string;\n name: string;\n startTime: number;\n endTime?: number;\n totalLatencyMs?: number;\n totalTokens?: number;\n events: TracerEvent[];\n metadata?: Record<string, unknown>;\n}\n\ninterface TracerEvent {\n id: string;\n traceId: string;\n type: string;\n name: string;\n startTime?: number;\n endTime?: number;\n latencyMs?: number;\n input?: unknown;\n output?: unknown;\n usage?: {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n };\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Map Tracer event types to DevTools run types\n */\nfunction mapEventType(type: string): TraceRunType {\n const typeMap: Record<string, TraceRunType> = {\n 'llm': 'llm',\n 'llm_call': 'llm',\n 'embedding': 'embedding',\n 'retrieval': 'retrieval',\n 'tool': 'tool',\n 'tool_call': 'tool',\n 'agent': 'agent',\n 'agent_step': 'agent',\n 'chain': 'chain',\n 'workflow': 'workflow',\n 'graph': 'graph',\n 'node': 'node',\n };\n return typeMap[type.toLowerCase()] ?? 'custom';\n}\n\n/**\n * Create a DevTools hook for the Tracer\n * \n * Usage:\n * ```ts\n * import { Tracer } from '@orka-js/observability';\n * import { createDevToolsHook, devtools } from '@orka-js/devtools';\n * \n * await devtools();\n * \n * const tracer = new Tracer({\n * hooks: [createDevToolsHook()]\n * });\n * ```\n */\nexport function createDevToolsHook(): ObservabilityHook {\n const collector = getCollector();\n const traceToSession = new Map<string, string>();\n const eventToRun = new Map<string, string>();\n\n return {\n onTraceStart(trace: TracerTrace) {\n // Start a new session for each trace\n const sessionId = collector.startSession(trace.name);\n traceToSession.set(trace.id, sessionId);\n },\n\n onTraceEnd(trace: TracerTrace) {\n const sessionId = traceToSession.get(trace.id);\n if (sessionId) {\n collector.endSession(sessionId);\n traceToSession.delete(trace.id);\n }\n },\n\n onEvent(event: TracerEvent) {\n const runType = mapEventType(event.type);\n \n // Build metadata\n const metadata: TraceMetadata = {\n ...event.metadata,\n };\n\n if (event.usage) {\n metadata.promptTokens = event.usage.promptTokens;\n metadata.completionTokens = event.usage.completionTokens;\n metadata.totalTokens = event.usage.totalTokens;\n }\n\n // If event has both start and end time, create a completed run\n if (event.startTime && event.endTime) {\n const runId = collector.startRun(runType, event.name, event.input, metadata);\n collector.endRun(runId, event.output, {\n ...metadata,\n // Override latency calculation since we have actual times\n });\n eventToRun.set(event.id, runId);\n } else if (event.startTime && !event.endTime) {\n // Event is starting\n const runId = collector.startRun(runType, event.name, event.input, metadata);\n eventToRun.set(event.id, runId);\n } else {\n // Event is ending (find matching start)\n const runId = eventToRun.get(event.id);\n if (runId) {\n collector.endRun(runId, event.output, metadata);\n eventToRun.delete(event.id);\n }\n }\n },\n\n onError(error: Error, context?: Record<string, unknown>) {\n // Find the most recent run and mark it as errored\n const sessions = collector.getSessions();\n if (sessions.length > 0) {\n const lastSession = sessions[sessions.length - 1];\n if (lastSession.runs.length > 0) {\n const findRunningRun = (runs: Array<{ id: string; status: string; children: unknown[] }>): string | undefined => {\n for (const run of runs) {\n if (run.status === 'running') return run.id;\n const childRun = findRunningRun(run.children as Array<{ id: string; status: string; children: unknown[] }>);\n if (childRun) return childRun;\n }\n return undefined;\n };\n\n const runningRunId = findRunningRun(lastSession.runs as Array<{ id: string; status: string; children: unknown[] }>);\n if (runningRunId) {\n collector.errorRun(runningRunId, error);\n }\n }\n }\n\n // Log context if provided\n if (context) {\n console.error('[DevTools] Error context:', context);\n }\n },\n };\n}\n\n/**\n * Convenience function to create a Tracer with DevTools integration\n */\nexport function createTracerWithDevTools(options: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n maxTraces?: number;\n traceTtlMs?: number;\n} = {}): {\n hook: ObservabilityHook;\n config: typeof options & { hooks: ObservabilityHook[] };\n} {\n const hook = createDevToolsHook();\n return {\n hook,\n config: {\n ...options,\n hooks: [hook],\n },\n };\n}\n","import type { TraceRun, TraceRunType, TraceEvent } from './types.js';\nimport { getCollector } from './collector.js';\n\n/**\n * OpenTelemetry configuration\n */\nexport interface OpenTelemetryConfig {\n endpoint: string;\n serviceName?: string;\n serviceVersion?: string;\n headers?: Record<string, string>;\n batchSize?: number;\n flushIntervalMs?: number;\n enabled?: boolean;\n}\n\n/**\n * OTLP Span representation\n */\ninterface OTLPSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n name: string;\n kind: number;\n startTimeUnixNano: string;\n endTimeUnixNano: string;\n attributes: Array<{ key: string; value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean } }>;\n status: { code: number; message?: string };\n}\n\n/**\n * OpenTelemetry Exporter for DevTools traces\n */\nexport class OpenTelemetryExporter {\n private config: Required<OpenTelemetryConfig>;\n private spanBuffer: OTLPSpan[] = [];\n private flushTimer?: ReturnType<typeof setInterval>;\n private unsubscribe?: () => void;\n\n constructor(config: OpenTelemetryConfig) {\n this.config = {\n endpoint: config.endpoint,\n serviceName: config.serviceName ?? 'orkajs-app',\n serviceVersion: config.serviceVersion ?? '1.0.0',\n headers: config.headers ?? {},\n batchSize: config.batchSize ?? 100,\n flushIntervalMs: config.flushIntervalMs ?? 5000,\n enabled: config.enabled ?? true,\n };\n }\n\n /**\n * Start the exporter - subscribes to trace events\n */\n start(): void {\n if (!this.config.enabled) return;\n\n const collector = getCollector();\n \n this.unsubscribe = collector.subscribe((event: TraceEvent) => {\n if (event.type === 'run:end' && event.run) {\n this.addSpan(event.run, event.sessionId);\n }\n });\n\n // Start flush timer\n this.flushTimer = setInterval(() => {\n this.flush();\n }, this.config.flushIntervalMs);\n }\n\n /**\n * Stop the exporter\n */\n async stop(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = undefined;\n }\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n // Final flush\n await this.flush();\n }\n\n /**\n * Convert a TraceRun to OTLP span\n */\n private addSpan(run: TraceRun, traceId: string): void {\n const span = this.runToSpan(run, traceId);\n this.spanBuffer.push(span);\n\n // Also add child spans\n for (const child of run.children) {\n this.addSpan(child, traceId);\n }\n\n // Flush if buffer is full\n if (this.spanBuffer.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n /**\n * Convert TraceRun to OTLP Span format\n */\n private runToSpan(run: TraceRun, traceId: string): OTLPSpan {\n const attributes: OTLPSpan['attributes'] = [\n { key: 'orka.run.type', value: { stringValue: run.type } },\n { key: 'orka.run.name', value: { stringValue: run.name } },\n { key: 'orka.run.status', value: { stringValue: run.status } },\n ];\n\n // Add metadata as attributes\n if (run.metadata) {\n if (run.metadata.model) {\n attributes.push({ key: 'llm.model', value: { stringValue: run.metadata.model } });\n }\n if (run.metadata.provider) {\n attributes.push({ key: 'llm.provider', value: { stringValue: run.metadata.provider } });\n }\n if (run.metadata.totalTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.total', value: { intValue: String(run.metadata.totalTokens) } });\n }\n if (run.metadata.promptTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.prompt', value: { intValue: String(run.metadata.promptTokens) } });\n }\n if (run.metadata.completionTokens !== undefined) {\n attributes.push({ key: 'llm.tokens.completion', value: { intValue: String(run.metadata.completionTokens) } });\n }\n if (run.metadata.cost !== undefined) {\n attributes.push({ key: 'llm.cost', value: { doubleValue: run.metadata.cost } });\n }\n if (run.metadata.toolName) {\n attributes.push({ key: 'tool.name', value: { stringValue: run.metadata.toolName } });\n }\n }\n\n // Add latency\n if (run.latencyMs !== undefined) {\n attributes.push({ key: 'orka.latency_ms', value: { intValue: String(run.latencyMs) } });\n }\n\n // Add error if present\n if (run.error) {\n attributes.push({ key: 'error.message', value: { stringValue: run.error } });\n }\n\n return {\n traceId: this.toHex(traceId, 32),\n spanId: this.toHex(run.id, 16),\n parentSpanId: run.parentId ? this.toHex(run.parentId, 16) : undefined,\n name: `${run.type}/${run.name}`,\n kind: this.getSpanKind(run.type),\n startTimeUnixNano: String(run.startTime * 1_000_000),\n endTimeUnixNano: String((run.endTime ?? run.startTime) * 1_000_000),\n attributes,\n status: {\n code: run.status === 'error' ? 2 : 1,\n message: run.error,\n },\n };\n }\n\n /**\n * Get OTLP span kind based on run type\n */\n private getSpanKind(type: TraceRunType): number {\n switch (type) {\n case 'llm':\n case 'embedding':\n return 3; // CLIENT\n case 'tool':\n return 3; // CLIENT\n case 'agent':\n case 'chain':\n case 'workflow':\n case 'graph':\n return 0; // INTERNAL\n default:\n return 0; // INTERNAL\n }\n }\n\n /**\n * Convert string ID to hex format\n */\n private toHex(id: string, length: number): string {\n // Simple hash to hex conversion\n let hash = 0;\n for (let i = 0; i < id.length; i++) {\n const char = id.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n const hex = Math.abs(hash).toString(16).padStart(length, '0');\n return hex.slice(0, length);\n }\n\n /**\n * Flush spans to OTLP endpoint\n */\n async flush(): Promise<void> {\n if (this.spanBuffer.length === 0) return;\n\n const spans = [...this.spanBuffer];\n this.spanBuffer = [];\n\n const payload = {\n resourceSpans: [{\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: this.config.serviceName } },\n { key: 'service.version', value: { stringValue: this.config.serviceVersion } },\n { key: 'telemetry.sdk.name', value: { stringValue: '@orka-js/devtools' } },\n { key: 'telemetry.sdk.version', value: { stringValue: '1.1.0' } },\n ],\n },\n scopeSpans: [{\n scope: {\n name: '@orka-js/devtools',\n version: '1.1.0',\n },\n spans,\n }],\n }],\n };\n\n try {\n const response = await fetch(`${this.config.endpoint}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n console.error(`[DevTools] OTLP export failed: ${response.status} ${response.statusText}`);\n // Re-add spans to buffer for retry\n this.spanBuffer.unshift(...spans);\n }\n } catch (error) {\n console.error('[DevTools] OTLP export error:', error);\n // Re-add spans to buffer for retry\n this.spanBuffer.unshift(...spans);\n }\n }\n}\n\n/**\n * Create and start an OpenTelemetry exporter\n */\nexport function createOTLPExporter(config: OpenTelemetryConfig): OpenTelemetryExporter {\n const exporter = new OpenTelemetryExporter(config);\n exporter.start();\n return exporter;\n}\n","import type { TraceRun, ReplayOptions, ReplayResult } from './types.js';\nimport { getCollector } from './collector.js';\n\n/**\n * Replay Debugger - Replay traces with modified inputs\n */\nexport class ReplayDebugger {\n /**\n * Replay a trace run with optionally modified input\n */\n async replay(options: ReplayOptions): Promise<ReplayResult> {\n const collector = getCollector();\n const originalRun = collector.findRun(options.runId, options.sessionId);\n\n if (!originalRun) {\n throw new Error(`Run not found: ${options.runId}`);\n }\n\n // Get the modified input\n const modifiedInput = options.modifyInput \n ? options.modifyInput(originalRun.input)\n : originalRun.input;\n\n // Create a new session for the replay\n const replaySessionId = collector.startSession(`Replay: ${originalRun.name}`);\n\n // Start the replayed run\n const replayRunId = collector.startRun(\n originalRun.type,\n `replay:${originalRun.name}`,\n modifiedInput,\n {\n ...originalRun.metadata,\n replayOf: originalRun.id,\n originalSessionId: options.sessionId,\n }\n );\n\n // For now, we just copy the output (actual replay would re-execute)\n // In a real implementation, this would call the original function\n const replayedOutput = originalRun.output;\n\n collector.endRun(replayRunId, replayedOutput, originalRun.metadata);\n collector.endSession(replaySessionId);\n\n // Get the replayed run\n const replayedRun = collector.findRun(replayRunId);\n\n if (!replayedRun) {\n throw new Error('Failed to create replayed run');\n }\n\n // Calculate diff\n const inputChanged = JSON.stringify(originalRun.input) !== JSON.stringify(modifiedInput);\n const outputChanged = JSON.stringify(originalRun.output) !== JSON.stringify(replayedRun.output);\n const latencyDiff = (replayedRun.latencyMs ?? 0) - (originalRun.latencyMs ?? 0);\n\n return {\n originalRun,\n replayedRun,\n diff: {\n inputChanged,\n outputChanged,\n latencyDiff,\n },\n };\n }\n\n /**\n * Fork a trace to create a new branch for experimentation\n */\n fork(runId: string, sessionId?: string): string {\n const collector = getCollector();\n const originalRun = collector.findRun(runId, sessionId);\n\n if (!originalRun) {\n throw new Error(`Run not found: ${runId}`);\n }\n\n // Create a new session for the fork\n const forkSessionId = collector.startSession(`Fork: ${originalRun.name}`);\n\n // Deep clone the run tree\n this.cloneRunTree(originalRun, forkSessionId);\n\n collector.endSession(forkSessionId);\n\n return forkSessionId;\n }\n\n /**\n * Clone a run and its children\n */\n private cloneRunTree(run: TraceRun, _sessionId: string, parentId?: string): string {\n const collector = getCollector();\n\n const newRunId = collector.startRun(\n run.type,\n `fork:${run.name}`,\n run.input,\n {\n ...run.metadata,\n forkedFrom: run.id,\n originalParentId: parentId,\n }\n );\n\n // Clone children\n for (const child of run.children) {\n this.cloneRunTree(child, _sessionId, newRunId);\n }\n\n // End the run with original output\n if (run.status === 'error') {\n collector.errorRun(newRunId, run.error ?? 'Unknown error');\n } else {\n collector.endRun(newRunId, run.output, run.metadata);\n }\n\n return newRunId;\n }\n\n /**\n * Compare two runs and return detailed diff\n */\n compare(runId1: string, runId2: string, sessionId?: string): RunComparison {\n const collector = getCollector();\n const run1 = collector.findRun(runId1, sessionId);\n const run2 = collector.findRun(runId2, sessionId);\n\n if (!run1 || !run2) {\n throw new Error('One or both runs not found');\n }\n\n return {\n run1: {\n id: run1.id,\n name: run1.name,\n type: run1.type,\n latencyMs: run1.latencyMs,\n status: run1.status,\n tokenCount: run1.metadata?.totalTokens as number | undefined,\n },\n run2: {\n id: run2.id,\n name: run2.name,\n type: run2.type,\n latencyMs: run2.latencyMs,\n status: run2.status,\n tokenCount: run2.metadata?.totalTokens as number | undefined,\n },\n diff: {\n latencyDiff: (run2.latencyMs ?? 0) - (run1.latencyMs ?? 0),\n latencyDiffPercent: run1.latencyMs \n ? ((run2.latencyMs ?? 0) - run1.latencyMs) / run1.latencyMs * 100 \n : 0,\n tokenDiff: ((run2.metadata?.totalTokens as number) ?? 0) - ((run1.metadata?.totalTokens as number) ?? 0),\n statusChanged: run1.status !== run2.status,\n inputChanged: JSON.stringify(run1.input) !== JSON.stringify(run2.input),\n outputChanged: JSON.stringify(run1.output) !== JSON.stringify(run2.output),\n },\n };\n }\n\n /**\n * Export a run as a reproducible test case\n */\n exportTestCase(runId: string, sessionId?: string): TestCase {\n const collector = getCollector();\n const run = collector.findRun(runId, sessionId);\n\n if (!run) {\n throw new Error(`Run not found: ${runId}`);\n }\n\n return {\n name: `test_${run.name}_${run.id.slice(0, 8)}`,\n description: `Exported from DevTools trace ${run.id}`,\n type: run.type,\n input: run.input,\n expectedOutput: run.output,\n metadata: run.metadata,\n assertions: [\n { type: 'status', expected: run.status },\n { type: 'latency_max', expected: (run.latencyMs ?? 0) * 1.5 },\n ],\n createdAt: new Date().toISOString(),\n };\n }\n}\n\n/**\n * Run comparison result\n */\nexport interface RunComparison {\n run1: RunSummary;\n run2: RunSummary;\n diff: {\n latencyDiff: number;\n latencyDiffPercent: number;\n tokenDiff: number;\n statusChanged: boolean;\n inputChanged: boolean;\n outputChanged: boolean;\n };\n}\n\ninterface RunSummary {\n id: string;\n name: string;\n type: string;\n latencyMs?: number;\n status: string;\n tokenCount?: number;\n}\n\n/**\n * Exported test case\n */\nexport interface TestCase {\n name: string;\n description: string;\n type: string;\n input: unknown;\n expectedOutput: unknown;\n metadata?: Record<string, unknown>;\n assertions: Array<{\n type: string;\n expected: unknown;\n }>;\n createdAt: string;\n}\n\n/**\n * Create a replay debugger instance\n */\nexport function createReplayDebugger(): ReplayDebugger {\n return new ReplayDebugger();\n}\n\n// Singleton instance\nlet replayDebugger: ReplayDebugger | undefined;\n\n/**\n * Get the global replay debugger instance\n */\nexport function getReplayDebugger(): ReplayDebugger {\n if (!replayDebugger) {\n replayDebugger = new ReplayDebugger();\n }\n return replayDebugger;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA2B;AAcpB,IAAM,iBAAN,MAAqB;AAAA,EAClB,WAAsC,oBAAI,IAAI;AAAA,EAC9C;AAAA,EACA,WAAoC,oBAAI,IAAI;AAAA,EAC5C;AAAA,EACA;AAAA,EACA,YAA8C,oBAAI,IAAI;AAAA,EAE9D,YAAY,SAAyB,CAAC,GAAG;AACvC,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,cAAc,OAAO,eAAe,KAAK,KAAK,KAAK;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,MAAuB;AAClC,UAAM,gBAAY,wBAAW;AAC7B,UAAM,UAAwB;AAAA,MAC5B,IAAI;AAAA,MACJ,MAAM,QAAQ,WAAW,KAAK,SAAS,OAAO,CAAC;AAAA,MAC/C,WAAW,KAAK,IAAI;AAAA,MACpB,MAAM,CAAC;AAAA,IACT;AAEA,SAAK,SAAS,IAAI,WAAW,OAAO;AACpC,SAAK,kBAAkB;AACvB,SAAK,SAAS,IAAI,WAAW,CAAC,CAAC;AAE/B,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,IACF,CAAC;AAED,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAA0B;AACnC,UAAM,KAAK,aAAa,KAAK;AAC7B,QAAI,CAAC,GAAI;AAET,UAAM,UAAU,KAAK,SAAS,IAAI,EAAE;AACpC,QAAI,SAAS;AACX,cAAQ,UAAU,KAAK,IAAI;AAC3B,WAAK,KAAK;AAAA,QACR,MAAM;AAAA,QACN,WAAW,KAAK,IAAI;AAAA,QACpB,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,oBAAoB,IAAI;AAC/B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SACE,MACA,MACA,OACA,UACQ;AACR,UAAM,YAAY,KAAK,mBAAmB,KAAK,aAAa;AAC5D,UAAM,UAAU,KAAK,SAAS,IAAI,SAAS;AAC3C,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AAEzC,UAAM,MAAgB;AAAA,MACpB,QAAI,wBAAW;AAAA,MACf,UAAU,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,EAAE,KAAK;AAAA,MAC1D;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,UAAU,CAAC;AAAA,IACb;AAGA,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,MAAM,SAAS,CAAC,EAAE,SAAS,KAAK,GAAG;AAAA,IAC3C,OAAO;AACL,cAAQ,KAAK,KAAK,GAAG;AAAA,IACvB;AAEA,UAAM,KAAK,GAAG;AAEd,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAED,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,OAAe,QAAkB,UAAgC;AACtE,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAW,MAAM,UAAU,OAAK,EAAE,OAAO,KAAK;AACpD,QAAI,aAAa,GAAI;AAErB,UAAM,MAAM,MAAM,QAAQ;AAC1B,QAAI,UAAU,KAAK,IAAI;AACvB,QAAI,YAAY,IAAI,UAAU,IAAI;AAClC,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,QAAI,UAAU;AACZ,UAAI,WAAW,EAAE,GAAG,IAAI,UAAU,GAAG,SAAS;AAAA,IAChD;AAGA,UAAM,OAAO,UAAU,CAAC;AAExB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,OAAe,OAA6B;AACnD,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,UAAW;AAEhB,UAAM,QAAQ,KAAK,SAAS,IAAI,SAAS;AACzC,QAAI,CAAC,MAAO;AAEZ,UAAM,WAAW,MAAM,UAAU,OAAK,EAAE,OAAO,KAAK;AACpD,QAAI,aAAa,GAAI;AAErB,UAAM,MAAM,MAAM,QAAQ;AAC1B,QAAI,UAAU,KAAK,IAAI;AACvB,QAAI,YAAY,IAAI,UAAU,IAAI;AAClC,QAAI,SAAS;AACb,QAAI,QAAQ,iBAAiB,QAAQ,MAAM,UAAU;AAErD,UAAM,OAAO,UAAU,CAAC;AAExB,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,MACA,OAAO,IAAI;AAAA,IACb,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,cAA8B;AAC5B,WAAO,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAA6C;AACtD,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAAkC;AAC3C,UAAM,WAAW,YACb,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,EAAE,OAAO,OAAO,IAC7C,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAErC,UAAM,UAAwB;AAAA,MAC5B,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,cAAc;AAAA,MACd,aAAa;AAAA,MACb,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAY,CAAC;AAAA,MACb,eAAe,CAAC;AAAA,MAChB,aAAa,CAAC;AAAA,IAChB;AAEA,QAAI,aAAa;AAEjB,UAAM,aAAa,CAAC,QAAkB;AACpC,cAAQ;AACR,cAAQ,kBAAkB,IAAI,aAAa;AAE3C,UAAI,IAAI,WAAW,QAAS;AAG5B,cAAQ,WAAW,IAAI,IAAI,KAAK,QAAQ,WAAW,IAAI,IAAI,KAAK,KAAK;AAGrE,UAAI,IAAI,UAAU;AAChB,cAAM,EAAE,aAAa,MAAM,MAAM,IAAI,IAAI;AACzC,YAAI,aAAa;AACf,kBAAQ,eAAe;AACvB,cAAI,OAAO;AACT,oBAAQ,cAAc,KAAK,KAAK,QAAQ,cAAc,KAAK,KAAK,KAAK;AAAA,UACvE;AAAA,QACF;AACA,YAAI,MAAM;AACR,kBAAQ,aAAa;AACrB,cAAI,OAAO;AACT,oBAAQ,YAAY,KAAK,KAAK,QAAQ,YAAY,KAAK,KAAK,KAAK;AAAA,UACnE;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,SAAS,IAAI,UAAU;AAChC,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAEA,eAAW,WAAW,UAAU;AAC9B,iBAAW,OAAO,QAAQ,MAAM;AAC9B,mBAAW,GAAG;AAAA,MAChB;AAAA,IACF;AAEA,YAAQ,eAAe,QAAQ,YAAY,IACvC,QAAQ,iBAAiB,QAAQ,YACjC;AACJ,YAAQ,YAAY,QAAQ,YAAY,IACpC,aAAa,QAAQ,YACrB;AAEJ,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,OAAe,WAA0C;AAC/D,UAAM,WAAW,YACb,CAAC,KAAK,SAAS,IAAI,SAAS,CAAC,EAAE,OAAO,OAAO,IAC7C,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAErC,UAAM,aAAa,CAAC,SAA2C;AAC7D,iBAAW,OAAO,MAAM;AACtB,YAAI,IAAI,OAAO,MAAO,QAAO;AAC7B,cAAM,QAAQ,WAAW,IAAI,QAAQ;AACrC,YAAI,MAAO,QAAO;AAAA,MACpB;AACA,aAAO;AAAA,IACT;AAEA,eAAW,WAAW,UAAU;AAC9B,YAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC,UAAI,MAAO,QAAO;AAAA,IACpB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,UAAmD;AAC3D,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKQ,KAAK,OAAyB;AACpC,eAAW,YAAY,KAAK,WAAW;AACrC,UAAI;AACF,iBAAS,KAAK;AAAA,MAChB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAgB;AACtB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,KAAK;AAE1B,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,UAAU;AACzC,UAAI,QAAQ,WAAW,QAAQ,UAAU,QAAQ;AAC/C,aAAK,SAAS,OAAO,EAAE;AACvB,aAAK,SAAS,OAAO,EAAE;AAAA,MACzB;AAAA,IACF;AAGA,QAAI,KAAK,SAAS,OAAO,KAAK,WAAW;AACvC,YAAM,SAAS,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC,EAC9C,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS;AAEjD,YAAM,WAAW,OAAO,MAAM,GAAG,KAAK,SAAS,OAAO,KAAK,SAAS;AACpE,iBAAW,CAAC,EAAE,KAAK,UAAU;AAC3B,aAAK,SAAS,OAAO,EAAE;AACvB,aAAK,SAAS,OAAO,EAAE;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,MAAM;AACpB,SAAK,SAAS,MAAM;AACpB,SAAK,kBAAkB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAiB;AACf,WAAO,KAAK,UAAU;AAAA,MACpB,UAAU,MAAM,KAAK,KAAK,SAAS,OAAO,CAAC;AAAA,MAC3C,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC,GAAG,MAAM,CAAC;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAoB;AACzB,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,eAAW,WAAW,KAAK,UAAU;AACnC,WAAK,SAAS,IAAI,QAAQ,IAAI,OAAO;AAAA,IACvC;AAAA,EACF;AACF;AAGA,IAAI;AAEG,SAAS,aAAa,QAAyC;AACpE,MAAI,CAAC,iBAAiB;AACpB,sBAAkB,IAAI,eAAe,MAAM;AAAA,EAC7C;AACA,SAAO;AACT;AAEO,SAAS,iBAAuB;AACrC,oBAAkB;AACpB;;;AC1XA,gBAA6B;AAC7B,iBAA8B;AAC9B,kBAA8B;AAN9B;AAWO,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAA+B,oBAAI,IAAI;AAAA,EACvC;AAAA,EAER,YAAY,WAA2B,SAAyB,CAAC,GAAG;AAClE,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,MACZ,MAAM,OAAO,QAAQ;AAAA,MACrB,MAAM,OAAO,QAAQ;AAAA,MACrB,MAAM,OAAO,QAAQ;AAAA,IACvB;AACA,SAAK,gBAAgB,KAAK,kBAAkB;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA4B;AAClC,QAAI;AACF,YAAM,iBAAa,0BAAc,YAAY,GAAG;AAChD,YAAM,gBAAY,qBAAQ,UAAU;AACpC,iBAAO,4BAAa,kBAAK,WAAW,gBAAgB,GAAG,OAAO;AAAA,IAChE,QAAQ;AAEN,aAAO,KAAK,uBAAuB;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAE3B,QAAI;AACJ,QAAI;AAEJ,QAAI;AACF,sBAAgB,MAAM,OAAO,SAAS;AACtC,mBAAa,MAAM,OAAO,MAAM;AAAA,IAClC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,cAAc,QAAQ;AAGlC,QAAI,KAAK,OAAO,MAAM;AACpB,UAAI,IAAI,CAAC,MAAe,KAAe,SAAuB;AAC5D,YAAI,OAAO,+BAA+B,GAAG;AAC7C,YAAI,OAAO,gCAAgC,cAAc;AACzD,YAAI,OAAO,gCAAgC,mBAAmB;AAC9D,aAAK;AAAA,MACP,CAAC;AAAA,IACH;AAEA,QAAI,IAAI,cAAc,KAAK,CAAyE;AAGpG,SAAK,YAAY,GAAG;AAGpB,SAAK,SAAS,WAAW,aAAa,GAAG;AAGzC,UAAM,IAAI,QAAc,CAAC,YAAY;AACnC,WAAK,OAAQ,OAAO,KAAK,OAAO,MAAM,KAAK,OAAO,MAAM,MAAM;AAC5D,gBAAQ,IAAI;AAAA,8CAA0C,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO,IAAI;AAAA,CAAI;AAC9F,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAGD,SAAK,UAAU,UAAU,CAAC,UAAU;AAClC,WAAK,eAAe,KAAK;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAK,OAAQ,MAAM,CAAC,QAAQ;AAC1B,cAAI,IAAK,QAAO,GAAG;AAAA,cACd,SAAQ;AAAA,QACf,CAAC;AAAA,MACH,CAAC;AACD,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,KAAwB;AAE1C,QAAI,IAAI,eAAe,CAAC,MAAe,QAAkB;AACvD,UAAI,KAAK,EAAE,QAAQ,MAAM,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,IAClD,CAAC;AAGD,QAAI,IAAI,iBAAiB,CAAC,MAAe,QAAkB;AACzD,YAAM,WAAW,KAAK,UAAU,YAAY;AAC5C,UAAI,KAAK,QAAQ;AAAA,IACnB,CAAC;AAGD,QAAI,IAAI,qBAAqB,CAAC,KAAc,QAAkB;AAC5D,YAAM,UAAU,KAAK,UAAU,WAAW,IAAI,OAAO,EAAE;AACvD,UAAI,CAAC,SAAS;AACZ,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,MACF;AACA,UAAI,KAAK,OAAO;AAAA,IAClB,CAAC;AAGD,QAAI,IAAI,gBAAgB,CAAC,KAAc,QAAkB;AACvD,YAAM,YAAY,IAAI,MAAM;AAC5B,YAAM,UAAU,KAAK,UAAU,WAAW,SAAS;AACnD,UAAI,KAAK,OAAO;AAAA,IAClB,CAAC;AAGD,QAAI,IAAI,iBAAiB,CAAC,KAAc,QAAkB;AACxD,YAAM,YAAY,IAAI,MAAM;AAC5B,YAAM,MAAM,KAAK,UAAU,QAAQ,IAAI,OAAO,IAAI,SAAS;AAC3D,UAAI,CAAC,KAAK;AACR,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,MACF;AACA,UAAI,KAAK,GAAG;AAAA,IACd,CAAC;AAGD,QAAI,OAAO,iBAAiB,CAAC,MAAe,QAAkB;AAC5D,WAAK,UAAU,MAAM;AACrB,UAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,IAC5B,CAAC;AAGD,QAAI,IAAI,eAAe,CAAC,MAAe,QAAkB;AACvD,YAAM,OAAO,KAAK,UAAU,OAAO;AACnC,UAAI,UAAU,gBAAgB,kBAAkB;AAChD,UAAI,UAAU,uBAAuB,uCAAuC;AAC5E,UAAI,KAAK,IAAI;AAAA,IACf,CAAC;AAGD,QAAI,KAAK,eAAe,CAAC,KAAc,QAAkB;AACvD,UAAI;AACF,aAAK,UAAU,OAAO,KAAK,UAAU,IAAI,IAAI,CAAC;AAC9C,YAAI,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,MAC5B,QAAQ;AACN,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAAA,MACtD;AAAA,IACF,CAAC;AAGD,QAAI,IAAI,eAAe,CAAC,KAAc,QAAkB;AACtD,UAAI,UAAU,gBAAgB,mBAAmB;AACjD,UAAI,UAAU,iBAAiB,UAAU;AACzC,UAAI,UAAU,cAAc,YAAY;AAExC,WAAK,QAAQ,IAAI,GAAgC;AAEjD,UAAI,GAAG,SAAS,MAAM;AACpB,aAAK,QAAQ,OAAO,GAAgC;AAAA,MACtD,CAAC;AAAA,IACH,CAAC;AAGD,QAAI,IAAI,KAAK,CAAC,MAAe,QAAkB;AAC7C,UAAI,KAAK,KAAK,aAAa;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,OAAyB;AAC9C,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,eAAW,UAAU,KAAK,SAAS;AACjC,aAAO,MAAM,SAAS,IAAI;AAAA;AAAA,CAAM;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,yBAAiC;AACvC,WAAO;AAAA,EACT;AACF;;;ACrKA,SAAS,aAAa,MAA4B;AAChD,QAAM,UAAwC;AAAA,IAC5C,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS;AAAA,IACT,cAAc;AAAA,IACd,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,QAAQ;AAAA,EACV;AACA,SAAO,QAAQ,KAAK,YAAY,CAAC,KAAK;AACxC;AAiBO,SAAS,qBAAwC;AACtD,QAAM,YAAY,aAAa;AAC/B,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,QAAM,aAAa,oBAAI,IAAoB;AAE3C,SAAO;AAAA,IACL,aAAaA,QAAoB;AAE/B,YAAM,YAAY,UAAU,aAAaA,OAAM,IAAI;AACnD,qBAAe,IAAIA,OAAM,IAAI,SAAS;AAAA,IACxC;AAAA,IAEA,WAAWA,QAAoB;AAC7B,YAAM,YAAY,eAAe,IAAIA,OAAM,EAAE;AAC7C,UAAI,WAAW;AACb,kBAAU,WAAW,SAAS;AAC9B,uBAAe,OAAOA,OAAM,EAAE;AAAA,MAChC;AAAA,IACF;AAAA,IAEA,QAAQ,OAAoB;AAC1B,YAAM,UAAU,aAAa,MAAM,IAAI;AAGvC,YAAM,WAA0B;AAAA,QAC9B,GAAG,MAAM;AAAA,MACX;AAEA,UAAI,MAAM,OAAO;AACf,iBAAS,eAAe,MAAM,MAAM;AACpC,iBAAS,mBAAmB,MAAM,MAAM;AACxC,iBAAS,cAAc,MAAM,MAAM;AAAA,MACrC;AAGA,UAAI,MAAM,aAAa,MAAM,SAAS;AACpC,cAAM,QAAQ,UAAU,SAAS,SAAS,MAAM,MAAM,MAAM,OAAO,QAAQ;AAC3E,kBAAU,OAAO,OAAO,MAAM,QAAQ;AAAA,UACpC,GAAG;AAAA;AAAA,QAEL,CAAC;AACD,mBAAW,IAAI,MAAM,IAAI,KAAK;AAAA,MAChC,WAAW,MAAM,aAAa,CAAC,MAAM,SAAS;AAE5C,cAAM,QAAQ,UAAU,SAAS,SAAS,MAAM,MAAM,MAAM,OAAO,QAAQ;AAC3E,mBAAW,IAAI,MAAM,IAAI,KAAK;AAAA,MAChC,OAAO;AAEL,cAAM,QAAQ,WAAW,IAAI,MAAM,EAAE;AACrC,YAAI,OAAO;AACT,oBAAU,OAAO,OAAO,MAAM,QAAQ,QAAQ;AAC9C,qBAAW,OAAO,MAAM,EAAE;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAAA,IAEA,QAAQ,OAAc,SAAmC;AAEvD,YAAM,WAAW,UAAU,YAAY;AACvC,UAAI,SAAS,SAAS,GAAG;AACvB,cAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,YAAI,YAAY,KAAK,SAAS,GAAG;AAC/B,gBAAM,iBAAiB,CAAC,SAAyF;AAC/G,uBAAW,OAAO,MAAM;AACtB,kBAAI,IAAI,WAAW,UAAW,QAAO,IAAI;AACzC,oBAAM,WAAW,eAAe,IAAI,QAAsE;AAC1G,kBAAI,SAAU,QAAO;AAAA,YACvB;AACA,mBAAO;AAAA,UACT;AAEA,gBAAM,eAAe,eAAe,YAAY,IAAkE;AAClH,cAAI,cAAc;AAChB,sBAAU,SAAS,cAAc,KAAK;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAGA,UAAI,SAAS;AACX,gBAAQ,MAAM,6BAA6B,OAAO;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,yBAAyB,UAIrC,CAAC,GAGH;AACA,QAAM,OAAO,mBAAmB;AAChC,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,MACN,GAAG;AAAA,MACH,OAAO,CAAC,IAAI;AAAA,IACd;AAAA,EACF;AACF;;;ACrJO,IAAM,wBAAN,MAA4B;AAAA,EACzB;AAAA,EACA,aAAyB,CAAC;AAAA,EAC1B;AAAA,EACA;AAAA,EAER,YAAY,QAA6B;AACvC,SAAK,SAAS;AAAA,MACZ,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO,eAAe;AAAA,MACnC,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,SAAS,OAAO,WAAW,CAAC;AAAA,MAC5B,WAAW,OAAO,aAAa;AAAA,MAC/B,iBAAiB,OAAO,mBAAmB;AAAA,MAC3C,SAAS,OAAO,WAAW;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,UAAM,YAAY,aAAa;AAE/B,SAAK,cAAc,UAAU,UAAU,CAAC,UAAsB;AAC5D,UAAI,MAAM,SAAS,aAAa,MAAM,KAAK;AACzC,aAAK,QAAQ,MAAM,KAAK,MAAM,SAAS;AAAA,MACzC;AAAA,IACF,CAAC;AAGD,SAAK,aAAa,YAAY,MAAM;AAClC,WAAK,MAAM;AAAA,IACb,GAAG,KAAK,OAAO,eAAe;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI,KAAK,YAAY;AACnB,oBAAc,KAAK,UAAU;AAC7B,WAAK,aAAa;AAAA,IACpB;AAEA,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY;AACjB,WAAK,cAAc;AAAA,IACrB;AAGA,UAAM,KAAK,MAAM;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQ,KAAe,SAAuB;AACpD,UAAM,OAAO,KAAK,UAAU,KAAK,OAAO;AACxC,SAAK,WAAW,KAAK,IAAI;AAGzB,eAAW,SAAS,IAAI,UAAU;AAChC,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAGA,QAAI,KAAK,WAAW,UAAU,KAAK,OAAO,WAAW;AACnD,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAU,KAAe,SAA2B;AAC1D,UAAM,aAAqC;AAAA,MACzC,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE;AAAA,MACzD,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE;AAAA,MACzD,EAAE,KAAK,mBAAmB,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE;AAAA,IAC/D;AAGA,QAAI,IAAI,UAAU;AAChB,UAAI,IAAI,SAAS,OAAO;AACtB,mBAAW,KAAK,EAAE,KAAK,aAAa,OAAO,EAAE,aAAa,IAAI,SAAS,MAAM,EAAE,CAAC;AAAA,MAClF;AACA,UAAI,IAAI,SAAS,UAAU;AACzB,mBAAW,KAAK,EAAE,KAAK,gBAAgB,OAAO,EAAE,aAAa,IAAI,SAAS,SAAS,EAAE,CAAC;AAAA,MACxF;AACA,UAAI,IAAI,SAAS,gBAAgB,QAAW;AAC1C,mBAAW,KAAK,EAAE,KAAK,oBAAoB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,WAAW,EAAE,EAAE,CAAC;AAAA,MACpG;AACA,UAAI,IAAI,SAAS,iBAAiB,QAAW;AAC3C,mBAAW,KAAK,EAAE,KAAK,qBAAqB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,YAAY,EAAE,EAAE,CAAC;AAAA,MACtG;AACA,UAAI,IAAI,SAAS,qBAAqB,QAAW;AAC/C,mBAAW,KAAK,EAAE,KAAK,yBAAyB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,gBAAgB,EAAE,EAAE,CAAC;AAAA,MAC9G;AACA,UAAI,IAAI,SAAS,SAAS,QAAW;AACnC,mBAAW,KAAK,EAAE,KAAK,YAAY,OAAO,EAAE,aAAa,IAAI,SAAS,KAAK,EAAE,CAAC;AAAA,MAChF;AACA,UAAI,IAAI,SAAS,UAAU;AACzB,mBAAW,KAAK,EAAE,KAAK,aAAa,OAAO,EAAE,aAAa,IAAI,SAAS,SAAS,EAAE,CAAC;AAAA,MACrF;AAAA,IACF;AAGA,QAAI,IAAI,cAAc,QAAW;AAC/B,iBAAW,KAAK,EAAE,KAAK,mBAAmB,OAAO,EAAE,UAAU,OAAO,IAAI,SAAS,EAAE,EAAE,CAAC;AAAA,IACxF;AAGA,QAAI,IAAI,OAAO;AACb,iBAAW,KAAK,EAAE,KAAK,iBAAiB,OAAO,EAAE,aAAa,IAAI,MAAM,EAAE,CAAC;AAAA,IAC7E;AAEA,WAAO;AAAA,MACL,SAAS,KAAK,MAAM,SAAS,EAAE;AAAA,MAC/B,QAAQ,KAAK,MAAM,IAAI,IAAI,EAAE;AAAA,MAC7B,cAAc,IAAI,WAAW,KAAK,MAAM,IAAI,UAAU,EAAE,IAAI;AAAA,MAC5D,MAAM,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AAAA,MAC7B,MAAM,KAAK,YAAY,IAAI,IAAI;AAAA,MAC/B,mBAAmB,OAAO,IAAI,YAAY,GAAS;AAAA,MACnD,iBAAiB,QAAQ,IAAI,WAAW,IAAI,aAAa,GAAS;AAAA,MAClE;AAAA,MACA,QAAQ;AAAA,QACN,MAAM,IAAI,WAAW,UAAU,IAAI;AAAA,QACnC,SAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,MAA4B;AAC9C,YAAQ,MAAM;AAAA,MACZ,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,MAAM,IAAY,QAAwB;AAEhD,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,YAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,cAAS,QAAQ,KAAK,OAAQ;AAC9B,aAAO,OAAO;AAAA,IAChB;AACA,UAAM,MAAM,KAAK,IAAI,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,QAAQ,GAAG;AAC5D,WAAO,IAAI,MAAM,GAAG,MAAM;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,WAAW,WAAW,EAAG;AAElC,UAAM,QAAQ,CAAC,GAAG,KAAK,UAAU;AACjC,SAAK,aAAa,CAAC;AAEnB,UAAM,UAAU;AAAA,MACd,eAAe,CAAC;AAAA,QACd,UAAU;AAAA,UACR,YAAY;AAAA,YACV,EAAE,KAAK,gBAAgB,OAAO,EAAE,aAAa,KAAK,OAAO,YAAY,EAAE;AAAA,YACvE,EAAE,KAAK,mBAAmB,OAAO,EAAE,aAAa,KAAK,OAAO,eAAe,EAAE;AAAA,YAC7E,EAAE,KAAK,sBAAsB,OAAO,EAAE,aAAa,oBAAoB,EAAE;AAAA,YACzE,EAAE,KAAK,yBAAyB,OAAO,EAAE,aAAa,QAAQ,EAAE;AAAA,UAClE;AAAA,QACF;AAAA,QACA,YAAY,CAAC;AAAA,UACX,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc;AAAA,QAChE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAG,KAAK,OAAO;AAAA,QACjB;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,gBAAQ,MAAM,kCAAkC,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAExF,aAAK,WAAW,QAAQ,GAAG,KAAK;AAAA,MAClC;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,iCAAiC,KAAK;AAEpD,WAAK,WAAW,QAAQ,GAAG,KAAK;AAAA,IAClC;AAAA,EACF;AACF;AAKO,SAAS,mBAAmB,QAAoD;AACrF,QAAM,WAAW,IAAI,sBAAsB,MAAM;AACjD,WAAS,MAAM;AACf,SAAO;AACT;;;ACjQO,IAAM,iBAAN,MAAqB;AAAA;AAAA;AAAA;AAAA,EAI1B,MAAM,OAAO,SAA+C;AAC1D,UAAM,YAAY,aAAa;AAC/B,UAAM,cAAc,UAAU,QAAQ,QAAQ,OAAO,QAAQ,SAAS;AAEtE,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,kBAAkB,QAAQ,KAAK,EAAE;AAAA,IACnD;AAGA,UAAM,gBAAgB,QAAQ,cAC1B,QAAQ,YAAY,YAAY,KAAK,IACrC,YAAY;AAGhB,UAAM,kBAAkB,UAAU,aAAa,WAAW,YAAY,IAAI,EAAE;AAG5E,UAAM,cAAc,UAAU;AAAA,MAC5B,YAAY;AAAA,MACZ,UAAU,YAAY,IAAI;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,GAAG,YAAY;AAAA,QACf,UAAU,YAAY;AAAA,QACtB,mBAAmB,QAAQ;AAAA,MAC7B;AAAA,IACF;AAIA,UAAM,iBAAiB,YAAY;AAEnC,cAAU,OAAO,aAAa,gBAAgB,YAAY,QAAQ;AAClE,cAAU,WAAW,eAAe;AAGpC,UAAM,cAAc,UAAU,QAAQ,WAAW;AAEjD,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAGA,UAAM,eAAe,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,UAAU,aAAa;AACvF,UAAM,gBAAgB,KAAK,UAAU,YAAY,MAAM,MAAM,KAAK,UAAU,YAAY,MAAM;AAC9F,UAAM,eAAe,YAAY,aAAa,MAAM,YAAY,aAAa;AAE7E,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,OAAe,WAA4B;AAC9C,UAAM,YAAY,aAAa;AAC/B,UAAM,cAAc,UAAU,QAAQ,OAAO,SAAS;AAEtD,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,kBAAkB,KAAK,EAAE;AAAA,IAC3C;AAGA,UAAM,gBAAgB,UAAU,aAAa,SAAS,YAAY,IAAI,EAAE;AAGxE,SAAK,aAAa,aAAa,aAAa;AAE5C,cAAU,WAAW,aAAa;AAElC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,KAAe,YAAoB,UAA2B;AACjF,UAAM,YAAY,aAAa;AAE/B,UAAM,WAAW,UAAU;AAAA,MACzB,IAAI;AAAA,MACJ,QAAQ,IAAI,IAAI;AAAA,MAChB,IAAI;AAAA,MACJ;AAAA,QACE,GAAG,IAAI;AAAA,QACP,YAAY,IAAI;AAAA,QAChB,kBAAkB;AAAA,MACpB;AAAA,IACF;AAGA,eAAW,SAAS,IAAI,UAAU;AAChC,WAAK,aAAa,OAAO,YAAY,QAAQ;AAAA,IAC/C;AAGA,QAAI,IAAI,WAAW,SAAS;AAC1B,gBAAU,SAAS,UAAU,IAAI,SAAS,eAAe;AAAA,IAC3D,OAAO;AACL,gBAAU,OAAO,UAAU,IAAI,QAAQ,IAAI,QAAQ;AAAA,IACrD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,QAAgB,QAAgB,WAAmC;AACzE,UAAM,YAAY,aAAa;AAC/B,UAAM,OAAO,UAAU,QAAQ,QAAQ,SAAS;AAChD,UAAM,OAAO,UAAU,QAAQ,QAAQ,SAAS;AAEhD,QAAI,CAAC,QAAQ,CAAC,MAAM;AAClB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,QACJ,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,YAAY,KAAK,UAAU;AAAA,MAC7B;AAAA,MACA,MAAM;AAAA,QACJ,IAAI,KAAK;AAAA,QACT,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,WAAW,KAAK;AAAA,QAChB,QAAQ,KAAK;AAAA,QACb,YAAY,KAAK,UAAU;AAAA,MAC7B;AAAA,MACA,MAAM;AAAA,QACJ,cAAc,KAAK,aAAa,MAAM,KAAK,aAAa;AAAA,QACxD,oBAAoB,KAAK,cACnB,KAAK,aAAa,KAAK,KAAK,aAAa,KAAK,YAAY,MAC5D;AAAA,QACJ,YAAa,KAAK,UAAU,eAA0B,MAAO,KAAK,UAAU,eAA0B;AAAA,QACtG,eAAe,KAAK,WAAW,KAAK;AAAA,QACpC,cAAc,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK;AAAA,QACtE,eAAe,KAAK,UAAU,KAAK,MAAM,MAAM,KAAK,UAAU,KAAK,MAAM;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,OAAe,WAA8B;AAC1D,UAAM,YAAY,aAAa;AAC/B,UAAM,MAAM,UAAU,QAAQ,OAAO,SAAS;AAE9C,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,kBAAkB,KAAK,EAAE;AAAA,IAC3C;AAEA,WAAO;AAAA,MACL,MAAM,QAAQ,IAAI,IAAI,IAAI,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC;AAAA,MAC5C,aAAa,gCAAgC,IAAI,EAAE;AAAA,MACnD,MAAM,IAAI;AAAA,MACV,OAAO,IAAI;AAAA,MACX,gBAAgB,IAAI;AAAA,MACpB,UAAU,IAAI;AAAA,MACd,YAAY;AAAA,QACV,EAAE,MAAM,UAAU,UAAU,IAAI,OAAO;AAAA,QACvC,EAAE,MAAM,eAAe,WAAW,IAAI,aAAa,KAAK,IAAI;AAAA,MAC9D;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAAA,EACF;AACF;AA+CO,SAAS,uBAAuC;AACrD,SAAO,IAAI,eAAe;AAC5B;AAGA,IAAI;AAKG,SAAS,oBAAoC;AAClD,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,IAAI,eAAe;AAAA,EACtC;AACA,SAAO;AACT;;;AL7OA,eAAsB,SAAS,SAAyB,CAAC,GAItD;AACD,QAAM,YAAY,aAAa,MAAM;AACrC,QAAM,SAAS,IAAI,eAAe,WAAW,MAAM;AAEnD,QAAM,OAAO,MAAM;AAGnB,MAAI,OAAO,SAAS,OAAO;AACzB,UAAM,MAAM,UAAU,OAAO,QAAQ,WAAW,IAAI,OAAO,QAAQ,IAAI;AACvE,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAe;AAC7C,YAAM,UAAU,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAAU;AACzD,WAAK,GAAG,OAAO,IAAI,GAAG,EAAE;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,MAAM,OAAO,KAAK;AAAA,EAC1B;AACF;AAKO,SAAS,UACd,IACA,UAII,CAAC,GACF;AACH,QAAM,YAAY,QAAQ,aAAa,aAAa;AACpD,QAAM,OAAO,QAAQ,QAAQ,GAAG,QAAQ;AACxC,QAAM,OAAO,QAAQ,QAAQ;AAE7B,UAAQ,UAAU,SAAoB;AACpC,UAAM,QAAQ,UAAU,SAAS,MAAM,MAAM,IAAI;AACjD,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,gBAAU,OAAO,OAAO,MAAM;AAC9B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,gBAAU,SAAS,OAAO,KAAc;AACxC,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAKO,SAAS,MAAM,UAGlB,CAAC,GAAG;AACN,SAAO,SACL,SACA,aACA,YACA;AACA,UAAM,iBAAiB,WAAW;AAClC,UAAM,OAAO,QAAQ,QAAQ;AAC7B,UAAM,OAAO,QAAQ,QAAQ;AAE7B,eAAW,QAAQ,kBAAmB,MAAiB;AACrD,YAAM,YAAY,aAAa;AAC/B,YAAM,QAAQ,UAAU,SAAS,MAAM,MAAM,IAAI;AACjD,UAAI;AACF,cAAM,SAAS,MAAM,eAAe,MAAM,MAAM,IAAI;AACpD,kBAAU,OAAO,OAAO,MAAM;AAC9B,eAAO;AAAA,MACT,SAAS,OAAO;AACd,kBAAU,SAAS,OAAO,KAAc;AACxC,cAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,IAAM,QAAQ;AAAA,EACnB,MAAM,MAAoB,MAAc,OAAiB,UAAkC;AACzF,WAAO,aAAa,EAAE,SAAS,MAAM,MAAM,OAAO,QAAQ;AAAA,EAC5D;AAAA,EAEA,IAAI,OAAe,QAAkB,UAAgC;AACnE,iBAAa,EAAE,OAAO,OAAO,QAAQ,QAAQ;AAAA,EAC/C;AAAA,EAEA,MAAM,OAAe,OAA6B;AAChD,iBAAa,EAAE,SAAS,OAAO,KAAK;AAAA,EACtC;AAAA,EAEA,QAAQ,MAAuB;AAC7B,WAAO,aAAa,EAAE,aAAa,IAAI;AAAA,EACzC;AAAA,EAEA,WAAW,WAA0B;AACnC,iBAAa,EAAE,WAAW,SAAS;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KACJ,MACA,MACA,IACA,UACY;AACZ,UAAM,QAAQ,aAAa,EAAE,SAAS,MAAM,MAAM,QAAW,QAAQ;AACrE,QAAI;AACF,YAAM,SAAS,MAAM,GAAG;AACxB,mBAAa,EAAE,OAAO,OAAO,MAAM;AACnC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,mBAAa,EAAE,SAAS,OAAO,KAAc;AAC7C,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":["trace"]}
package/dist/index.d.cts CHANGED
@@ -170,7 +170,12 @@ declare class DevToolsServer {
170
170
  private config;
171
171
  private server?;
172
172
  private clients;
173
+ private dashboardHTML;
173
174
  constructor(collector: TraceCollector, config?: DevToolsConfig);
175
+ /**
176
+ * Load dashboard HTML from file
177
+ */
178
+ private loadDashboardHTML;
174
179
  /**
175
180
  * Start the DevTools server
176
181
  */
@@ -188,9 +193,9 @@ declare class DevToolsServer {
188
193
  */
189
194
  private broadcastEvent;
190
195
  /**
191
- * Get embedded dashboard HTML
196
+ * Inline fallback dashboard HTML
192
197
  */
193
- private getDashboardHTML;
198
+ private getInlineDashboardHTML;
194
199
  }
195
200
 
196
201
  /**