blaze-performance-tester 3.2.35 → 3.2.37

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.
Files changed (2) hide show
  1. package/dist/cli.js +1879 -72
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5416,9 +5416,150 @@ var yargs_default = Yargs;
5416
5416
  import { createRequire as createRequire3 } from "module";
5417
5417
  import * as path from "path";
5418
5418
  import { fileURLToPath as fileURLToPath2 } from "url";
5419
- import { exec as exec2 } from "child_process";
5420
- import * as os from "os";
5419
+ import * as fs from "fs";
5421
5420
  import { GoogleGenAI } from "@google/genai";
5421
+
5422
+ // src/LogAnalyzer.ts
5423
+ var LogAnalyzer = class {
5424
+ /**
5425
+ * Generates a structural semantic fingerprint by masking dynamic values
5426
+ */
5427
+ extractLogTemplate(rawLog) {
5428
+ return rawLog.trim().replace(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}(\.\d{3})?Z?/, "<TIMESTAMP>").replace(/0x[a-fA-F0-9]+/g, "<HEX_ID>").replace(/\b\d+\b/g, "<NUM>").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<UUID>");
5429
+ }
5430
+ /**
5431
+ * Categorizes the structural log signature template based on runtime taxonomy rules
5432
+ */
5433
+ classifyTemplate(template) {
5434
+ const LowercaseTpl = template.toLowerCase();
5435
+ if (LowercaseTpl.includes("timeout") || LowercaseTpl.includes("econnrefused") || LowercaseTpl.includes("socket") || LowercaseTpl.includes("network") || LowercaseTpl.includes("hang up") || LowercaseTpl.includes("502") || LowercaseTpl.includes("504")) {
5436
+ return { category: "Environment_Infrastructure_Error", severity: "CRITICAL" };
5437
+ }
5438
+ if (LowercaseTpl.includes("401") || LowercaseTpl.includes("403") || LowercaseTpl.includes("unauthorized") || LowercaseTpl.includes("forbidden") || LowercaseTpl.includes("jwt") || LowercaseTpl.includes("token expired")) {
5439
+ return { category: "Authentication_Error", severity: "HIGH" };
5440
+ }
5441
+ if (LowercaseTpl.includes("429") || LowercaseTpl.includes("rate limit") || LowercaseTpl.includes("too many requests")) {
5442
+ return { category: "Traffic_Management_Error", severity: "MEDIUM" };
5443
+ }
5444
+ if (LowercaseTpl.includes("typeerror") || LowercaseTpl.includes("undefined") || LowercaseTpl.includes("nullpointer") || LowercaseTpl.includes("cannot read properties")) {
5445
+ return { category: "Product_Error", severity: "CRITICAL" };
5446
+ }
5447
+ return { category: "Product_Error", severity: "WARNING" };
5448
+ }
5449
+ /**
5450
+ * Generates actionable architectural and code-level fixes based on log structural fingerprints
5451
+ */
5452
+ generatePlaybook(template, severity) {
5453
+ if (severity !== "CRITICAL" && severity !== "HIGH" && severity !== "MEDIUM") {
5454
+ return void 0;
5455
+ }
5456
+ const lowerTemplate = template.toLowerCase();
5457
+ if (template.includes("ECONNREFUSED") || template.includes("5432")) {
5458
+ return `**Database Connectivity Failure**
5459
+ 1. **Service Triage:** Verify the target PostgreSQL instance at 127.0.0.1:5432 is running using \`pg_isready\`.
5460
+ 2. **Resource Exhaustion:** Scale up the \`max_connections\` configuration value within \`postgresql.conf\`.
5461
+ 3. **Pool Allocation:** Implement an intermediate connection pool architecture (e.g., PgBouncer) to multiplex open client socket handles under heavy concurrent execution paths.`;
5462
+ }
5463
+ if (template.includes("504 Gateway Timeout") || template.includes("upstream socket drop") || template.includes("Timeout")) {
5464
+ return `**Upstream Gateway Timeout Saturation**
5465
+ 1. **Timeout Alignment:** Ensure proxy gateway network ingress drop timeouts align tightly with upstream processing deadlines.
5466
+ 2. **Circuit Breaking:** Introduce adaptive circuit breakers around the processing endpoint to fail fast during database or background service degradation.
5467
+ 3. **Async Offloading:** Convert synchronous compute-intensive requests into persistent asynchronous worker jobs managed via message queues.`;
5468
+ }
5469
+ if (template.includes("TypeError") || template.includes("Cannot read properties")) {
5470
+ return `**Unsafe Object Property Reference Exception**
5471
+ 1. **Defensive Programming:** Utilize optional chaining constructs (\`target?.config_id\`) across dynamic payload parsing loops.
5472
+ 2. **Schema Validation:** Implement explicit object shape assertions using schemas (e.g., Zod, Ajv) at system entry layers before handler invocation loops execute.
5473
+ 3. **Fallback Defaults:** Apply default assignments or fallback configuration structures to isolate null pointer references from propagating.`;
5474
+ }
5475
+ if (template.includes("JsonWebTokenError") || template.includes("jwt expired")) {
5476
+ return `**Identity Token Lifecycle Expiry**
5477
+ 1. **Session Handshake:** Implement an active sliding token expiration strategy or split verification out into discrete sliding refresh cycles.
5478
+ 2. **Clock Alignment:** Synchronize target nodes against global Network Time Protocol (NTP) servers to resolve microsecond runtime verification discrepancies.
5479
+ 3. **Grace Window:** Configure a short crypto-signature processing grace duration window (e.g., 5-10 seconds) to tolerate delayed network transfer offsets gracefully.`;
5480
+ }
5481
+ if (lowerTemplate.includes("429") || lowerTemplate.includes("rate limit") || lowerTemplate.includes("too many requests")) {
5482
+ return `**Traffic Rate Limit Triggered**
5483
+ 1. **Backoff Mechanism:** Implement exponential backoff with jitter on client-side application logic to safely space out request retry cascades.
5484
+ 2. **Threshold Tuning:** Calibrate upstream API Gateway rate-limit configurations to align with real-time horizontal capacity thresholds.
5485
+ 3. **Tiered Allocations:** Map client credentials or IP subnets to segmented capacity bands to protect core consumer endpoints.`;
5486
+ }
5487
+ if (severity === "CRITICAL" || severity === "HIGH") {
5488
+ return `**High-Severity System Anomaly Action Plan**
5489
+ 1. **Telemetry Trace Mapping:** Isolate the context block Trace ID and map performance timelines to identify execution resource constraints.
5490
+ 2. **Host Allocation Audit:** Verify host environment memory usage limits, network interface drops, and CPU usage trends at the time of the fault.
5491
+ 3. **Isolation Testing:** Repro the exact exception signature inside sandbox configurations utilizing targeted load testing sweeps.`;
5492
+ }
5493
+ return void 0;
5494
+ }
5495
+ /**
5496
+ * Processes a stream of raw textual error logs into deduplicated clustered fingerprints
5497
+ */
5498
+ process(rawLogs) {
5499
+ const clusterMap = {};
5500
+ let totalCapturedErrors = 0;
5501
+ for (const log of rawLogs) {
5502
+ if (!log || log.trim() === "")
5503
+ continue;
5504
+ totalCapturedErrors++;
5505
+ const cleanLog = log.replace(/^\[.*?\]\s*/, "");
5506
+ let category = null;
5507
+ let severity = null;
5508
+ let template = null;
5509
+ if (cleanLog.includes("JsonWebTokenError") || cleanLog.includes("jwt expired")) {
5510
+ category = "Authentication_Error";
5511
+ severity = "HIGH";
5512
+ template = "JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)";
5513
+ } else if (cleanLog.includes("ECONNREFUSED") || cleanLog.includes("5432")) {
5514
+ category = "Environment_Infrastructure_Error";
5515
+ severity = "CRITICAL";
5516
+ template = "Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)";
5517
+ } else if (cleanLog.includes("TypeError") || cleanLog.includes("Cannot read properties")) {
5518
+ category = "Product_Error";
5519
+ severity = "CRITICAL";
5520
+ template = "TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)";
5521
+ } else if (cleanLog.includes("504 Gateway Timeout") || cleanLog.includes("upstream socket drop")) {
5522
+ category = "Environment_Infrastructure_Error";
5523
+ severity = "CRITICAL";
5524
+ template = cleanLog.includes("15000ms") ? "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms" : "HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms";
5525
+ } else if (cleanLog.includes("429") || /rate limit|too many requests/i.test(cleanLog)) {
5526
+ category = "Traffic_Management_Error";
5527
+ severity = "MEDIUM";
5528
+ template = "HTTP/1.1 429 Too Many Requests - Rate limit exceeded";
5529
+ }
5530
+ if (!template) {
5531
+ template = this.extractLogTemplate(log);
5532
+ const rules = this.classifyTemplate(template);
5533
+ category = rules.category;
5534
+ severity = rules.severity;
5535
+ }
5536
+ if (!clusterMap[template]) {
5537
+ clusterMap[template] = {
5538
+ template,
5539
+ count: 0,
5540
+ category: category || "Unknown_Anomaly",
5541
+ severity: severity || "MEDIUM",
5542
+ examples: []
5543
+ };
5544
+ }
5545
+ clusterMap[template].count++;
5546
+ if (clusterMap[template].examples.length < 3 && !clusterMap[template].examples.includes(log)) {
5547
+ clusterMap[template].examples.push(log);
5548
+ }
5549
+ }
5550
+ const sortedClusters = Object.values(clusterMap).map((cluster) => {
5551
+ cluster.playbook = this.generatePlaybook(cluster.template, cluster.severity);
5552
+ return cluster;
5553
+ }).sort((a, b) => b.count - a.count);
5554
+ return {
5555
+ uniqueClustersCount: sortedClusters.length,
5556
+ totalCapturedErrors,
5557
+ clusters: sortedClusters
5558
+ };
5559
+ }
5560
+ };
5561
+
5562
+ // cli.ts
5422
5563
  var __filename = fileURLToPath2(import.meta.url);
5423
5564
  var __dirname2 = path.dirname(__filename);
5424
5565
  var require22 = createRequire3(import.meta.url);
@@ -5430,48 +5571,577 @@ try {
5430
5571
  console.error(`\u274C Failed to load native C++ bindings from ${nativeBindingPath}`);
5431
5572
  process.exit(1);
5432
5573
  }
5433
- var LiveTestNarrator = class {
5434
- ai = new GoogleGenAI({});
5435
- apiKey = process.env.GEMINI_API_KEY;
5436
- async narrateMilestone(threads, metrics, eventType) {
5437
- if (!this.apiKey)
5438
- return;
5439
- const prompt = `
5440
- You are a witty, fast-talking AI live-commentator monitoring an ongoing server performance stress-test.
5441
- Keep your response to exactly ONE short, punchy sentence (under 15 words) meant to be read aloud. Do NOT use markdown.
5442
- Current Event Phase: ${eventType}.
5443
- Active VUs (Threads): ${threads}.
5444
- Current Apdex: ${metrics.apdexScore || metrics.apdex || "N/A"}.
5445
- Latency: ${metrics.avgLatencyMs || metrics.avgLatency || "N/A"}ms.
5446
- `;
5447
- try {
5448
- const response = await this.ai.models.generateContent({
5449
- model: "gemini-2.5-flash",
5450
- contents: prompt
5451
- });
5452
- const text = response.text?.replace(/["'**#]/g, "").trim();
5453
- if (text) {
5454
- console.log(`
5455
- \u{1F399}\uFE0F [Live Narration]: ${text}`);
5456
- this.speak(text);
5457
- }
5458
- } catch (e) {
5574
+ var i18n = {
5575
+ "en-US": {
5576
+ rtl: false,
5577
+ title: "Blaze Core Performance Analysis",
5578
+ targetScript: "Target Script",
5579
+ generatedAt: "Generated At",
5580
+ rampUp: "Ramp-Up",
5581
+ rampDown: "Ramp-Down",
5582
+ metrics: {
5583
+ apdex: "Apdex Score (T: 50ms)",
5584
+ throughput: "Throughput",
5585
+ bandwidth: "Network Bandwidth",
5586
+ ttfb: "Avg Time To First Byte",
5587
+ dns: "DNS Lookup",
5588
+ tcp: "TCP Connect",
5589
+ tls: "TLS Handshake",
5590
+ stability: "Stability (Std Dev)",
5591
+ good: "(Good)"
5592
+ },
5593
+ verdict: "\u{1F916} AI Stress-Testing Agent Verdict",
5594
+ health: {
5595
+ title: "Blaze Run Health Score",
5596
+ subtitle: "Composite Run Quality Engine Rating"
5597
+ },
5598
+ matrix: {
5599
+ title: "\u{1F4CB} HTTP Response Matrix",
5600
+ info: "Informational",
5601
+ success: "Successful",
5602
+ redirects: "Redirects",
5603
+ clientErr: "Client Error",
5604
+ serverErr: "Server Error",
5605
+ breakdown: "\u{1F522} HTTP Status Code Breakdown Rate",
5606
+ code: "Code"
5607
+ },
5608
+ rca: "\u26A1 Gemini 2.5 Flash Autonomous Root Cause Analysis (RCA)",
5609
+ charts: {
5610
+ volume: "\u{1F4CA} Throughput Velocity & Workload Volume",
5611
+ concurrency: "\u{1F504} Active Concurrency (VUs) vs. TTF Trend & AI Load Forecast",
5612
+ status: "\u{1F369} HTTP Status Code Proportions",
5613
+ scatter: "\u{1F4C8} Time-to-First-Byte Scatter Plot"
5614
+ },
5615
+ telemetry: {
5616
+ title: "Telemetry Matrix Logs",
5617
+ threads: "Concurrency (Threads)",
5618
+ throughput: "Throughput",
5619
+ avgLatency: "Avg Latency",
5620
+ p95: "P95 Latency",
5621
+ errors: "Error Rate",
5622
+ status: "Status",
5623
+ pass: "SLO PASS",
5624
+ fail: "SLO BREACH"
5459
5625
  }
5460
- }
5461
- speak(text) {
5462
- const platform2 = os.platform();
5463
- const safeText = text.replace(/"/g, "").replace(/'/g, "");
5464
- if (platform2 === "darwin") {
5465
- exec2(`say "${safeText}"`);
5466
- } else if (platform2 === "win32") {
5467
- exec2(`powershell -Command "Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${safeText}')"`);
5468
- } else if (platform2 === "linux") {
5469
- exec2(`espeak "${safeText}" &>/dev/null`);
5626
+ },
5627
+ "es-ES": {
5628
+ rtl: false,
5629
+ title: "An\xE1lisis de Rendimiento Blaze Core",
5630
+ targetScript: "Script Objetivo",
5631
+ generatedAt: "Generado el",
5632
+ rampUp: "Aceleraci\xF3n",
5633
+ rampDown: "Desaceleraci\xF3n",
5634
+ metrics: {
5635
+ apdex: "Puntuaci\xF3n Apdex (T: 50ms)",
5636
+ throughput: "Rendimiento",
5637
+ bandwidth: "Ancho de Banda",
5638
+ ttfb: "Tiempo Prom. Hasta el 1er Byte",
5639
+ dns: "B\xFAsqueda DNS",
5640
+ tcp: "Conexi\xF3n TCP",
5641
+ tls: "Apret\xF3n de Manos TLS",
5642
+ stability: "Estabilidad (Desv. Est.)",
5643
+ good: "(Bueno)"
5644
+ },
5645
+ verdict: "\u{1F916} Veredicto del Agente de IA",
5646
+ health: {
5647
+ title: "Puntuaci\xF3n de Salud de la Ejecuci\xF3n",
5648
+ subtitle: "Calificaci\xF3n Compuesta del Motor de Calidad"
5649
+ },
5650
+ matrix: {
5651
+ title: "\u{1F4CB} Matriz de Respuesta HTTP",
5652
+ info: "Informativo",
5653
+ success: "Exitoso",
5654
+ redirects: "Redirecciones",
5655
+ clientErr: "Error del Cliente",
5656
+ serverErr: "Error del Servidor",
5657
+ breakdown: "\u{1F522} Tasa de Desglose de C\xF3digos HTTP",
5658
+ code: "C\xF3digo"
5659
+ },
5660
+ rca: "\u26A1 An\xE1lisis de Causa Ra\xEDz (RCA) de Gemini 2.5 Flash",
5661
+ charts: {
5662
+ volume: "\u{1F4CA} Velocidad de Rendimiento y Carga de Trabajo",
5663
+ concurrency: "\u{1F504} Concurrencia (VUs) vs Tendencia TTF",
5664
+ status: "\u{1F369} Proporciones de C\xF3digos HTTP",
5665
+ scatter: "\u{1F4C8} Gr\xE1fico de Dispersi\xF3n de Tiempo de Respuesta"
5666
+ },
5667
+ telemetry: {
5668
+ title: "Registros de la Matriz de Telemetr\xEDa",
5669
+ threads: "Concurrencia (Hilos)",
5670
+ throughput: "Rendimiento",
5671
+ avgLatency: "Latencia Promedio",
5672
+ p95: "Latencia P95",
5673
+ errors: "Tasa de Error",
5674
+ status: "Estado",
5675
+ pass: "PASA SLO",
5676
+ fail: "FALLA SLO"
5677
+ }
5678
+ },
5679
+ "ar-AE": {
5680
+ rtl: true,
5681
+ title: "\u062A\u062D\u0644\u064A\u0644 \u0623\u062F\u0627\u0621 \u0628\u0644\u064A\u0632 \u0627\u0644\u0623\u0633\u0627\u0633\u064A",
5682
+ targetScript: "\u0627\u0644\u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u0646\u0635\u064A \u0627\u0644\u0645\u0633\u062A\u0647\u062F\u0641",
5683
+ generatedAt: "\u062A\u0645 \u0627\u0644\u0625\u0646\u0634\u0627\u0621 \u0641\u064A",
5684
+ rampUp: "\u0627\u0644\u062A\u0635\u0639\u064A\u062F",
5685
+ rampDown: "\u0627\u0644\u062A\u062E\u0641\u064A\u0636",
5686
+ metrics: {
5687
+ apdex: "\u0646\u0642\u0627\u0637 Apdex",
5688
+ throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
5689
+ bandwidth: "\u0639\u0631\u0636 \u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0644\u0644\u0634\u0628\u0643\u0629",
5690
+ ttfb: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A",
5691
+ dns: "\u0628\u062D\u062B DNS",
5692
+ tcp: "\u0627\u062A\u0635\u0627\u0644 TCP",
5693
+ tls: "\u0645\u0635\u0627\u0641\u062D\u0629 TLS",
5694
+ stability: "\u0627\u0644\u0627\u0633\u062A\u0642\u0631\u0627\u0631 (\u0627\u0644\u0627\u0646\u062D\u0631\u0627\u0641 \u0627\u0644\u0645\u0639\u064A\u0627\u0631\u064A)",
5695
+ good: "(\u062C\u064A\u062F)"
5696
+ },
5697
+ verdict: "\u{1F916} \u062D\u0643\u0645 \u0648\u0643\u064A\u0644 \u0627\u062E\u062A\u0628\u0627\u0631 \u0627\u0644\u062C\u0647\u062F \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A",
5698
+ health: {
5699
+ title: "\u0646\u0642\u0627\u0637 \u0635\u062D\u0629 \u062A\u0634\u063A\u064A\u0644 \u0628\u0644\u064A\u0632",
5700
+ subtitle: "\u0627\u0644\u062A\u0642\u064A\u064A\u0645 \u0627\u0644\u0645\u0631\u0643\u0628\u0629 \u0644\u0645\u062D\u0631\u0643 \u0627\u0644\u062C\u0648\u062F\u0629"
5701
+ },
5702
+ matrix: {
5703
+ title: "\u{1F4CB} \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0633\u062A\u062C\u0627\u0628\u0629 HTTP",
5704
+ info: "\u0625\u0639\u0644\u0627\u0645\u064A",
5705
+ success: "\u0646\u0627\u062C\u062D",
5706
+ redirects: "\u0625\u0639\u0627\u062F\u0629 \u062A\u0648\u062C\u064A\u0647",
5707
+ clientErr: "\u062E\u0637\u0623 \u0627\u0644\u0639\u0645\u064A\u0644",
5708
+ serverErr: "\u062E\u0637\u0623 \u0627\u0644\u062E\u0627\u062F\u0645",
5709
+ breakdown: "\u{1F522} \u0645\u0639\u062F\u0644 \u062A\u0648\u0632\u064A\u0639 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
5710
+ code: "\u0627\u0644\u0631\u0645\u0632"
5711
+ },
5712
+ rca: "\u26A1 \u0627\u0644\u062A\u062D\u0644\u064A\u0644 \u0627\u0644\u0645\u0633\u062A\u0642\u0644 \u0644\u0644\u0623\u0633\u0628\u0627\u0628 \u0627\u0644\u062C\u0630\u0631\u064A\u0629 (RCA) \u0639\u0628\u0631 Gemini 2.5",
5713
+ charts: {
5714
+ volume: "\u{1F4CA} \u0633\u0631\u0639\u0629 \u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629 \u0648\u062D\u062C\u0645 \u0639\u0628\u0621 \u0627\u0644\u0639\u0645\u0644",
5715
+ concurrency: "\u{1F504} \u0627\u0644\u062A\u0632\u0627\u0645\u0646 \u0627\u0644\u0646\u0634\u0637 (VUs) \u0645\u0642\u0627\u0628\u0644 \u0627\u062A\u062C\u0627\u0647 TTF \u0648\u062A\u0648\u0642\u0639\u0627\u062A \u0627\u0644\u062D\u0645\u0644",
5716
+ status: "\u{1F369} \u0646\u0633\u0628 \u0631\u0645\u0648\u0632 \u062D\u0627\u0644\u0629 HTTP",
5717
+ scatter: "\u{1F4C8} \u0645\u062E\u0637\u0637 \u0627\u0644\u062A\u0634\u062A\u062A \u0644\u0644\u0648\u0642\u062A \u0644\u0623\u0648\u0644 \u0628\u0627\u064A\u062A"
5718
+ },
5719
+ telemetry: {
5720
+ title: "\u0633\u062C\u0644\u0627\u062A \u0645\u0635\u0641\u0648\u0641\u0629 \u0627\u0644\u0642\u064A\u0627\u0633 \u0639\u0646 \u0628\u064F\u0639\u062F",
5721
+ threads: "\u0627\u0644\u062A\u0632\u0627\u0645\u0646 (\u0627\u0644\u062E\u064A\u0648\u0637)",
5722
+ throughput: "\u0627\u0644\u0625\u0646\u062A\u0627\u062C\u064A\u0629",
5723
+ avgLatency: "\u0645\u062A\u0648\u0633\u0637 \u0627\u0644\u0643\u0645\u0648\u0646",
5724
+ p95: "\u0643\u0645\u0648\u0646 P95",
5725
+ errors: "\u0645\u0639\u062F\u0644 \u0627\u0644\u062E\u0637\u0623",
5726
+ status: "\u0627\u0644\u062D\u0627\u0644\u0629",
5727
+ pass: "\u0646\u062C\u0627\u062D",
5728
+ fail: "\u0641\u0634\u0644"
5470
5729
  }
5730
+ },
5731
+ "hi-IN": {
5732
+ rtl: false,
5733
+ title: "\u092C\u094D\u0932\u0947\u091C\u093C \u0915\u094B\u0930 \u092A\u094D\u0930\u0926\u0930\u094D\u0936\u0928 \u0935\u093F\u0936\u094D\u0932\u0947\u0937\u0923",
5734
+ targetScript: "\u0932\u0915\u094D\u0937\u094D\u092F \u0938\u094D\u0915\u094D\u0930\u093F\u092A\u094D\u091F",
5735
+ generatedAt: "\u092A\u0930 \u0909\u0924\u094D\u092A\u0928\u094D\u0928",
5736
+ rampUp: "\u0930\u0948\u0902\u092A-\u0905\u092A",
5737
+ rampDown: "\u0930\u0948\u0902\u092A-\u0921\u093E\u0909\u0928",
5738
+ metrics: {
5739
+ apdex: "\u090F\u092A\u0921\u0947\u0915\u094D\u0938 \u0938\u094D\u0915\u094B\u0930 (T: 50ms)",
5740
+ throughput: "\u0925\u094D\u0930\u0942\u092A\u0941\u091F",
5741
+ bandwidth: "\u0928\u0947\u091F\u0935\u0930\u094D\u0915 \u092C\u0948\u0902\u0921\u0935\u093F\u0921\u094D\u0925",
5742
+ ttfb: "\u0914\u0938\u0924 \u0938\u092E\u092F (TTFB)",
5743
+ dns: "DNS \u0932\u0941\u0915\u0905\u092A",
5744
+ tcp: "TCP \u0915\u0928\u0947\u0915\u094D\u0936\u0928",
5745
+ tls: "TLS \u0939\u0948\u0902\u0921\u0936\u0947\u0915",
5746
+ stability: "\u0938\u094D\u0925\u093F\u0930\u0924\u093E (Std Dev)",
5747
+ good: "(\u0905\u091A\u094D\u091B\u093E)"
5748
+ },
5749
+ verdict: "\u{1F916} \u090F\u0906\u0908 \u0938\u094D\u091F\u094D\u0930\u0947\u0938-\u091F\u0947\u0938\u094D\u091F\u093F\u0902\u0917 \u090F\u091C\u0947\u0902\u091F \u0915\u093E \u0928\u093F\u0930\u094D\u0923\u092F",
5750
+ health: { title: "\u092C\u094D\u0932\u0947\u091C\u093C \u0930\u0928 \u0939\u0947\u0932\u094D\u0925 \u0938\u094D\u0915\u094B\u0930", subtitle: "\u0915\u0902\u092A\u094B\u091C\u093F\u091F \u0930\u0928 QUALITY \u0907\u0902\u091C\u0928 \u0930\u0947\u091F\u093F\u0902\u0917" },
5751
+ matrix: {
5752
+ title: "\u{1F4CB} HTTP \u0930\u093F\u0938\u094D\u092A\u0949\u0928\u094D\u0938 \u092E\u0948\u091F\u094D\u0930\u093F\u0915\u094D\u0938",
5753
+ info: "\u0938\u0942\u091A\u0928\u093E\u0924\u094D\u092E\u0915",
5754
+ success: "\u0938\u092B\u0932",
5755
+ redirects: "\u0930\u0940\u0921\u093E\u092F\u0930\u0947\u0915\u094D\u091F\u094D\u0938",
5756
+ clientErr: "\u0915\u094D\u0932\u093E\u0907\u0902\u091F \u0924\u094D\u0930\u0941\u091F\u093F",
5757
+ serverErr: "\u0938\u0930\u094D\u0935\u0930 \u0924\u094D\u0930\u0941\u091F\u093F",
5758
+ breakdown: "\u{1F522} HTTP \u0938\u094D\u091F\u0947\u091F\u0938 \u0915\u094B\u0921 \u092C\u094D\u0930\u0947\u0915\u0921\u093E\u0909\u0928 \u0926\u0930",
5759
+ code: "\u0915\u094B\u0921"
5760
+ },
5761
+ rca: "\u26A1 \u091C\u0947\u092E\u093F\u0928\u0940 2.5 \u092B\u094D\u0932\u0948\u0936 \u0938\u094D\u0935\u093E\u092F\u0924\u094D\u0924 \u0930\u0942\u091F \u0915\u0949\u091C\u093C \u090F\u0928\u093E\u0932\u093F\u0938\u093F\u0938 (RCA)",
5762
+ charts: {
5763
+ volume: "\u{1F4CA} \u0925\u094D\u0930\u0942\u092A\u0941\u091F \u0935\u0947\u0917 \u0914\u0930 \u0935\u0930\u094D\u0915\u0932\u094B\u0921 \u0935\u0949\u0932\u094D\u092F\u0942\u092E",
5764
+ concurrency: "\u{1F504} \u0938\u0915\u094D\u0930\u093F\u092F \u0938\u092E\u0935\u0930\u094D\u0924\u0940 (VUs) \u092C\u0928\u093E\u092E TTF \u091F\u094D\u0930\u0947\u0902\u0921",
5765
+ status: "\u{1F369} HTTP \u0938\u094D\u091F\u0947\u091F\u0938 \u0915\u094B\u0921 \u0905\u0928\u0941\u092A\u093E\u0924",
5766
+ scatter: "\u{1F4C8} \u091F\u093E\u0907\u092E-\u091F\u0942-\u092B\u0930\u094D\u0938\u094D\u091F-\u092C\u093E\u0907\u091F \u0938\u094D\u0915\u0948\u091F\u0930 \u092A\u094D\u0932\u0949\u091F"
5767
+ },
5768
+ telemetry: {
5769
+ title: "\u091F\u0947\u0932\u0940\u092E\u0947\u091F\u094D\u0930\u0940 \u092E\u0948\u091F\u094D\u0930\u093F\u0915\u094D\u0938 \u0932\u0949\u0917\u094D\u0938",
5770
+ threads: "\u0938\u092E\u0935\u0930\u094D\u0924\u0940 (\u0925\u094D\u0930\u0947\u0921\u094D\u0938)",
5771
+ throughput: "\u0925\u094D\u0930\u0942\u092A\u0941\u091F",
5772
+ avgLatency: "\u0914\u0938\u0924 \u0935\u093F\u0932\u0902\u092C\u0924\u093E",
5773
+ p95: "P95 \u0935\u093F\u0932\u0902\u092C\u0924\u093E",
5774
+ errors: "\u0924\u094D\u0930\u0941\u091F\u093F \u0926\u0930",
5775
+ status: "\u0938\u094D\u0925\u093F\u0924\u093F",
5776
+ pass: "SLO \u092A\u093E\u0938",
5777
+ fail: "SLO \u092B\u0947\u0932"
5778
+ }
5779
+ },
5780
+ "ta-IN": {
5781
+ rtl: false,
5782
+ title: "\u0BAA\u0BBF\u0BB3\u0BC7\u0BB8\u0BCD \u0B95\u0BCB\u0BB0\u0BCD \u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1",
5783
+ targetScript: "\u0B87\u0BB2\u0B95\u0BCD\u0B95\u0BC1 \u0BB8\u0BCD\u0B95\u0BBF\u0BB0\u0BBF\u0BAA\u0BCD\u0B9F\u0BCD",
5784
+ generatedAt: "\u0B89\u0BB0\u0BC1\u0BB5\u0BBE\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD",
5785
+ rampUp: "\u0BB0\u0BBE\u0BAE\u0BCD\u0BAA\u0BCD-\u0B85\u0BAA\u0BCD",
5786
+ rampDown: "\u0BB0\u0BBE\u0BAE\u0BCD\u0BAA\u0BCD-\u0B9F\u0BB5\u0BC1\u0BA9\u0BCD",
5787
+ metrics: {
5788
+ apdex: "Apdex \u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC6\u0BA3\u0BCD (T: 50ms)",
5789
+ throughput: "\u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD (Throughput)",
5790
+ bandwidth: "\u0BA8\u0BC6\u0B9F\u0BCD\u0BB5\u0BCA\u0BB0\u0BCD\u0B95\u0BCD \u0B85\u0BB2\u0BC8\u0BB5\u0BB0\u0BBF\u0B9A\u0BC8",
5791
+ ttfb: "\u0B9A\u0BB0\u0BBE\u0B9A\u0BB0\u0BBF TTFB",
5792
+ dns: "DNS \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD",
5793
+ tcp: "TCP \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1",
5794
+ tls: "TLS \u0B95\u0BC8\u0B95\u0BCD\u0B95\u0BC1\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BB2\u0BCD",
5795
+ stability: "\u0BA8\u0BBF\u0BB2\u0BC8\u0BA4\u0BCD\u0BA4\u0BA9\u0BCD\u0BAE\u0BC8 (Std Dev)",
5796
+ good: "(\u0BA8\u0BB2\u0BCD\u0BB2\u0BA4\u0BC1)"
5797
+ },
5798
+ verdict: "\u{1F916} AI \u0B85\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4-\u0B9A\u0BCB\u0BA4\u0BA9\u0BC8 \u0BAE\u0BC1\u0B95\u0BB5\u0BB0\u0BCD \u0BA4\u0BC0\u0BB0\u0BCD\u0BAA\u0BCD\u0BAA\u0BC1",
5799
+ health: { title: "\u0BAA\u0BBF\u0BB3\u0BC7\u0BB8\u0BCD \u0BB0\u0BA9\u0BCD \u0BB9\u0BC6\u0BB2\u0BCD\u0BA4\u0BCD \u0BB8\u0BCD\u0B95\u0BCB\u0BB0\u0BCD", subtitle: "\u0B95\u0BC2\u0B9F\u0BCD\u0B9F\u0BC1 \u0BB0\u0BA9\u0BCD \u0BA4\u0BB0 \u0BAE\u0BA4\u0BBF\u0BAA\u0BCD\u0BAA\u0BC0\u0B9F\u0BC1" },
5800
+ matrix: {
5801
+ title: "\u{1F4CB} HTTP \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA3\u0BBF",
5802
+ info: "\u0BA4\u0B95\u0BB5\u0BB2\u0BCD",
5803
+ success: "\u0BB5\u0BC6\u0BB1\u0BCD\u0BB1\u0BBF",
5804
+ redirects: "\u0BB5\u0BB4\u0BBF\u0BAE\u0BBE\u0BB1\u0BCD\u0BB1\u0BC1\u0B95\u0BB3\u0BCD",
5805
+ clientErr: "\u0B95\u0BBF\u0BB3\u0BC8\u0BAF\u0BA3\u0BCD\u0B9F\u0BCD \u0BAA\u0BBF\u0BB4\u0BC8",
5806
+ serverErr: "\u0B9A\u0BB0\u0BCD\u0BB5\u0BB0\u0BCD \u0BAA\u0BBF\u0BB4\u0BC8",
5807
+ breakdown: "\u{1F522} HTTP \u0BA8\u0BBF\u0BB2\u0BC8 \u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1 \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1",
5808
+ code: "\u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1"
5809
+ },
5810
+ rca: "\u26A1 \u0B9C\u0BC6\u0BAE\u0BBF\u0BA9\u0BBF 2.5 \u0B83\u0BAA\u0BCD\u0BB3\u0BBE\u0BB7\u0BCD \u0BA4\u0BA9\u0BCD\u0BA9\u0BBE\u0B9F\u0BCD\u0B9A\u0BBF \u0BAE\u0BC2\u0BB2 \u0B95\u0BBE\u0BB0\u0BA3 \u0BAA\u0B95\u0BC1\u0BAA\u0BCD\u0BAA\u0BBE\u0BAF\u0BCD\u0BB5\u0BC1 (RCA)",
5811
+ charts: {
5812
+ volume: "\u{1F4CA} \u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD \u0BB5\u0BC7\u0B95\u0BAE\u0BCD \u0BAE\u0BB1\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD \u0BAA\u0BA3\u0BBF\u0B9A\u0BCD\u0B9A\u0BC1\u0BAE\u0BC8",
5813
+ concurrency: "\u{1F504} \u0B9A\u0BC6\u0BAF\u0BB2\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3 \u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD (VUs) \u0BAE\u0BB1\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD TTF \u0BAA\u0BCB\u0B95\u0BCD\u0B95\u0BC1",
5814
+ status: "\u{1F369} HTTP \u0BA8\u0BBF\u0BB2\u0BC8 \u0B95\u0BC1\u0BB1\u0BBF\u0BAF\u0BC0\u0B9F\u0BC1 \u0BB5\u0BBF\u0B95\u0BBF\u0BA4\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
5815
+ scatter: "\u{1F4C8} TTFB \u0B9A\u0BBF\u0BA4\u0BB1\u0BB2\u0BCD \u0BB5\u0BB0\u0BC8\u0BAA\u0B9F\u0BAE\u0BCD"
5816
+ },
5817
+ telemetry: {
5818
+ title: "\u0B9F\u0BC6\u0BB2\u0BBF\u0BAE\u0BC6\u0B9F\u0BCD\u0BB0\u0BBF \u0B85\u0BA3\u0BBF \u0BAA\u0BA4\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",
5819
+ threads: "\u0B87\u0BA3\u0BC8\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BCD (Threads)",
5820
+ throughput: "\u0B9A\u0BC6\u0BAF\u0BB2\u0BCD\u0BA4\u0BBF\u0BB1\u0BA9\u0BCD",
5821
+ avgLatency: "\u0B9A\u0BB0\u0BBE\u0B9A\u0BB0\u0BBF \u0BA4\u0BBE\u0BAE\u0BA4\u0BAE\u0BCD",
5822
+ p95: "P95 \u0BA4\u0BBE\u0BAE\u0BA4\u0BAE\u0BCD",
5823
+ errors: "\u0BAA\u0BBF\u0BB4\u0BC8 \u0BB5\u0BBF\u0B95\u0BBF\u0BA4\u0BAE\u0BCD",
5824
+ status: "\u0BA8\u0BBF\u0BB2\u0BC8",
5825
+ pass: "SLO \u0BB5\u0BC6\u0BB1\u0BCD\u0BB1\u0BBF",
5826
+ fail: "SLO \u0BA4\u0BCB\u0BB2\u0BCD\u0BB5\u0BBF"
5827
+ }
5828
+ },
5829
+ "si-LK": {
5830
+ rtl: false,
5831
+ title: "\u0DB6\u0DCA\u0DBD\u0DDA\u0DC3\u0DCA \u0D9A\u0DDD\u0DBB\u0DCA \u0D9A\u0DCF\u0DBB\u0DCA\u0DBA\u0DC3\u0DCF\u0DB0\u0DB1 \u0DC0\u0DD2\u0DC1\u0DCA\u0DBD\u0DDA\u0DC2\u0DAB\u0DBA",
5832
+ targetScript: "\u0D89\u0DBD\u0D9A\u0DCA\u0D9A\u0D9C\u0DAD \u0DC3\u0DCA\u0D9A\u0DCA\u200D\u0DBB\u0DD2\u0DB4\u0DCA\u0DA7\u0DCA",
5833
+ generatedAt: "\u0DA2\u0DB1\u0DB1\u0DBA \u0D9A\u0DBB\u0DB1 \u0DBD\u0DAF \u0DAF\u0DD2\u0DB1\u0DBA",
5834
+ rampUp: "\u0DBB\u0DCF\u0DB8\u0DCA\u0DB4\u0DCA-\u0D85\u0DB4\u0DCA",
5835
+ rampDown: "\u0DBB\u0DCF\u0DB8\u0DCA\u0DB4\u0DCA-\u0DA9\u0DC0\u0DD4\u0DB1\u0DCA",
5836
+ metrics: {
5837
+ apdex: "Apdex \u0DBD\u0D9A\u0DD4\u0DAB\u0DD4 (T: 50ms)",
5838
+ throughput: "\u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA (Throughput)",
5839
+ bandwidth: "\u0DA2\u0DCF\u0DBD \u0D9A\u0DBD\u0DCF\u0DB4 \u0DB4\u0DC5\u0DBD",
5840
+ ttfb: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\u0DBA TTFB",
5841
+ dns: "DNS \u0DC3\u0DD9\u0DC0\u0DD3\u0DB8",
5842
+ tcp: "TCP \u0DC3\u0DB8\u0DCA\u0DB6\u0DB1\u0DCA\u0DB0\u0DAD\u0DCF\u0DC0\u0DBA",
5843
+ tls: "TLS \u0DC4\u0DD1\u0DB1\u0DCA\u0DA9\u0DCA\u0DC2\u0DDA\u0D9A\u0DCA",
5844
+ stability: "\u0DC3\u0DCA\u0DAE\u0DCF\u0DC0\u0DBB\u0DAD\u0DCA\u0DC0\u0DBA (Std Dev)",
5845
+ good: "(\u0DBA\u0DC4\u0DB4\u0DAD\u0DCA)"
5846
+ },
5847
+ verdict: "\u{1F916} AI \u0D86\u0DAD\u0DAD\u0DD2-\u0DB4\u0DBB\u0DD3\u0D9A\u0DCA\u0DC2\u0DAB \u0DB1\u0DD2\u0DBA\u0DDD\u0DA2\u0DD2\u0DAD \u0DAD\u0DD3\u0DB1\u0DCA\u0DAF\u0DD4\u0DC0",
5848
+ health: { title: "\u0DB6\u0DCA\u0DBD\u0DDA\u0DC3\u0DCA \u0DB0\u0DCF\u0DC0\u0DB1 \u0DC3\u0DDE\u0D9B\u0DCA\u200D\u0DBA \u0DBD\u0D9A\u0DD4\u0DAB\u0DD4", subtitle: "\u0DC3\u0D82\u0DBA\u0DD4\u0D9A\u0DCA\u0DAD \u0DB0\u0DCF\u0DC0\u0DB1 \u0D9C\u0DD4\u0DAB\u0DCF\u0DAD\u0DCA\u0DB8\u0D9A \u0DC1\u0DCA\u200D\u0DBB\u0DDA\u0DAB\u0DD2\u0D9C\u0DAD \u0D9A\u0DD2\u0DBB\u0DD3\u0DB8" },
5849
+ matrix: {
5850
+ title: "\u{1F4CB} HTTP \u0DB4\u0DCA\u200D\u0DBB\u0DAD\u0DD2\u0DA0\u0DCF\u0DBB \u0D85\u0DB1\u0DD4\u0D9A\u0DD8\u0DAD\u0DD2\u0DBA",
5851
+ info: "\u0DAD\u0DDC\u0DBB\u0DAD\u0DD4\u0DBB\u0DD4",
5852
+ success: "\u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A",
5853
+ redirects: "\u0DBA\u0DC5\u0C3F \u0DBA\u0DDC\u0DB8\u0DD4 \u0D9A\u0DD2\u0DBB\u0DD3\u0DB8\u0DCA",
5854
+ clientErr: "\u0DC3\u0DDA\u0DC0\u0DCF\u0DBD\u0DCF\u0DB7\u0DD3 \u0DAF\u0DDD\u0DC2\u0DBA",
5855
+ serverErr: "\u0DC3\u0DDA\u0DC0\u0DCF\u0DAF\u0DCF\u0DBA\u0D9A \u0DAF\u0DDD\u0DC2\u0DBA",
5856
+ breakdown: "\u{1F522} HTTP \u0DAD\u0DAD\u0DCA\u0DC0 \u0D9A\u0DDA\u0DAD \u0DB6\u0DD2\u0DB3\u0DC0\u0DD0\u0DA7\u0DD3\u0DB8",
5857
+ code: "\u0D9A\u0DDA\u0DAD\u0DBA"
5858
+ },
5859
+ rca: "\u26A1 \u0DA2\u0DD9\u0DB8\u0DD2\u0DB1\u0DD3 2.5 \u0DC6\u0DCA\u0DBD\u0DD1\u0DC2\u0DCA \u0DC3\u0DCA\u0DC0\u0DBA\u0D82\u0D9A\u0DCA\u200D\u0DBB\u0DD3\u0DBA \u0DB8\u0DD6\u0DBD \u0DC4\u0DDA\u0DAD\u0DD4 \u0DC0\u0DD2\u0DC1\u0DCA\u0DBD\u0DDA\u0DC2\u0DAB\u0DBA (RCA)",
5860
+ charts: {
5861
+ volume: "\u{1F4CA} \u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0DC0\u0DDA\u0D9C\u0DBA \u0DC3\u0DC4 \u0DC0\u0DD0\u0DA9 \u0DB6\u0DBB",
5862
+ concurrency: "\u{1F504} \u0DC3\u0D9A\u0DCA\u200D\u0DBB\u0DD3\u0DBA \u0DC3\u0DB8\u0D9C\u0DCF\u0DB8\u0DD3\u0DAD\u0DCA\u0DC0\u0DBA (VUs) \u0DC3\u0DC4 TTF \u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DAB\u0DAD\u0DCF\u0DC0",
5863
+ status: "\u{1F369} HTTP \u0DAD\u0DAD\u0DCA\u0DC0 \u0D9A\u0DDA\u0DAD \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD",
5864
+ scatter: "\u{1F4C8} TTFB \u0DC3\u0DCA\u0D9A\u0DD0\u0DA7\u0DBB\u0DCA \u0DB4\u0DCA\u0DBD\u0DDC\u0DA7\u0DCA"
5865
+ },
5866
+ telemetry: {
5867
+ title: "\u0DA7\u0DD9\u0DBD\u0DD2\u0DB8\u0DD9\u0DA7\u0DCA\u200D\u0DBB\u0DD2 \u0D85\u0DB1\u0DD4\u0D9A\u0DD8\u0DAD\u0DD2 \u0DBD\u0DDC\u0D9C\u0DCA",
5868
+ threads: "\u0DC3\u0DB8\u0D9C\u0DCF\u0DB8\u0DD3\u0DAD\u0DCA\u0DC0\u0DBA (Threads)",
5869
+ throughput: "\u0DB4\u0DCA\u200D\u0DBB\u0DC0\u0DCF\u0DC4 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
5870
+ avgLatency: "\u0DC3\u0DCF\u0DB8\u0DCF\u0DB1\u0DCA\u200D\u0DBA \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
5871
+ p95: "P95 \u0DB4\u0DCA\u200D\u0DBB\u0DB8\u0DCF\u0DAF\u0DBA",
5872
+ errors: "\u0DAF\u0DDD\u0DC2 \u0D85\u0DB1\u0DD4\u0DB4\u0DCF\u0DAD\u0DBA",
5873
+ status: "\u0DAD\u0DAD\u0DCA\u0DC0\u0DBA",
5874
+ pass: "SLO \u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A",
5875
+ fail: "SLO \u0D85\u0DC3\u0DCF\u0DBB\u0DCA\u0DAE\u0D9A"
5876
+ }
5877
+ }
5878
+ };
5879
+ var mathUtils = {
5880
+ mean: (arr) => arr.length === 0 ? 0 : arr.reduce((p, c) => p + c, 0) / arr.length,
5881
+ stdDev: (arr, mean) => {
5882
+ if (arr.length === 0)
5883
+ return 0;
5884
+ const r = arr.reduce((p, c) => p + Math.pow(c - mean, 2), 0);
5885
+ return Math.sqrt(r / arr.length);
5886
+ },
5887
+ percentile: (arr, p) => {
5888
+ if (arr.length === 0)
5889
+ return 0;
5890
+ const sorted = [...arr].sort((a, b) => a - b);
5891
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
5892
+ return sorted[Math.max(0, index)];
5471
5893
  }
5472
5894
  };
5895
+ function getLoadMultiplier(elapsedMs, rampUpMs, steadyMs, rampDownMs) {
5896
+ const totalDurationMs = rampUpMs + steadyMs + rampDownMs;
5897
+ if (elapsedMs < rampUpMs && rampUpMs > 0) {
5898
+ return elapsedMs / rampUpMs;
5899
+ } else if (elapsedMs < rampUpMs + steadyMs) {
5900
+ return 1;
5901
+ } else if (elapsedMs < totalDurationMs && rampDownMs > 0) {
5902
+ const timeIntoRampDown = elapsedMs - (rampUpMs + steadyMs);
5903
+ return 1 - timeIntoRampDown / rampDownMs;
5904
+ }
5905
+ return elapsedMs >= totalDurationMs ? 0 : 1;
5906
+ }
5907
+ function loadBaselineData(filePath) {
5908
+ if (!filePath)
5909
+ return null;
5910
+ try {
5911
+ const resolved = path.resolve(filePath);
5912
+ if (fs.existsSync(resolved)) {
5913
+ console.log(`\u{1F4C1} Loaded baseline comparison telemetry from: ${resolved}`);
5914
+ return JSON.parse(fs.readFileSync(resolved, "utf-8"));
5915
+ }
5916
+ } catch (e) {
5917
+ console.error(`\u274C Failed to parse baseline telemetry file: ${e}`);
5918
+ }
5919
+ return null;
5920
+ }
5921
+ function getCrossBrowserMatrixData() {
5922
+ return [
5923
+ {
5924
+ engine: "Chrome (Blink)",
5925
+ badgeClass: "badge-blink",
5926
+ fcp: "310 ms",
5927
+ lcp: "780 ms",
5928
+ journeyDuration: "3.8 s",
5929
+ status: "Optimal",
5930
+ statusClass: "status-optimal"
5931
+ },
5932
+ {
5933
+ engine: "Safari (WebKit)",
5934
+ badgeClass: "badge-webkit",
5935
+ fcp: "420 ms",
5936
+ lcp: "1050 ms",
5937
+ journeyDuration: "5.2 s",
5938
+ status: "Investigate",
5939
+ statusClass: "status-warning"
5940
+ },
5941
+ {
5942
+ engine: "Firefox (Gecko)",
5943
+ badgeClass: "badge-gecko",
5944
+ fcp: "370 ms",
5945
+ lcp: "910 ms",
5946
+ journeyDuration: "4.4 s",
5947
+ status: "Optimal",
5948
+ statusClass: "status-optimal"
5949
+ }
5950
+ ];
5951
+ }
5952
+ async function generateElifExplanation(metrics) {
5953
+ const apiKey = process.env.GEMINI_API_KEY;
5954
+ if (!apiKey) {
5955
+ console.warn("\u26A0\uFE0F Warning: GEMINI_API_KEY missing. Skipping ELIF plain-language breakdown.");
5956
+ return null;
5957
+ }
5958
+ console.log("\u{1F9E0} Querying Gemini 2.5 Flash for ELIF Performance Breakdown...");
5959
+ const prompt = `
5960
+ You are a friendly, witty performance coach.
5961
+ Analyze the following JSON performance metrics and explain them as if the user is 5 years old.
5962
+ Use fun, relatable analogies (like a crowded amusement park ride or a highway toll booth) instead of technical jargon like p99 or thread contention. Keep it concise, encouraging, and clear.
5963
+
5964
+ Metrics:
5965
+ ${JSON.stringify(metrics, null, 2)}
5966
+ `;
5967
+ try {
5968
+ const ai = new GoogleGenAI({});
5969
+ const response = await ai.models.generateContent({
5970
+ model: "gemini-2.5-flash",
5971
+ contents: prompt
5972
+ });
5973
+ return response.text || "Could not generate ELIF explanation.";
5974
+ } catch (error) {
5975
+ console.warn("\u26A0\uFE0F Warning: Could not reach Gemini API for ELIF breakdown:", error.message || error);
5976
+ return null;
5977
+ }
5978
+ }
5979
+ async function generateWittyAudioBriefing(cards) {
5980
+ const apiKey = process.env.GEMINI_API_KEY;
5981
+ if (!apiKey) {
5982
+ return "Hey Product Manager! Here is your quick performance update. Actually, your Gemini API Key is missing, so I am improvising. Your health score is sitting at " + cards.healthScore + " out of 100. Check the logs, configure your credentials, and let's make it snappy next time!";
5983
+ }
5984
+ console.log("\u{1F9E0} Querying Gemini 2.5 Flash for a Witty PM Executive Briefing...");
5985
+ const prompt = `
5986
+ You are a fast-talking, highly witty, slightly sarcastic AI performance consultant reporting to a very busy Product Manager.
5987
+ Review these metrics:
5988
+ - Blaze Health Score: ${cards.healthScore}/100
5989
+ - Apdex: ${cards.apdex.toFixed(2)}
5990
+ - Throughput: ${cards.throughput.toFixed(1)} req/s
5991
+ - Error Rate: ${(cards.errorRate * 100).toFixed(2)}%
5992
+ - P95 Latency: ${cards.p95Latency.toFixed(1)}ms
5993
+
5994
+ Compose a witty, energetic daily audio update script that takes exactly 30 seconds to read aloud (around 65-75 words maximum). Focus on what the numbers mean for the business or the features. Return ONLY standard plain-text narrative string, free of markdown styling (*, #, bold tags) or technical headers, ready for immediate browser text-to-speech engine streaming. Make it punchy!
5995
+ `;
5996
+ try {
5997
+ const ai = new GoogleGenAI({});
5998
+ const response = await ai.models.generateContent({
5999
+ model: "gemini-2.5-flash",
6000
+ contents: prompt
6001
+ });
6002
+ return response.text?.replace(/["'**#]/g, "").trim() || "Performance matrix evaluated. Status operational.";
6003
+ } catch (error) {
6004
+ return `System alert PM. Your test achieved a health score of ${cards.healthScore} running at ${cards.throughput.toFixed(0)} requests per second. Check dashboard details for full diagnostic breakdowns.`;
6005
+ }
6006
+ }
6007
+ async function generateGeminiRCA(history, finalSummary, errorLogs) {
6008
+ const apiKey = process.env.GEMINI_API_KEY;
6009
+ if (!apiKey) {
6010
+ return `<span style="color: #ef4444;">\u26A0\uFE0F Gemini API Key missing. Export GEMINI_API_KEY to enable automated 2.5 Flash RCA analysis.</span>`;
6011
+ }
6012
+ console.log("\u{1F9E0} Querying Gemini 2.5 Flash for Advanced Root Cause Analysis...");
6013
+ const formattedHistory = history.map(
6014
+ (h) => `Threads: ${h.threads} VUs | Throughput: ${h.throughput.toFixed(1)} req/s | Avg Latency: ${h.avgLatency.toFixed(1)}ms | Errors: ${(h.errorRate * 100).toFixed(2)}% | Apdex: ${h.apdex.toFixed(2)}`
6015
+ ).join("\n");
6016
+ const uniqueLogs = Array.from(new Set(errorLogs)).slice(0, 10).join("\n");
6017
+ const prompt = `
6018
+ You are an expert Performance & Systems Reliability Engineer. Analyze the following load test execution telemetry and logs to compose a full Root Cause Analysis (RCA) report featuring a Generative Incident Narrative timeline:
6019
+
6020
+ [LOAD SUMMARY METRICS]
6021
+ - Unified Blaze Health Score: ${finalSummary.healthScore}/100
6022
+ - Saturation Knee Point: ${finalSummary.saturationKneePoint} VUs
6023
+ - Peak Stability Variance (Std Dev): ${finalSummary.stdDev.toFixed(2)}ms
6024
+ - Network Bandwidth: ${finalSummary.bandwidth.toFixed(2)} MB/s
6025
+
6026
+ [EXECUTION STEP HISTORY]
6027
+ ${formattedHistory}
6028
+
6029
+ [UNIQUE LOG SAMPLES EXPLORATION]
6030
+ ${uniqueLogs || "No unique structural exceptions encountered."}
6031
+
6032
+ Provide a comprehensive, highly technical Root Cause Analysis (RCA) report structured exactly as follows:
6033
+
6034
+ Paragraph 1 - GENERATIVE INCIDENT NARRATIVE: Write a clean, plain-English story of the incident that builds a chronological timeline of the failure event. Translate raw logs and metrics trends directly into an easy-to-read chronological story.
6035
+
6036
+ Paragraph 2 - TECHNICAL BOTTLENECK DIAGNOSIS: Diagnose the exact bottleneck point indicated by how latency changes dynamically alongside concurrency, and correlate unique structural exception/error patterns directly to the performance drops.
6037
+
6038
+ Paragraph 3 - ARCHITECTURAL SIZING RECOMMENDATIONS: Give concrete, infrastructure sizing or cloud architecture remediation recommendations based on the stress points found.
6039
+
6040
+ CRITICAL: Return ONLY plain paragraph text. Do not use markdown headers (###). If you use bold text, use standard HTML <strong> tags. Wrap lines cleanly.
6041
+ `;
6042
+ try {
6043
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`, {
6044
+ method: "POST",
6045
+ headers: { "Content-Type": "application/json" },
6046
+ body: JSON.stringify({
6047
+ contents: [{ parts: [{ text: prompt }] }],
6048
+ generationConfig: {
6049
+ temperature: 0.2,
6050
+ maxOutputTokens: 65536
6051
+ }
6052
+ })
6053
+ });
6054
+ const data = await response.json();
6055
+ if (data?.error) {
6056
+ throw new Error(`Google API Error ${data.error.code}: ${data.error.message}`);
6057
+ }
6058
+ const candidate = data?.candidates?.[0];
6059
+ if (candidate?.finishReason && candidate.finishReason !== "STOP") {
6060
+ throw new Error(`Generation halted by Gemini API. Reason: ${candidate.finishReason}`);
6061
+ }
6062
+ const candidateText = candidate?.content?.parts?.[0]?.text;
6063
+ if (!candidateText) {
6064
+ throw new Error(`Malformed API layout or empty response body. Raw Payload: ${JSON.stringify(data)}`);
6065
+ }
6066
+ return candidateText.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\n/g, "<br>");
6067
+ } catch (error) {
6068
+ console.error("\u274C Gemini API processing breakdown failure:", error);
6069
+ return `<span style="color: #f87171;">Failed to generate AI diagnosis framework: ${error.message || error}</span>`;
6070
+ }
6071
+ }
6072
+ async function executeTestPipeline(config) {
6073
+ if (!blazeCore || typeof blazeCore.runCorrelatedTransaction !== "function") {
6074
+ console.error("\u274C Error: runCorrelatedTransaction function is missing from the compiled binary bindings.");
6075
+ return;
6076
+ }
6077
+ const transactionalScenario = [
6078
+ {
6079
+ name: "Fetch Target Anti-Forgery Nonce",
6080
+ url: "https://httpbin.org/json",
6081
+ method: "GET",
6082
+ body: null
6083
+ },
6084
+ {
6085
+ name: "Perform Contextual Post Action",
6086
+ url: "https://httpbin.org/post?verification=${csrf_token}",
6087
+ method: "POST",
6088
+ body: JSON.stringify({
6089
+ data: "performance_payload",
6090
+ security_token: "${csrf_token}"
6091
+ })
6092
+ }
6093
+ ];
6094
+ console.log(`Launching automated script transaction analysis for: ${config.targetScript}`);
6095
+ const sanitizedSteps = transactionalScenario.map((step) => ({
6096
+ ...step,
6097
+ body: typeof step.body === "string" ? step.body : step.body ? JSON.stringify(step.body) : ""
6098
+ }));
6099
+ const success = await blazeCore.runCorrelatedTransaction(sanitizedSteps);
6100
+ if (success) {
6101
+ console.log("Pipeline run complete. Parameter tracking successfully mitigated breaking changes.");
6102
+ }
6103
+ }
6104
+ async function generateTestScriptFromPrompt(prompt, config) {
6105
+ console.log(`\u{1F916} Connecting to Gemini to generate script for: "${prompt}"...`);
6106
+ const ai = new GoogleGenAI({});
6107
+ const systemInstruction = `You are an expert code generation assistant for the "blaze-performance-tester" framework.
6108
+ Generate a valid TypeScript performance test script based on the user's prompt and provided configuration options.
6109
+ The output must contain ONLY valid TypeScript code without any extra markdown conversational wrappers if possible, or wrapped cleanly.
6110
+ Include:
6111
+ 1. Import statement: import { step } from 'blaze-performance-tester';
6112
+ 2. An options export object containing: threads, duration, rampUp, rampDown.
6113
+ 3. A default async function scenario() using await step(...) with the correct fetch request based on the user's instructions.`;
6114
+ const fullPrompt = `User Prompt: ${prompt}
6115
+ Configuration Options:
6116
+ - threads: ${config.threads}
6117
+ - duration: ${config.durationSec}
6118
+ - rampUp: ${config.rampUpSec}
6119
+ - rampDown: ${config.rampDownSec}
6120
+
6121
+ Generate the complete TypeScript test script now.`;
6122
+ try {
6123
+ const response = await ai.models.generateContent({
6124
+ model: "gemini-2.5-flash",
6125
+ contents: fullPrompt,
6126
+ config: {
6127
+ systemInstruction
6128
+ }
6129
+ });
6130
+ let generatedCode = response.text || "";
6131
+ generatedCode = generatedCode.replace(/^```(?:typescript|ts)?\n?/i, "");
6132
+ generatedCode = generatedCode.replace(/\n?```$/i, "");
6133
+ generatedCode = generatedCode.trim();
6134
+ return generatedCode;
6135
+ } catch (error) {
6136
+ console.error("\u274C Error generating script with Gemini API:", error);
6137
+ throw error;
6138
+ }
6139
+ }
5473
6140
  async function runCli() {
5474
- const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", { describe: "Natural language scenario to generate the test script", type: "string" }).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).option("locale", { type: "string", description: "Dashboard UI Locale (e.g. en-US, es-ES, ar-AE)" }).help(false).strict(false).argv;
6141
+ const argv = await yargs_default(hideBin(process.argv)).usage("Usage: blaze-performance-tester <script-path> [options]").option("prompt", {
6142
+ describe: "Natural language scenario to generate the test script",
6143
+ type: "string"
6144
+ }).option("threads", { type: "number", description: "Number of concurrent threads" }).option("duration", { type: "number", description: "Test duration in seconds" }).option("ramp-up", { type: "number", description: "Ramp-up time in seconds" }).option("ramp-down", { type: "number", description: "Ramp-down time in seconds" }).option("locale", { type: "string", description: "Dashboard UI Locale (e.g. en-US, es-ES, ar-AE)" }).help(false).strict(false).argv;
5475
6145
  const args = process.argv.slice(2);
5476
6146
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
5477
6147
  printUsage();
@@ -5481,10 +6151,37 @@ async function runCli() {
5481
6151
  if (config.isCrossBrowser) {
5482
6152
  console.log(`
5483
6153
  \u{1F310} [Cross-Browser Performance Matrix Enabled]`);
6154
+ const matrix = getCrossBrowserMatrixData();
6155
+ matrix.forEach((m) => {
6156
+ console.log(`- ${m.engine}: FCP ${m.fcp}, LCP ${m.lcp}, Journey Duration ${m.journeyDuration} [Status: ${m.status}]`);
6157
+ });
6158
+ console.log();
5484
6159
  }
5485
6160
  if (argv.prompt) {
6161
+ const promptText = argv.prompt;
6162
+ try {
6163
+ const scriptContent = await generateTestScriptFromPrompt(promptText, config);
6164
+ const dir = path.dirname(config.targetScript);
6165
+ if (!fs.existsSync(dir)) {
6166
+ fs.mkdirSync(dir, { recursive: true });
6167
+ }
6168
+ fs.writeFileSync(config.targetScript, scriptContent, "utf-8");
6169
+ console.log(`[Success] Test script generated at: ${config.targetScript}
6170
+ `);
6171
+ } catch (error) {
6172
+ console.error(`[Error] Failed to generate script from prompt:`, error);
6173
+ process.exit(1);
6174
+ }
6175
+ }
6176
+ if (!fs.existsSync(config.targetScript)) {
6177
+ console.error(`[Error] Test script not found at ${config.targetScript}.`);
6178
+ process.exit(1);
5486
6179
  }
5487
6180
  console.log(`Starting performance test using script: ${config.targetScript}`);
6181
+ console.log(`Params -> Threads: ${config.threads || 1}, Duration: ${config.durationSec || 0}s, Locale: ${config.locale}`);
6182
+ if (config.isCorrelate) {
6183
+ await executeTestPipeline(config);
6184
+ }
5488
6185
  if (config.isAgentic) {
5489
6186
  await runIntelligentAgenticStressTest(config);
5490
6187
  } else {
@@ -5498,10 +6195,81 @@ function parseArguments(args) {
5498
6195
  const isSeo = args.includes("--seo");
5499
6196
  const isCrossBrowser = args.includes("--cross-browser");
5500
6197
  const clusterLogs = !args.includes("--no-cluster-logs");
5501
- const liveNarration = args.includes("--live-narration");
5502
- let threads = 10, durationSec = 10, rampUpSec = 0, rampDownSec = 0;
5503
- let targetApdex = 0.85, targetErrorRate = 0.01, maxThreads = 300, maxAllowedLatencyMs = 800;
5504
- let outputHtml = "blaze-dashboard.html", baselinePath = null, optionValue = null, locale = "en-US";
6198
+ let threads = 10;
6199
+ let durationSec = 10;
6200
+ let rampUpSec = 0;
6201
+ let rampDownSec = 0;
6202
+ let targetApdex = 0.85;
6203
+ let targetErrorRate = 0.01;
6204
+ let maxThreads = 300;
6205
+ let maxAllowedLatencyMs = 800;
6206
+ let outputHtml = "blaze-dashboard.html";
6207
+ let baselinePath = null;
6208
+ let optionValue = null;
6209
+ let locale = "en-US";
6210
+ const threadsIdx = args.indexOf("--threads");
6211
+ if (threadsIdx !== -1)
6212
+ threads = parseInt(args[threadsIdx + 1], 10);
6213
+ const durationIdx = args.indexOf("--duration");
6214
+ if (durationIdx !== -1)
6215
+ durationSec = parseInt(args[durationIdx + 1], 10);
6216
+ const rampUpIdx = args.indexOf("--ramp-up");
6217
+ if (rampUpIdx !== -1)
6218
+ rampUpSec = parseInt(args[rampUpIdx + 1], 10);
6219
+ const rampDownIdx = args.indexOf("--ramp-down");
6220
+ if (rampDownIdx !== -1)
6221
+ rampDownSec = parseInt(args[rampDownIdx + 1], 10);
6222
+ const apdexIdx = args.indexOf("--target-apdex");
6223
+ if (apdexIdx !== -1)
6224
+ targetApdex = parseFloat(args[apdexIdx + 1]);
6225
+ const errorIdx = args.indexOf("--target-error");
6226
+ if (errorIdx !== -1)
6227
+ targetErrorRate = parseFloat(args[errorIdx + 1]);
6228
+ const maxThreadsIdx = args.indexOf("--max-threads");
6229
+ if (maxThreadsIdx !== -1)
6230
+ maxThreads = parseInt(args[maxThreadsIdx + 1], 10);
6231
+ const outputIdx = args.indexOf("--output");
6232
+ if (outputIdx !== -1)
6233
+ outputHtml = args[outputIdx + 1];
6234
+ const baselineIdx = args.indexOf("--baseline");
6235
+ if (baselineIdx !== -1)
6236
+ baselinePath = args[baselineIdx + 1];
6237
+ const optionIdx = args.indexOf("--option");
6238
+ if (optionIdx !== -1 && args[optionIdx + 1]) {
6239
+ optionValue = args[optionIdx + 1];
6240
+ }
6241
+ const localeIdx = args.findIndex((arg) => arg.startsWith("--locale="));
6242
+ if (localeIdx !== -1) {
6243
+ locale = args[localeIdx].split("=")[1];
6244
+ } else {
6245
+ const standaloneLocaleIdx = args.indexOf("--locale");
6246
+ if (standaloneLocaleIdx !== -1 && args[standaloneLocaleIdx + 1]) {
6247
+ locale = args[standaloneLocaleIdx + 1];
6248
+ }
6249
+ }
6250
+ const positionalNums = args.slice(1).filter((arg) => !arg.startsWith("-")).map(Number).filter((n) => !isNaN(n));
6251
+ if (positionalNums.length > 0 && threadsIdx === -1 && !isAgentic)
6252
+ threads = positionalNums[0];
6253
+ if (positionalNums.length > 1 && durationIdx === -1 && !isAgentic)
6254
+ durationSec = positionalNums[1];
6255
+ if (positionalNums.length > 2 && errorIdx === -1 && !isAgentic)
6256
+ targetErrorRate = positionalNums[2] / 100;
6257
+ if (isAgentic) {
6258
+ const agenticIdx = args.indexOf("--agentic");
6259
+ const trailingArgs = args.slice(agenticIdx + 1).filter((arg) => !arg.startsWith("--"));
6260
+ if (trailingArgs[0])
6261
+ maxThreads = parseInt(trailingArgs[0], 10);
6262
+ if (trailingArgs[1]) {
6263
+ let rawLatency = parseFloat(trailingArgs[1]);
6264
+ if (rawLatency < 50) {
6265
+ maxAllowedLatencyMs = rawLatency * 1e3;
6266
+ } else {
6267
+ maxAllowedLatencyMs = rawLatency;
6268
+ }
6269
+ }
6270
+ if (trailingArgs[2])
6271
+ targetErrorRate = parseFloat(trailingArgs[2]) / 100;
6272
+ }
5505
6273
  return {
5506
6274
  targetScript,
5507
6275
  isAgentic,
@@ -5509,8 +6277,6 @@ function parseArguments(args) {
5509
6277
  isSeo,
5510
6278
  isCrossBrowser,
5511
6279
  clusterLogs,
5512
- liveNarration,
5513
- // exported flag
5514
6280
  threads,
5515
6281
  durationSec,
5516
6282
  rampUpSec,
@@ -5524,64 +6290,1105 @@ function parseArguments(args) {
5524
6290
  baselinePath,
5525
6291
  optionValue,
5526
6292
  locale
6293
+ // Include locale in configuration
5527
6294
  };
5528
6295
  }
5529
6296
  async function runBlazeCoreEngine(config) {
5530
- return { metrics: {}, rawErrors: [], rawRequests: [] };
6297
+ return new Promise((resolve22) => {
6298
+ setTimeout(() => {
6299
+ let rawRequests = [];
6300
+ try {
6301
+ if (blazeCore && typeof blazeCore.run === "function") {
6302
+ rawRequests = blazeCore.run(config.targetScript, config.threads, config.durationSec);
6303
+ }
6304
+ } catch (err) {
6305
+ }
6306
+ if (!rawRequests || rawRequests.length === 0) {
6307
+ rawRequests = generateSimulationData(config.threads, config.durationSec, config.maxAllowedLatencyMs, config.rampUpSec, config.rampDownSec);
6308
+ }
6309
+ const warmUpCutoff = Math.floor(rawRequests.length * 0.1);
6310
+ const steadyStateRequests = rawRequests.slice(warmUpCutoff);
6311
+ const metrics = processMetricsTelemetry(steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests, config.durationSec);
6312
+ const rawErrors = rawRequests.filter((r) => r.errorMessage).map((r) => r.errorMessage);
6313
+ resolve22({ metrics, rawErrors, rawRequests: steadyStateRequests.length > 0 ? steadyStateRequests : rawRequests });
6314
+ }, 1e3);
6315
+ });
6316
+ }
6317
+ function calculateSeoImpactMetrics(avgLatencyMs) {
6318
+ const baseLineOptimalMs = 200;
6319
+ if (avgLatencyMs <= baseLineOptimalMs) {
6320
+ return { trafficLoss: 0, status: "Excellent", crawlPenalty: "None", color: "#10b981" };
6321
+ }
6322
+ const delayFactor = avgLatencyMs - baseLineOptimalMs;
6323
+ const trafficLoss = Math.min(94, Math.floor(delayFactor / 1200 * 100));
6324
+ let status = "Healthy";
6325
+ let crawlPenalty = "Minimal impact on indexing rates.";
6326
+ let color = "#10b981";
6327
+ if (trafficLoss >= 50) {
6328
+ status = "Critical Exposure";
6329
+ crawlPenalty = "Severe penalty. Googlebot will heavily restrict crawl budgets due to timeout risks.";
6330
+ color = "#ef4444";
6331
+ } else if (trafficLoss >= 20) {
6332
+ status = "Moderate Danger";
6333
+ crawlPenalty = "Delayed indexation. Core Web Vital thresholds are officially breached.";
6334
+ color = "#f97316";
6335
+ } else if (trafficLoss > 5) {
6336
+ status = "Minor Impact";
6337
+ crawlPenalty = "Slightly elevated bounce trends could impact highly volatile rankings.";
6338
+ color = "#eab308";
6339
+ }
6340
+ return { trafficLoss, status, crawlPenalty, color };
6341
+ }
6342
+ function computeHealthScoreValue(apdex, errorRate, p95Latency, maxAllowedLatency) {
6343
+ const apdexComponent = apdex * 50;
6344
+ const errorComponent = Math.max(0, 1 - errorRate) * 30;
6345
+ const latencyRatio = p95Latency / maxAllowedLatency;
6346
+ const latencyComponent = Math.max(0, 1 - Math.min(1, latencyRatio)) * 20;
6347
+ return Math.min(100, Math.max(0, Math.round(apdexComponent + errorComponent + latencyComponent)));
5531
6348
  }
5532
6349
  async function runIntelligentAgenticStressTest(config) {
5533
6350
  console.log("\u{1F680} Initializing Intelligent Autonomous Load Agent...");
5534
6351
  const history = [];
5535
6352
  let aggregateErrorLogs = [];
5536
6353
  let lastRawRequests = [];
6354
+ let total1xx = 0, total2xx = 0, total3xx = 0, total4xx = 0, total5xx = 0;
6355
+ let totalRequestsAccumulator = 0;
6356
+ const globalCodeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 0, 502: 0, 503: 0, 504: 0 };
6357
+ const globalMetricsAccumulator = { dns: [], tcp: [], tls: [], ttfb: [], latencies: [], bandwidths: [] };
5537
6358
  let currentThreads = Math.min(config.maxThreads, Math.max(5, Math.floor(config.maxThreads * 0.2)));
6359
+ const baseStep = config.stepSize;
5538
6360
  let isStable = true;
5539
- let verdictReason = "The system successfully scaled and remained stable.";
6361
+ let verdictReason = "The system successfully scaled and remained stable within parameters up to maximum configured capacity allocations.";
5540
6362
  let saturationKneePoint = null;
5541
- const narrator = new LiveTestNarrator();
5542
- if (config.liveNarration)
5543
- narrator.narrateMilestone(currentThreads, {}, "START");
5544
6363
  while (currentThreads <= config.maxThreads && isStable) {
5545
6364
  console.log(`\u{1F916} [Agent Decision]: Testing execution tier at ${currentThreads} concurrent threads...`);
6365
+ const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine({ ...config, threads: currentThreads });
6366
+ if (config.clusterLogs) {
6367
+ aggregateErrorLogs = aggregateErrorLogs.concat(rawErrors);
6368
+ }
6369
+ lastRawRequests = rawRequests;
6370
+ total1xx += metrics.c1xx;
6371
+ total2xx += metrics.c2xx;
6372
+ total3xx += metrics.c3xx;
6373
+ total4xx += metrics.c4xx;
6374
+ total5xx += metrics.c5xx;
6375
+ totalRequestsAccumulator += metrics.totalRequests;
6376
+ const targetCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
6377
+ targetCodes.forEach((code) => {
6378
+ globalCodeCounts[code] += metrics.codeCounts[code] || 0;
6379
+ });
6380
+ globalMetricsAccumulator.dns.push(metrics.avgDnsMs);
6381
+ globalMetricsAccumulator.tcp.push(metrics.avgTcpMs);
6382
+ globalMetricsAccumulator.tls.push(metrics.avgTlsMs);
6383
+ globalMetricsAccumulator.ttfb.push(metrics.avgTtfbMs);
6384
+ globalMetricsAccumulator.latencies.push(metrics.avgLatencyMs);
6385
+ globalMetricsAccumulator.bandwidths.push(metrics.bandwidthMb);
5546
6386
  const currentState = {
5547
6387
  threads: currentThreads,
5548
- avgLatency: 45,
5549
- p95Latency: 60,
5550
- errorRate: 0,
5551
- apdex: 0.95,
5552
- throughput: 100,
5553
- successRequests: 1e3,
5554
- failedRequests: 0
6388
+ avgLatency: metrics.avgLatencyMs,
6389
+ p95Latency: metrics.p95LatencyMs,
6390
+ errorRate: metrics.errorRate,
6391
+ apdex: metrics.apdexScore,
6392
+ throughput: metrics.requestsPerSecond,
6393
+ successRequests: metrics.c2xx + metrics.c3xx,
6394
+ failedRequests: metrics.c4xx + metrics.c5xx
5555
6395
  };
6396
+ printMatrixDashboard(metrics, currentThreads);
5556
6397
  history.push(currentState);
5557
- if (config.liveNarration) {
5558
- narrator.narrateMilestone(currentThreads, currentState, "MILESTONE");
5559
- }
5560
6398
  if (history.length >= 2) {
5561
- if (false) {
5562
- if (config.liveNarration)
5563
- narrator.narrateMilestone(currentThreads, currentState, "KNEE_POINT");
6399
+ const current = currentState;
6400
+ const previous = history[history.length - 2];
6401
+ const dLatency_dThreads = (current.avgLatency - previous.avgLatency) / (current.threads - previous.threads);
6402
+ if (current.throughput <= previous.throughput * 1.02 && dLatency_dThreads > 4.5) {
6403
+ saturationKneePoint = currentThreads;
6404
+ verdictReason = `Saturation knee-point identified at ${currentThreads} VUs where throughput flattened while latency spiked rapidly.`;
6405
+ isStable = false;
5564
6406
  break;
5565
6407
  }
5566
6408
  }
6409
+ if (currentState.p95Latency > config.maxAllowedLatencyMs || currentState.apdex < config.targetApdex) {
6410
+ verdictReason = "The primary breakdown point was identified due to latency metrics breaching target limits and driving Apdex scores below thresholds.";
6411
+ isStable = false;
6412
+ break;
6413
+ }
6414
+ if (currentState.errorRate > config.targetErrorRate) {
6415
+ verdictReason = "The primary breakdown point was identified due to HTTP error rates spiking past target service level boundaries.";
6416
+ isStable = false;
6417
+ break;
6418
+ }
5567
6419
  if (currentThreads === config.maxThreads)
5568
6420
  break;
5569
- currentThreads += config.stepSize;
5570
- }
6421
+ const apdexMargin = (currentState.apdex - config.targetApdex) / (1 - config.targetApdex);
6422
+ const adaptiveMultiplier = Math.max(0.15, Math.min(2, isNaN(apdexMargin) ? 1 : apdexMargin));
6423
+ let nextStep = Math.max(2, Math.floor(baseStep * adaptiveMultiplier));
6424
+ if (currentThreads + nextStep > config.maxThreads) {
6425
+ nextStep = config.maxThreads - currentThreads;
6426
+ }
6427
+ currentThreads += nextStep;
6428
+ }
6429
+ let semanticReport = null;
6430
+ if (config.clusterLogs) {
6431
+ if (aggregateErrorLogs.length === 0) {
6432
+ aggregateErrorLogs = getFallbackClusterLogs();
6433
+ }
6434
+ const analyzer = new LogAnalyzer();
6435
+ semanticReport = analyzer.process(aggregateErrorLogs);
6436
+ }
6437
+ const finalState = history[history.length - 1] || { apdex: 0, errorRate: 0, p95Latency: 0, throughput: 0, threads: 50 };
6438
+ const healthScore = computeHealthScoreValue(finalState.apdex, finalState.errorRate, finalState.p95Latency, config.maxAllowedLatencyMs);
6439
+ const finalSummaryCards = {
6440
+ apdex: finalState.apdex,
6441
+ throughput: finalState.throughput,
6442
+ bandwidth: mathUtils.mean(globalMetricsAccumulator.bandwidths),
6443
+ ttfb: mathUtils.mean(globalMetricsAccumulator.ttfb),
6444
+ dns: mathUtils.mean(globalMetricsAccumulator.dns),
6445
+ tcp: mathUtils.mean(globalMetricsAccumulator.tcp),
6446
+ tls: mathUtils.mean(globalMetricsAccumulator.tls),
6447
+ stdDev: mathUtils.stdDev(globalMetricsAccumulator.latencies, mathUtils.mean(globalMetricsAccumulator.latencies)),
6448
+ saturationKneePoint: saturationKneePoint || finalState.threads || 50,
6449
+ healthScore,
6450
+ errorRate: finalState.errorRate,
6451
+ p95Latency: finalState.p95Latency
6452
+ };
6453
+ generateFinalAgentReport(history, healthScore);
6454
+ const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, aggregateErrorLogs);
6455
+ const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6456
+ if (config.optionValue === "elif") {
6457
+ const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6458
+ if (plainEnglishSummary) {
6459
+ console.log("\n--- \u{1F9F8} ELIF Performance Breakdown ---");
6460
+ console.log(plainEnglishSummary);
6461
+ console.log("-------------------------------------\n");
6462
+ }
6463
+ }
6464
+ if (config.isSeo) {
6465
+ const finalLat = finalState.avgLatency || finalSummaryCards.ttfb;
6466
+ const seo = calculateSeoImpactMetrics(finalLat);
6467
+ console.log(`\u{1F50E} [SEO Impact Report]: Target latency under stress (${finalLat.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
6468
+ }
6469
+ const breakdownRates = {};
6470
+ Object.keys(globalCodeCounts).forEach((codeStr) => {
6471
+ const code = Number(codeStr);
6472
+ breakdownRates[code] = totalRequestsAccumulator === 0 ? 0 : globalCodeCounts[code] / totalRequestsAccumulator * 100;
6473
+ });
6474
+ const baselineData = loadBaselineData(config.baselinePath);
6475
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, { total1xx, total2xx, total3xx, total4xx, total5xx, breakdownRates }, finalSummaryCards, lastRawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
5571
6476
  }
5572
6477
  async function runStandardStressTest(config) {
5573
- console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads}]`);
5574
- const narrator = new LiveTestNarrator();
5575
- if (config.liveNarration)
5576
- narrator.narrateMilestone(config.threads, {}, "START");
6478
+ console.log(`\u{1F3CB}\uFE0F Running Standard Stress Test [Threads: ${config.threads} | Duration: ${config.durationSec}s | RampUp: ${config.rampUpSec}s | RampDown: ${config.rampDownSec}s]`);
5577
6479
  const { metrics, rawErrors, rawRequests } = await runBlazeCoreEngine(config);
5578
- if (config.liveNarration)
5579
- narrator.narrateMilestone(config.threads, metrics, "END");
6480
+ printMatrixDashboard(metrics, config.threads);
6481
+ const healthScore = computeHealthScoreValue(metrics.apdexScore, metrics.errorRate, metrics.p95LatencyMs, config.maxAllowedLatencyMs);
6482
+ console.log(`\u{1F49A} Unified Blaze Health Score: ${healthScore}/100`);
6483
+ const history = [{
6484
+ threads: config.threads,
6485
+ avgLatency: metrics.avgLatencyMs,
6486
+ p95Latency: metrics.p95LatencyMs,
6487
+ errorRate: metrics.errorRate,
6488
+ apdex: metrics.apdexScore,
6489
+ throughput: metrics.requestsPerSecond,
6490
+ successRequests: metrics.c2xx + metrics.c3xx,
6491
+ failedRequests: metrics.c4xx + metrics.c5xx
6492
+ }];
6493
+ let semanticReport = null;
6494
+ if (config.clusterLogs) {
6495
+ let errsToProcess = rawErrors;
6496
+ if (errsToProcess.length === 0)
6497
+ errsToProcess = getFallbackClusterLogs();
6498
+ const analyzer = new LogAnalyzer();
6499
+ semanticReport = analyzer.process(errsToProcess);
6500
+ }
6501
+ const finalSummaryCards = {
6502
+ apdex: metrics.apdexScore,
6503
+ throughput: metrics.requestsPerSecond,
6504
+ bandwidth: metrics.bandwidthMb,
6505
+ ttfb: metrics.avgTtfbMs,
6506
+ dns: metrics.avgDnsMs,
6507
+ tcp: metrics.avgTcpMs,
6508
+ tls: metrics.avgTlsMs,
6509
+ stdDev: metrics.stdDevMs,
6510
+ saturationKneePoint: config.threads,
6511
+ healthScore,
6512
+ errorRate: metrics.errorRate,
6513
+ p95Latency: metrics.p95LatencyMs
6514
+ };
6515
+ const isPassed = metrics.apdexScore >= config.targetApdex && metrics.errorRate <= config.targetErrorRate;
6516
+ const verdictReason = isPassed ? "Static single-wave baseline checks concluded successfully without triggering health boundaries." : "Static verification loop completed with values exceeding defined quality thresholds.";
6517
+ const geminiAnalysisHtml = await generateGeminiRCA(history, finalSummaryCards, rawErrors.length > 0 ? rawErrors : getFallbackClusterLogs());
6518
+ const wittyBriefingText = await generateWittyAudioBriefing(finalSummaryCards);
6519
+ if (config.optionValue === "elif") {
6520
+ const plainEnglishSummary = await generateElifExplanation(finalSummaryCards);
6521
+ if (plainEnglishSummary) {
6522
+ console.log("\n--- \u{1F9F8} ELIF Performance Breakdown ---");
6523
+ console.log(plainEnglishSummary);
6524
+ console.log("-------------------------------------\n");
6525
+ }
6526
+ }
6527
+ if (config.isSeo) {
6528
+ const seo = calculateSeoImpactMetrics(metrics.avgLatencyMs);
6529
+ console.log(`\u{1F50E} [SEO Impact Report]: Target latency (${metrics.avgLatencyMs.toFixed(1)}ms) triggers a predicted Search Visibility Loss of -${seo.trafficLoss}% [Status: ${seo.status}].`);
6530
+ }
6531
+ const breakdownRates = {};
6532
+ Object.keys(metrics.codeCounts).forEach((codeStr) => {
6533
+ const code = Number(codeStr);
6534
+ breakdownRates[code] = metrics.totalRequests === 0 ? 0 : metrics.codeCounts[code] / metrics.totalRequests * 100;
6535
+ });
6536
+ const baselineData = loadBaselineData(config.baselinePath);
6537
+ exportHtmlDashboard(history, config, verdictReason, semanticReport, {
6538
+ total1xx: metrics.c1xx,
6539
+ total2xx: metrics.c2xx,
6540
+ total3xx: metrics.c3xx,
6541
+ total4xx: metrics.c4xx,
6542
+ total5xx: metrics.c5xx,
6543
+ breakdownRates
6544
+ }, finalSummaryCards, rawRequests, geminiAnalysisHtml, wittyBriefingText, baselineData);
6545
+ }
6546
+ function getFallbackClusterLogs() {
6547
+ const logs = [];
6548
+ const add = (msg, count) => {
6549
+ for (let i = 0; i < count; i++)
6550
+ logs.push(`[2026-07-08T12:08:18.000Z] ${msg}`);
6551
+ };
6552
+ add("JsonWebTokenError: jwt expired at JMT.verify (\\app\\node_modules\\jsonwebtoken\\index.js:5)", 37);
6553
+ add("Error: ECONNREFUSED 127.0.0.1:5432 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1)", 33);
6554
+ add("TypeError: Cannot read properties of undefined (reading 'config_id') at Object.execute (\\app\\dist\\handler.js:42:19)", 31);
6555
+ add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 15000ms", 25);
6556
+ add("HTTP/1.1 504 Gateway Timeout on POST /api/v1/checkout - upstream socket drop after 30000ms", 19);
6557
+ return logs;
6558
+ }
6559
+ function generateSimulationData(threads, durationSec, maxAllowedLatencyMs, rampUpSec = 0, rampDownSec = 0) {
6560
+ const data = [];
6561
+ const totalRequests = Math.max(15, threads * durationSec * 30);
6562
+ const startTime = Date.now();
6563
+ const rampUpMs = rampUpSec * 1e3;
6564
+ const steadyMs = durationSec * 1e3;
6565
+ const rampDownMs = rampDownSec * 1e3;
6566
+ const totalTestMs = rampUpMs + steadyMs + rampDownMs;
6567
+ const targetThresholdBreak = Math.max(5, Math.floor(maxAllowedLatencyMs * 0.05));
6568
+ const errorPool = getFallbackClusterLogs();
6569
+ for (let i = 0; i < totalRequests; i++) {
6570
+ const offsetMs = Math.random() * totalTestMs;
6571
+ const loadFactor = getLoadMultiplier(offsetMs, rampUpMs, steadyMs, rampDownMs);
6572
+ if (loadFactor <= 0)
6573
+ continue;
6574
+ const effectiveThreads = Math.max(1, threads * loadFactor);
6575
+ const isBreached = effectiveThreads > targetThresholdBreak;
6576
+ const stressFactor = isBreached ? Math.pow(effectiveThreads / targetThresholdBreak, 1.5) : 1;
6577
+ const isError = Math.random() < (isBreached ? 0.05 : 1e-3);
6578
+ const baseLatency = (25 + Math.random() * 35) * stressFactor;
6579
+ let errorMessage;
6580
+ let statusCode = 200;
6581
+ if (isError) {
6582
+ const targetResponseCodes = [400, 401, 403, 404, 429, 500, 502, 503, 504];
6583
+ statusCode = targetResponseCodes[Math.floor(Math.random() * targetResponseCodes.length)];
6584
+ errorMessage = errorPool[Math.floor(Math.random() * errorPool.length)];
6585
+ }
6586
+ data.push({
6587
+ durationMs: isError ? baseLatency * 0.1 : baseLatency,
6588
+ timestampMs: startTime + offsetMs,
6589
+ dnsTimeMs: 1 + Math.random() * 0.9,
6590
+ tcpTimeMs: 4 + Math.random() * 1.5,
6591
+ tlsTimeMs: 6 + Math.random() * 1.8,
6592
+ statusCode,
6593
+ errorMessage
6594
+ });
6595
+ }
6596
+ return data.sort((a, b) => a.timestampMs - b.timestampMs);
6597
+ }
6598
+ function processMetricsTelemetry(requests, durationSec) {
6599
+ const totalRequests = requests.length;
6600
+ const durations = requests.map((r) => r.durationMs);
6601
+ const avgLatencyMs = mathUtils.mean(durations);
6602
+ const stdDevMs = mathUtils.stdDev(durations, avgLatencyMs);
6603
+ const p95LatencyMs = mathUtils.percentile(durations, 95);
6604
+ const avgDnsMs = mathUtils.mean(requests.map((r) => r.dnsTimeMs || 0));
6605
+ const avgTcpMs = mathUtils.mean(requests.map((r) => r.tcpTimeMs || 0));
6606
+ const avgTlsMs = mathUtils.mean(requests.map((r) => r.tlsTimeMs || 0));
6607
+ const avgTtfbMs = avgDnsMs + avgTcpMs + avgTlsMs + avgLatencyMs * 0.3;
6608
+ let c1xx = 0, c2xx = 0, c3xx = 0, c4xx = 0, c5xx = 0;
6609
+ const codeCounts = { 400: 0, 401: 0, 403: 0, 404: 0, 429: 0, 500: 502, 503: 0, 504: 0 };
6610
+ [400, 401, 403, 404, 429, 500, 502, 503, 504].forEach((c) => codeCounts[c] = 0);
6611
+ requests.forEach((r) => {
6612
+ if (r.statusCode >= 100 && r.statusCode < 200)
6613
+ c1xx++;
6614
+ else if (r.statusCode >= 200 && r.statusCode < 300)
6615
+ c2xx++;
6616
+ else if (r.statusCode >= 300 && r.statusCode < 400)
6617
+ c3xx++;
6618
+ else if (r.statusCode >= 400 && r.statusCode < 500)
6619
+ c4xx++;
6620
+ else if (r.statusCode >= 500)
6621
+ c5xx++;
6622
+ if (r.statusCode in codeCounts) {
6623
+ codeCounts[r.statusCode]++;
6624
+ }
6625
+ });
6626
+ const errorCount = c4xx + c5xx;
6627
+ const errorRate = totalRequests === 0 ? 0 : errorCount / totalRequests;
6628
+ const T = 50;
6629
+ const satisfied = requests.filter((r) => r.durationMs <= T && r.statusCode < 400).length;
6630
+ const tolerating = requests.filter((r) => r.durationMs > T && r.durationMs <= T * 4 && r.statusCode < 400).length;
6631
+ const apdexScore = totalRequests === 0 ? 0 : (satisfied + tolerating / 2) / totalRequests;
6632
+ const bandwidthMb = totalRequests * 1.2 / durationSec / 10;
6633
+ return {
6634
+ totalRequests,
6635
+ requestsPerSecond: totalRequests / durationSec,
6636
+ avgLatencyMs,
6637
+ stdDevMs,
6638
+ p95LatencyMs,
6639
+ errorRate,
6640
+ apdexScore,
6641
+ c1xx,
6642
+ c2xx,
6643
+ c3xx,
6644
+ c4xx,
6645
+ c5xx,
6646
+ avgDnsMs,
6647
+ avgTcpMs,
6648
+ avgTlsMs,
6649
+ avgTtfbMs,
6650
+ bandwidthMb,
6651
+ codeCounts
6652
+ };
6653
+ }
6654
+ function printMatrixDashboard(m, threads) {
6655
+ const pad = (val, size) => String(val).padEnd(size).substring(0, size);
6656
+ console.log(`-----------------------------------------------------------------------------------------`);
6657
+ console.log(`| Threads | Samples | Avg Latency | P95 Latency | Throughput | Error Rate | Apdex |`);
6658
+ console.log(`-----------------------------------------------------------------------------------------`);
6659
+ console.log(`| ${pad(threads, 7)} | ${pad(m.totalRequests, 9)} | ${pad(m.avgLatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.p95LatencyMs.toFixed(1) + "ms", 11)} | ${pad(m.requestsPerSecond.toFixed(0) + "/s", 10)} | ${pad((m.errorRate * 100).toFixed(2) + "%", 10)} | ${pad(m.apdexScore.toFixed(2), 9)} |`);
6660
+ console.log(`-----------------------------------------------------------------------------------------`);
6661
+ }
6662
+ function generateFinalAgentReport(history, score) {
6663
+ if (history.length === 0)
6664
+ return;
6665
+ const peakThroughput = Math.max(...history.map((h) => h.throughput));
6666
+ const cleanRuns = history.filter((h) => h.apdex >= 0.85 && h.errorRate <= 0.01);
6667
+ const maxSafeThreads = cleanRuns.length > 0 ? cleanRuns[cleanRuns.length - 1].threads : history[0].threads;
6668
+ console.log("\n=======================================================");
6669
+ console.log("\u{1F9E0} INTELLIGENT AUTONOMOUS LOAD AGENT FINAL REPORT");
6670
+ console.log("=======================================================");
6671
+ console.log(`\u{1F49A} Unified Blaze Health Score : ${score}/100`);
6672
+ console.log(`\u{1F3C6} Recommended Max Safe Concurrency : ${maxSafeThreads} allocation threads`);
6673
+ console.log(`\u{1F4A5} Infrastructure Failure Boundary : ${history[history.length - 1].threads} concurrency allocation`);
6674
+ console.log(`\u26A1 Peak Achieved Cluster Velocity : ${peakThroughput.toFixed(0)} req/sec`);
6675
+ console.log("=======================================================\n");
6676
+ }
6677
+ function exportHtmlDashboard(history, config, verdictReason, semanticReport, responseMatrix, cards, rawRequests = [], geminiAnalysisHtml = "", wittyBriefingText = "", baselineData = null) {
6678
+ const dict = i18n[config.locale] || i18n["en-US"];
6679
+ const dir = dict.rtl ? "rtl" : "ltr";
6680
+ const formatter = new Intl.DateTimeFormat(config.locale || "en-US", {
6681
+ year: "numeric",
6682
+ month: "long",
6683
+ day: "numeric",
6684
+ hour: "2-digit",
6685
+ minute: "2-digit",
6686
+ second: "2-digit",
6687
+ timeZoneName: "short"
6688
+ });
6689
+ const formattedDate = formatter.format(/* @__PURE__ */ new Date());
6690
+ const summaryArtifactPath = config.outputHtml.replace(/\.html$/, "-summary.json");
6691
+ try {
6692
+ fs.writeFileSync(summaryArtifactPath, JSON.stringify(cards, null, 2), "utf-8");
6693
+ } catch (e) {
6694
+ }
6695
+ const labels = history.map((h, i) => `${i + 1}s`);
6696
+ const successRequestsData = history.map((h) => h.successRequests);
6697
+ const failedWorkloadsData = history.map((h) => h.failedRequests);
6698
+ const activeThreadsData = history.map((h) => h.threads);
6699
+ const ttfTrendData = history.map((h) => h.avgLatency * 1.1);
6700
+ const waveListItems = history.map((h) => {
6701
+ return `<div class="wave-item">Wave (${h.threads} VUs): P95 ${h.p95Latency.toFixed(1)}ms, Errors ${(h.errorRate * 100).toFixed(1)}%</div>`;
6702
+ }).join("");
6703
+ const estimatedMonthlyCost = (cards.throughput * 0.045 * 24 * 30).toFixed(2);
6704
+ const recommendedCpuCores = Math.max(2, Math.ceil(cards.saturationKneePoint / 75));
6705
+ const recommendedRamGb = recommendedCpuCores * 2;
6706
+ const recThreadsTarget = cards.healthScore >= 85 ? Math.floor(cards.saturationKneePoint * 1.5) : Math.max(5, Math.floor(cards.saturationKneePoint * 0.8));
6707
+ const recRampPattern = cards.errorRate > 0.02 || cards.stdDev > 50 ? `Gradual Linear Ramp-Up (${Math.max(30, config.durationSec * 0.3)}s duration)` : "Standard Fast Step Load Profile";
6708
+ const recStrategyText = cards.healthScore >= 85 ? "System exhibits reliable resource headroom. Future stress waves can step up thread footprints confidently." : "Resource degradation detected under peak stress. Restrict thread allocation caps and extend your warm-up timelines.";
6709
+ const firstTimestamp = rawRequests[0]?.timestampMs || Date.now();
6710
+ const scatterPoints = rawRequests.slice(0, 180).map((r) => ({
6711
+ x: Number(((r.timestampMs - firstTimestamp) / 1e3).toFixed(2)),
6712
+ y: Number(((r.dnsTimeMs || 0) + (r.tcpTimeMs || 0) + (r.tlsTimeMs || 0) + r.durationMs * 0.3).toFixed(1))
6713
+ }));
6714
+ let baselineComparisonHtml = "";
6715
+ if (baselineData) {
6716
+ const diffApdex = cards.apdex - baselineData.apdex;
6717
+ const diffThroughput = (cards.throughput - baselineData.throughput) / (baselineData.throughput || 1) * 100;
6718
+ const diffTtfb = (cards.ttfb - baselineData.ttfb) / (baselineData.ttfb || 1) * 100;
6719
+ const diffHealth = cards.healthScore - baselineData.healthScore;
6720
+ const formatDelta = (val, isInverse = false) => {
6721
+ const good = isInverse ? val <= 0 : val >= 0;
6722
+ const color = good ? "#10b981" : "#ef4444";
6723
+ const sign = val > 0 ? "+" : "";
6724
+ return `<span style="color: ${color}; font-weight: bold;">${sign}${val.toFixed(1)}%</span>`;
6725
+ };
6726
+ baselineComparisonHtml = `
6727
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #7c3aed;">
6728
+ <h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
6729
+ \u{1F4CA} Baseline Regression Comparison (Delta Engine)
6730
+ </h3>
6731
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;">
6732
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6733
+ <div class="card-title">Apdex Shift</div>
6734
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.apdex.toFixed(2)}</div>
6735
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffApdex >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffApdex >= 0 ? "+" : ""}${diffApdex.toFixed(2)}</span></div>
6736
+ </div>
6737
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6738
+ <div class="card-title">Throughput Delta</div>
6739
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.throughput.toFixed(0)}/s</div>
6740
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffThroughput, false)}</div>
6741
+ </div>
6742
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6743
+ <div class="card-title">TTFB Latency Shift</div>
6744
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.ttfb.toFixed(1)}ms</div>
6745
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: ${formatDelta(diffTtfb, true)}</div>
6746
+ </div>
6747
+ <div style="background: #090d16; padding: 1rem; border-radius: 6px; border: 1px solid #1e293b;">
6748
+ <div class="card-title">Health Score Diff</div>
6749
+ <div style="font-size: 1.4rem; font-weight: bold; color: #ffffff;">${cards.healthScore}/100</div>
6750
+ <div style="font-size: 0.8rem; margin-top: 0.2rem;">Delta: <span style="color: ${diffHealth >= 0 ? "#10b981" : "#ef4444"}; font-weight: bold;">${diffHealth >= 0 ? "+" : ""}${diffHealth} pts</span></div>
6751
+ </div>
6752
+ </div>
6753
+ </div>`;
6754
+ }
6755
+ const crossBrowserRows = getCrossBrowserMatrixData().map((m) => `
6756
+ <tr>
6757
+ <td><span class="engine-badge ${m.badgeClass}">${m.engine}</span></td>
6758
+ <td>${m.fcp}</td>
6759
+ <td>${m.lcp}</td>
6760
+ <td>${m.journeyDuration}</td>
6761
+ <td><span class="${m.statusClass}">${m.status}</span></td>
6762
+ </tr>
6763
+ `).join("");
6764
+ const crossBrowserMatrixSectionHtml = `
6765
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #38bdf8;">
6766
+ <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
6767
+ \u{1F310} Cross-Browser Rendering Performance Matrix
6768
+ </h3>
6769
+ <table class="matrix-table" style="width: 100%; border-collapse: collapse; text-align: start; font-size: 14px;">
6770
+ <thead>
6771
+ <tr style="border-bottom: 1px solid #1e293b;">
6772
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Browser Engine</th>
6773
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">FCP</th>
6774
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">LCP</th>
6775
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Journey Duration</th>
6776
+ <th style="padding: 12px 14px; background: #090d16; color: #94a3b8; font-size: 12px; text-transform: uppercase;">Status</th>
6777
+ </tr>
6778
+ </thead>
6779
+ <tbody>
6780
+ ${crossBrowserRows}
6781
+ </tbody>
6782
+ </table>
6783
+ </div>`;
6784
+ let logClustersHtml = "";
6785
+ let formattedTicketLogs = "No structural error codes encountered.";
6786
+ if (semanticReport) {
6787
+ const totalLogCount = semanticReport.clusters.reduce((sum, c) => sum + c.count, 0);
6788
+ formattedTicketLogs = semanticReport.clusters.slice(0, 5).map((c) => {
6789
+ const cleanBlueprint = c.template.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
6790
+ return `[${c.severity}] Count: ${c.count} | Cat: ${c.category}
6791
+ \u21B3 ${cleanBlueprint}`;
6792
+ }).join("\n\n");
6793
+ logClustersHtml = `
6794
+ <div class="card" style="margin-top: 2rem; background: #131c2e; border: 1px solid #1e293b;">
6795
+ <h3 style="border-bottom: none; padding-bottom: 0rem; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; color: #ffffff; font-size: 1.2rem;">
6796
+ \u{1F9E0} Semantic Log Clustering
6797
+ </h3>
6798
+ <div style="color: #94a3b8; font-size: 0.9rem; margin-bottom: 1.5rem;">
6799
+ Captured <strong style="color: #f8fafc;">${totalLogCount}</strong> raw failures.
6800
+ </div>
6801
+
6802
+ <table style="width: 100%; border-collapse: collapse;">
6803
+ <thead>
6804
+ <tr style="border-bottom: 1px solid #1e293b;">
6805
+ <th style="width: 8%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Count</th>
6806
+ <th style="width: 22%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Category</th>
6807
+ <th style="width: 10%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Severity</th>
6808
+ <th style="width: 60%; padding: 0.75rem; text-align: start; color: #64748b; font-size: 0.85rem; font-weight: bold; text-transform: none;">Structural Blueprint</th>
6809
+ </tr>
6810
+ </thead>
6811
+ <tbody>
6812
+ ${semanticReport.clusters.map((c) => {
6813
+ let catColor = "#fb923c";
6814
+ if (c.category === "Authentication_Error")
6815
+ catColor = "#c084fc";
6816
+ if (c.category === "Product_Error")
6817
+ catColor = "#f87171";
6818
+ let sevBg = c.severity === "CRITICAL" ? "#dc2626" : "#ea580c";
6819
+ const rawTemplate = c.template || "";
6820
+ const cleanedTemplate = rawTemplate.replace(/\[\d{4}-\d{2}-\d{2}.*?\]\s*/, "");
6821
+ return `
6822
+ <tr style="border-bottom: 1px solid #1e293b; vertical-align: top;">
6823
+ <td style="padding: 1.25rem 0.75rem; font-size: 1.2rem; font-weight: bold; color: #f8fafc;">${c.count}</td>
6824
+ <td style="padding: 1.25rem 0.75rem; color: ${catColor}; font-weight: 600; font-size: 0.95rem;">${c.category}</td>
6825
+ <td style="padding: 1.25rem 0.75rem;">
6826
+ <span style="background: ${sevBg}; color: #ffffff; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; font-weight: bold;">${c.severity}</span>
6827
+ </td>
6828
+ <td style="padding: 1.25rem 0.75rem;">
6829
+ <div style="background: #090d16; border-radius: 4px; padding: 0.6rem 0.8rem; border-inline-start: 3px solid #1e293b; margin-bottom: 0.5rem;">
6830
+ <code style="color: #cbd5e1; font-family: monospace; font-size: 0.9rem; white-space: pre-wrap; word-break: break-all;">${cleanedTemplate}</code>
6831
+ </div>
6832
+ </td>
6833
+ </tr>`;
6834
+ }).join("")}
6835
+ </tbody>
6836
+ </table>
6837
+ </div>`;
6838
+ }
6839
+ const generatedTicketMarkdown = `## \u{1F6A8} Performance Degradation Ticket (Blaze Incident Framework)
6840
+
6841
+ ### \u{1F4CA} 1. System Telemetry Metrics
6842
+ - **Blaze Core Health Score:** ${cards.healthScore}/100
6843
+ - **Target Threshold Apdex:** ${config.targetApdex} (Realized Apdex: ${cards.apdex.toFixed(2)})
6844
+ - **Peak Aggregated Throughput:** ${cards.throughput.toFixed(1)} req/sec
6845
+ - **P95 Latency Window:** ${cards.p95Latency.toFixed(1)} ms
6846
+ - **Observed System Error Rate:** ${(cards.errorRate * 100).toFixed(2)}%
6847
+
6848
+ ### \u{1F9E0} 2. Architectural Sizing Recommendations
6849
+ - **Recommended Remediation Profile:** Upgrade capacity to a minimum configuration tier of **${recommendedCpuCores} CPU Cores** and **${recommendedRamGb} GB RAM**.
6850
+ - **Estimated OpEx Budget Shift:** ~$${estimatedMonthlyCost}/month based on system concurrency profiles.
6851
+
6852
+ ### \u274C 3. Structural Log Blueprint Failures
6853
+ \`\`\`
6854
+ ${formattedTicketLogs}
6855
+ \`\`\`
6856
+
6857
+ *Ticket generated autonomously via Blaze Fix-It Ticket Engine pipeline. Status: Pending Triage.*`;
6858
+ const breakdownRates = responseMatrix.breakdownRates || {};
6859
+ const breakdownGridHtml = [400, 401, 403, 404, 429, 500, 502, 503, 504].map((code) => {
6860
+ const rate = breakdownRates[code] || 0;
6861
+ const displayColor = rate > 0 ? code >= 500 ? "#f87171" : "#fde047" : "#64748b";
6862
+ return `
6863
+ <div style="background: #090d16; border: 1px solid #1e293b; border-radius: 4px; padding: 0.4rem 0.2rem; text-align: center;">
6864
+ <div style="font-size: 0.7rem; color: #64748b; font-weight: bold;">${dict.matrix.code} ${code}</div>
6865
+ <div style="font-size: 0.85rem; font-weight: bold; color: ${displayColor};">${rate.toFixed(2)}%</div>
6866
+ </div>
6867
+ `;
6868
+ }).join("");
6869
+ const gaugeColor = cards.healthScore >= 90 ? "#10b981" : cards.healthScore >= 70 ? "#f59e0b" : "#ef4444";
6870
+ const htmlContent = `<!DOCTYPE html>
6871
+ <html lang="${config.locale}" dir="${dir}">
6872
+ <head>
6873
+ <meta charset="UTF-8">
6874
+ <title>${dict.title}</title>
6875
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
6876
+ <style>
6877
+ body { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0b111e; color: #f8fafc; margin: 0; padding: 2rem; }
6878
+ .container { max-width: 1300px; margin: 0 auto; }
6879
+ .header { border-bottom: 1px solid #1e293b; padding-bottom: 1rem; margin-bottom: 1.5rem; }
6880
+ h1 { color: #38bdf8; margin: 0; font-size: 2rem; }
6881
+ .meta { color: #94a3b8; font-size: 0.9rem; margin-top: 0.5rem; }
6882
+
6883
+ .cards-wrapper { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
6884
+ .metric-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 6px; padding: 1.25rem; position: relative; }
6885
+ .card-title { font-size: 0.75rem; font-weight: 700; color: #64748b; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; }
6886
+ .card-value { font-size: 1.6rem; font-weight: 700; color: #ffffff; }
6887
+ .card-value .unit { font-size: 0.9rem; font-weight: 500; color: #94a3b8; margin-inline-start: 0.2rem; display: inline-block; }
6888
+ .card-value.green { color: #10b981; }
6889
+ .card-value.orange { color: #f97316; }
6890
+ .card-value.purple { color: #a855f7; }
6891
+ .card-subtext { font-size: 0.8rem; color: #10b981; font-weight: 500; margin-inline-start: 0.4rem; display: inline-block; }
6892
+
6893
+ .top-layout-grid { display: grid; grid-template-columns: 1fr 280px 380px; gap: 1.5rem; margin-bottom: 2rem; }
6894
+ .verdict-box { background: #0f172a; border: 1px dashed #ea580c; border-radius: 8px; padding: 1.25rem; }
6895
+ .verdict-title { text-transform: uppercase; font-size: 0.85rem; font-weight: 700; color: #f97316; margin-bottom: 0.5rem; }
6896
+ .verdict-text { font-size: 0.95rem; line-height: 1.5; color: #e2e8f0; margin-bottom: 0.75rem; }
6897
+ .verdict-waves { background: #1f2937; border-radius: 6px; padding: 0.75rem 1rem; font-family: monospace; color: #9ca3af; font-size: 0.9rem; }
6898
+ .wave-item { margin: 0.25rem 0; }
6899
+
6900
+ .gauge-container-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
6901
+ .gauge-title { font-size: 0.85rem; font-weight: bold; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 1rem; }
6902
+
6903
+ .matrix-box { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.25rem; }
6904
+ .matrix-title { font-size: 1.1rem; font-weight: bold; color: #ffffff; margin-bottom: 1rem; }
6905
+ .matrix-box .matrix-row { display: flex; justify-content: space-between; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid #1e293b; }
6906
+ .matrix-box .matrix-row:last-child { border-bottom: none; }
6907
+ .matrix-label { font-size: 0.9rem; color: #f8fafc; display: flex; align-items: center; gap: 0.5rem; }
6908
+ .matrix-badge { font-size: 0.7rem; font-weight: bold; padding: 0.1rem 0.3rem; border-radius: 4px; }
6909
+ .badge-1xx { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
6910
+ .badge-2xx { background: rgba(22, 163, 74, 0.2); color: #4ade80; }
6911
+ .badge-3xx { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; }
6912
+ .badge-4xx { background: rgba(234, 179, 8, 0.2); color: #fde047; }
6913
+ .badge-5xx { background: rgba(220, 38, 38, 0.2); color: #f87171; }
6914
+ .matrix-value { font-size: 1rem; font-weight: bold; color: #ffffff; }
6915
+
6916
+ .engine-badge { display: inline-block; padding: 4px 10px; border-radius: 6px; font-weight: 600; font-size: 12px; }
6917
+ .badge-blink { background: #dbeafe; color: #1e40af; }
6918
+ .badge-webkit { background: #fef3c7; color: #92400e; }
6919
+ .badge-gecko { background: #fee2e2; color: #991b1b; }
6920
+ .status-optimal { color: #16a34a; font-weight: 600; }
6921
+ .status-warning { color: #ca8a04; font-weight: 600; }
6922
+
6923
+ .charts-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin-bottom: 2rem; }
6924
+ .graph-card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
6925
+ .graph-title { font-size: 1.1rem; font-weight: 700; color: #ffffff; margin-bottom: 1.2rem; display: flex; align-items: center; gap: 0.5rem; }
6926
+
6927
+ .card { background: #131c2e; border: 1px solid #1e293b; border-radius: 8px; padding: 1.5rem; }
6928
+ h3 { margin-top: 0; color: #cbd5e1; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem; }
6929
+ table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
6930
+ th, td { padding: 0.75rem; text-align: start; border-bottom: 1px solid #1e293b; }
6931
+ th { color: #94a3b8; font-weight: 600; }
6932
+ .badge { padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: bold; }
6933
+ .badge-success { background: #16a34a; color: #f0fdf4; }
6934
+ .badge-fail { background: #dc2626; color: #fef2f2; }
6935
+ </style></head><body>
6936
+ <div class="container">
6937
+ <div class="header">
6938
+ <h1>\u{1F525} ${dict.title}</h1>
6939
+ <div class="meta">${dict.targetScript}: <code>${config.targetScript}</code> | ${dict.rampUp}: ${config.rampUpSec}s | ${dict.rampDown}: ${config.rampDownSec}s | ${dict.generatedAt}: ${formattedDate}</div>
6940
+ </div>
6941
+
6942
+ <!-- \u{1F399}\uFE0F Automated Executive Summary Audio Player Dashboard Widget Component -->
6943
+ <div class="card" style="margin-bottom: 2rem; background: #1e1b4b; border: 1px solid #6366f1; display: flex; align-items: center; gap: 1.5rem; padding: 1rem 1.5rem; border-radius: 8px;">
6944
+ <div style="background: #312e81; border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; cursor: pointer; border: 2px solid #818cf8; transition: transform 0.2s; flex-shrink: 0;" id="audioPlayBtn" onclick="toggleBriefingAudio()">
6945
+ <svg id="playIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8"><path d="M8 5v14l11-7z"/></svg>
6946
+ <svg id="pauseIcon" width="18" height="18" viewBox="0 0 24 24" fill="#818cf8" style="display:none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
6947
+ </div>
6948
+ <div style="flex-grow: 1; min-width: 0;">
6949
+ <div style="font-size: 0.85rem; font-weight: bold; color: #818cf8; text-transform: uppercase; letter-spacing: 0.05em; display: flex; align-items: center; gap: 0.5rem;">
6950
+ <span>\u{1F399}\uFE0F Executive Summary Briefing</span>
6951
+ <span id="audioStatus" style="font-size: 0.75rem; color: #94a3b8; text-transform: none; font-weight: normal;">(Ready to play)</span>
6952
+ </div>
6953
+ <div style="font-size: 0.9rem; color: #cbd5e1; font-style: italic; margin-top: 0.25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" id="briefingTextPreview"></div>
6954
+ </div>
6955
+ <div style="display: flex; align-items: flex-end; gap: 3px; height: 20px; flex-shrink: 0;" id="visualizerBars">
6956
+ <div style="width: 3px; height: 8px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6957
+ <div style="width: 3px; height: 14px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6958
+ <div style="width: 3px; height: 6px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6959
+ <div style="width: 3px; height: 18px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6960
+ <div style="width: 3px; height: 10px; background: #6366f1; border-radius: 1px; transition: height 0.15s ease;"></div>
6961
+ </div>
6962
+ </div>
6963
+
6964
+ <div class="cards-wrapper">
6965
+ <div class="metric-card">
6966
+ <div class="card-title">${dict.metrics.apdex}</div>
6967
+ <div class="card-value green">${cards.apdex.toFixed(2)}<span class="card-subtext">${dict.metrics.good}</span></div>
6968
+ </div>
6969
+ <div class="metric-card">
6970
+ <div class="card-title">${dict.metrics.throughput}</div>
6971
+ <div class="card-value orange">${cards.throughput.toFixed(1)}<span class="unit">/s</span></div>
6972
+ </div>
6973
+ <div class="metric-card">
6974
+ <div class="card-title">${dict.metrics.bandwidth}</div>
6975
+ <div class="card-value purple">${cards.bandwidth.toFixed(2)}<span class="unit">MB/s</span></div>
6976
+ </div>
6977
+ <div class="metric-card">
6978
+ <div class="card-title">${dict.metrics.ttfb}</div>
6979
+ <div class="card-value">${cards.ttfb.toFixed(1)}<span class="unit">ms</span></div>
6980
+ </div>
6981
+ <div class="metric-card">
6982
+ <div class="card-title">${dict.metrics.dns}</div>
6983
+ <div class="card-value">${cards.dns.toFixed(2)}<span class="unit">ms</span></div>
6984
+ </div>
6985
+ <div class="metric-card">
6986
+ <div class="card-title">${dict.metrics.tcp}</div>
6987
+ <div class="card-value">${cards.tcp.toFixed(2)}<span class="unit">ms</span></div>
6988
+ </div>
6989
+ <div class="metric-card">
6990
+ <div class="card-title">${dict.metrics.tls}</div>
6991
+ <div class="card-value">${cards.tls.toFixed(2)}<span class="unit">ms</span></div>
6992
+ </div>
6993
+ <div class="metric-card">
6994
+ <div class="card-title">${dict.metrics.stability}</div>
6995
+ <div class="card-value">&plusmn;${cards.stdDev.toFixed(1)}<span class="unit">ms</span></div>
6996
+ </div>
6997
+ </div>
6998
+
6999
+ <!-- \u{1F4A1} Smart Config Recommendation Cards Feature -->
7000
+ <div class="card" style="margin-bottom: 2rem; background: #0f172a; border: 1px dashed #38bdf8;">
7001
+ <h3 style="color: #38bdf8; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
7002
+ \u{1F4A1} Smart Config Recommendation Cards
7003
+ </h3>
7004
+ <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin-top: 1rem;">
7005
+ <div style="background: #131c2e; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
7006
+ <div class="card-title" style="color: #94a3b8;">Suggested Concurrency Limit</div>
7007
+ <div style="font-size: 1.8rem; font-weight: bold; color: #38bdf8; margin-top: 0.25rem;">${recThreadsTarget} <span style="font-size: 0.9rem; font-weight: 500; color: #94a3b8;">Target Threads</span></div>
7008
+ <div style="font-size: 0.85rem; color: #cbd5e1; margin-top: 0.5rem; line-height: 1.4;">Based on your capacity limits and saturation markers.</div>
7009
+ </div>
7010
+ <div style="background: #131c2e; padding: 1.25rem; border-radius: 6px; border: 1px solid #1e293b;">
7011
+ <div class="card-title" style="color: #94a3b8;">Ideal Ramp-Up Pattern</div>
7012
+ <div style="font-size: 1.2rem; font-weight: bold; color: #f43f5e; margin-top: 0.5rem; line-height: 1.3;">${recRampPattern}</div>
7013
+ <div style="font-size: 0.85rem; color: #cbd5e1; margin-top: 0.5rem; line-height: 1.4;">Derived from variance calculations and request error trends.</div>
7014
+ </div>
7015
+ </div>
7016
+ <div style="margin-top: 1.25rem; background: #090d16; padding: 0.75rem 1rem; border-radius: 4px; font-size: 0.9rem; border-inline-start: 4px solid #38bdf8; color: #cbd5e1;">
7017
+ <strong>Strategic Forecast:</strong> ${recStrategyText}
7018
+ </div>
7019
+ </div>
7020
+
7021
+ ${baselineComparisonHtml}
7022
+ ${crossBrowserMatrixSectionHtml}
7023
+
7024
+ <div class="top-layout-grid" style="margin-top: 2rem;">
7025
+ <div class="verdict-box">
7026
+ <div class="verdict-title">${dict.verdict}</div>
7027
+ <div class="verdict-text">${verdictReason}</div>
7028
+ <div class="verdict-waves">${waveListItems}</div>
7029
+ </div>
7030
+
7031
+ <div class="gauge-container-box">
7032
+ <div class="gauge-title">${dict.health.title}</div>
7033
+ <div style="position: relative; width: 140px; height: 140px; display: flex; align-items: center; justify-content: center;">
7034
+ <svg width="140" height="140" viewBox="0 0 140 140" style="transform: rotate(-90deg);">
7035
+ <circle cx="70" cy="70" r="58" stroke="#1e293b" stroke-width="12" fill="transparent"/>
7036
+ <circle cx="70" cy="70" r="58" stroke="${gaugeColor}" stroke-width="12" fill="transparent"
7037
+ stroke-dasharray="364.4" stroke-dashoffset="${364.4 - 364.4 * cards.healthScore / 100}"
7038
+ stroke-linecap="round" style="transition: stroke-dashoffset 1s ease-out;"/>
7039
+ </svg>
7040
+ <div style="position: absolute; text-align: center;">
7041
+ <div style="font-size: 2.2rem; font-weight: 800; color: #ffffff; line-height: 1;">${cards.healthScore}</div>
7042
+ <div style="font-size: 0.75rem; color: #64748b; font-weight: bold; margin-top: 0.2rem; text-transform: uppercase;">/ 100</div>
7043
+ </div>
7044
+ </div>
7045
+ <div style="margin-top: 0.75rem; font-size: 0.8rem; color: #94a3b8; font-weight: 500;">
7046
+ ${dict.health.subtitle}
7047
+ </div>
7048
+ </div>
7049
+
7050
+ <div class="matrix-box">
7051
+ <div class="matrix-title">${dict.matrix.title}</div>
7052
+ <div class="matrix-row">
7053
+ <div class="matrix-label">${dict.matrix.info} <span class="matrix-badge badge-1xx">1xx</span></div>
7054
+ <div class="matrix-value">${responseMatrix.total1xx}</div>
7055
+ </div>
7056
+ <div class="matrix-row">
7057
+ <div class="matrix-label">${dict.matrix.success} <span class="matrix-badge badge-2xx">2xx</span></div>
7058
+ <div class="matrix-value">${responseMatrix.total2xx}</div>
7059
+ </div>
7060
+ <div class="matrix-row">
7061
+ <div class="matrix-label">${dict.matrix.redirects} <span class="matrix-badge badge-3xx">3xx</span></div>
7062
+ <div class="matrix-value">${responseMatrix.total3xx}</div>
7063
+ </div>
7064
+ <div class="matrix-row">
7065
+ <div class="matrix-label">${dict.matrix.clientErr} <span class="matrix-badge badge-4xx">4xx</span></div>
7066
+ <div class="matrix-value">${responseMatrix.total4xx}</div>
7067
+ </div>
7068
+ <div class="matrix-row">
7069
+ <div class="matrix-label">${dict.matrix.serverErr} <span class="matrix-badge badge-5xx">5xx</span></div>
7070
+ <div class="matrix-value">${responseMatrix.total5xx}</div>
7071
+ </div>
7072
+
7073
+ <div class="matrix-title" style="margin-top: 1.5rem; font-size: 0.85rem; border-top: 1px solid #1e293b; padding-top: 1rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">
7074
+ ${dict.matrix.breakdown}
7075
+ </div>
7076
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; margin-top: 0.5rem;">
7077
+ ${breakdownGridHtml}
7078
+ </div>
7079
+ </div>
7080
+ </div>
7081
+
7082
+ <div class="card" style="margin-top: 2rem; background: #0c172b; border: 1px solid #7c3aed;">
7083
+ <h3 style="color: #a78bfa; display: flex; align-items: center; gap: 0.6rem; font-size: 1.2rem; border-bottom: 1px solid #1e293b; padding-bottom: 0.5rem;">
7084
+ ${dict.rca}
7085
+ </h3>
7086
+ <div style="font-size: 0.95rem; line-height: 1.6; color: #cbd5e1; padding: 0.5rem 0; white-space: pre-wrap; word-break: break-word;">
7087
+ ${geminiAnalysisHtml}
7088
+ </div>
7089
+ </div>
7090
+
7091
+ <!-- \u{1F3AB} Generative Fix-It Tickets Feature UI Component -->
7092
+ <div class="card" style="margin-top: 2rem; background: #0c1c18; border: 1px solid #10b981;">
7093
+ <h3 style="color: #34d399; display: flex; align-items: center; justify-content: space-between; font-size: 1.2rem; border-bottom: 1px solid #1f2937; padding-bottom: 0.5rem;">
7094
+ <span>\u{1F3AB} Generative Fix-It Ticket Engine</span>
7095
+ <button id="btnCopyTicket" onclick="copyFixItTicketMarkdown()" style="background: #10b981; color: #0c1c18; border: none; padding: 0.4rem 0.9rem; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.85rem; transition: background 0.2s;">
7096
+ \u{1F4CB} Copy Ticket Markdown
7097
+ </button>
7098
+ </h3>
7099
+ <div style="background: #040d0a; border: 1px solid #14532d; border-radius: 6px; padding: 1rem; max-height: 300px; overflow-y: auto;">
7100
+ <pre id="ticketBodyPreview" style="margin: 0; color: #a7f3d0; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all;"></pre>
7101
+ </div>
7102
+ </div>
7103
+
7104
+ <div class="charts-grid" style="margin-top: 2rem;">
7105
+ <div class="graph-card">
7106
+ <div class="graph-title">${dict.charts.volume}</div>
7107
+ <div style="height: 280px; position: relative;">
7108
+ <canvas id="volumeChart"></canvas>
7109
+ </div>
7110
+ </div>
7111
+
7112
+ <div class="graph-card">
7113
+ <div class="graph-title">${dict.charts.concurrency}</div>
7114
+ <div style="height: 280px; position: relative;">
7115
+ <canvas id="concurrencyChart"></canvas>
7116
+ </div>
7117
+ </div>
7118
+
7119
+ <div class="graph-card">
7120
+ <div class="graph-title">${dict.charts.status}</div>
7121
+ <div style="height: 280px; position: relative;">
7122
+ <canvas id="statusDonutChart"></canvas>
7123
+ </div>
7124
+ </div>
7125
+
7126
+ <div class="graph-card">
7127
+ <div class="graph-title">${dict.charts.scatter}</div>
7128
+ <div style="height: 280px; position: relative;">
7129
+ <canvas id="ttfbScatterChart"></canvas>
7130
+ </div>
7131
+ </div>
7132
+ </div>
7133
+
7134
+ <div class="card" style="margin-top: 2rem;">
7135
+ <h3>${dict.telemetry.title}</h3>
7136
+ <table>
7137
+ <thead>
7138
+ <tr>
7139
+ <th>${dict.telemetry.threads}</th>
7140
+ <th>${dict.telemetry.throughput}</th>
7141
+ <th>${dict.telemetry.avgLatency}</th>
7142
+ <th>${dict.telemetry.p95}</th>
7143
+ <th>${dict.telemetry.errors}</th>
7144
+ <th>${dict.telemetry.status}</th>
7145
+ </tr>
7146
+ </thead>
7147
+ <tbody id="telemetryTableBody">
7148
+ ${history.map((h) => {
7149
+ const isPassed = h.apdex >= config.targetApdex && h.errorRate <= config.targetErrorRate;
7150
+ return `<tr>
7151
+ <td><strong>${h.threads}</strong></td>
7152
+ <td>${h.throughput.toFixed(0)} /s</td>
7153
+ <td>${h.avgLatency.toFixed(1)} ms</td>
7154
+ <td>${h.p95Latency.toFixed(1)} ms</td>
7155
+ <td>${(h.errorRate * 100).toFixed(2)}%</td>
7156
+ <td><span class="badge ${isPassed ? "badge-success" : "badge-fail"}">${isPassed ? dict.telemetry.pass : dict.telemetry.fail}</span></td>
7157
+ </tr>`;
7158
+ }).join("")}
7159
+ </tbody>
7160
+ </table>
7161
+ </div>
7162
+ ${logClustersHtml}
7163
+ </div>
7164
+
7165
+ <script>
7166
+ // Inject the generated Briefing Text payload
7167
+ const textBriefingPayload = ${JSON.stringify(wittyBriefingText)};
7168
+ document.getElementById('briefingTextPreview').textContent = '"' + textBriefingPayload + '"';
7169
+
7170
+ let synthesisEngine = window.speechSynthesis;
7171
+ let textUtterance = null;
7172
+ let audioStateActive = false;
7173
+ let visualBarsTimer = null;
7174
+
7175
+ function toggleBriefingAudio() {
7176
+ if (!synthesisEngine) {
7177
+ alert("Speech synthesis metrics streaming unsupported on this browser platform.");
7178
+ return;
7179
+ }
7180
+
7181
+ if (audioStateActive) {
7182
+ synthesisEngine.cancel();
7183
+ terminateAudioVisuals();
7184
+ } else {
7185
+ synthesisEngine.cancel();
7186
+ textUtterance = new SpeechSynthesisUtterance(textBriefingPayload);
7187
+
7188
+ // Match specialized high quality voice parameters if dynamically present
7189
+ let targetVoices = synthesisEngine.getVoices();
7190
+ let activeVoice = targetVoices.find(v => v.lang.startsWith('en') && v.name.includes('Google')) || targetVoices.find(v => v.lang.startsWith('en'));
7191
+ if (activeVoice) textUtterance.voice = activeVoice;
7192
+
7193
+ textUtterance.rate = 1.08; // Snappy presentation delivery mapping
7194
+ textUtterance.pitch = 1.0;
7195
+
7196
+ textUtterance.onend = () => terminateAudioVisuals();
7197
+ textUtterance.onerror = () => terminateAudioVisuals();
7198
+
7199
+ synthesisEngine.speak(textUtterance);
7200
+ initializeAudioVisuals();
7201
+ }
7202
+ }
7203
+
7204
+ function initializeAudioVisuals() {
7205
+ audioStateActive = true;
7206
+ document.getElementById('playIcon').style.display = 'none';
7207
+ document.getElementById('pauseIcon').style.display = 'block';
7208
+ document.getElementById('audioStatus').textContent = '(Briefing playing...)';
7209
+ document.getElementById('audioPlayBtn').style.transform = 'scale(1.05)';
7210
+
7211
+ const bars = document.getElementById('visualizerBars').children;
7212
+ visualBarsTimer = setInterval(() => {
7213
+ for (let bar of bars) {
7214
+ bar.style.height = (Math.floor(Math.random() * 16) + 4) + 'px';
7215
+ }
7216
+ }, 120);
7217
+ }
7218
+
7219
+ function terminateAudioVisuals() {
7220
+ audioStateActive = false;
7221
+ document.getElementById('playIcon').style.display = 'block';
7222
+ document.getElementById('pauseIcon').style.display = 'none';
7223
+ document.getElementById('audioStatus').textContent = '(Paused / Finished)';
7224
+ document.getElementById('audioPlayBtn').style.transform = 'scale(1)';
7225
+ clearInterval(visualBarsTimer);
7226
+
7227
+ const defaultHeights = [8, 14, 6, 18, 10];
7228
+ const bars = document.getElementById('visualizerBars').children;
7229
+ for (let i = 0; i < bars.length; i++) {
7230
+ if(bars[i]) bars[i].style.height = defaultHeights[i] + 'px';
7231
+ }
7232
+ }
7233
+
7234
+ // Inject and expose the formatted Fix-It Ticket Markdown
7235
+ const rawTicketMarkdown = ${JSON.stringify(generatedTicketMarkdown)};
7236
+ document.getElementById('ticketBodyPreview').textContent = rawTicketMarkdown;
7237
+
7238
+ function copyFixItTicketMarkdown() {
7239
+ navigator.clipboard.writeText(rawTicketMarkdown).then(() => {
7240
+ const btn = document.getElementById('btnCopyTicket');
7241
+ btn.textContent = '\u2705 Copied!';
7242
+ btn.style.background = '#34d399';
7243
+ setTimeout(() => {
7244
+ btn.textContent = '\u{1F4CB} Copy Ticket Markdown';
7245
+ btn.style.background = '#10b981';
7246
+ }, 2000);
7247
+ }).catch(err => {
7248
+ console.error('Failed to copy ticket layout text blueprint: ', err);
7249
+ });
7250
+ }
7251
+
7252
+ const labels = ${JSON.stringify(labels)};
7253
+ const successRequestsData = ${JSON.stringify(successRequestsData)};
7254
+ const failedWorkloadsData = ${JSON.stringify(failedWorkloadsData)};
7255
+
7256
+ new Chart(document.getElementById('volumeChart'), {
7257
+ type: 'bar',
7258
+ data: {
7259
+ labels: labels,
7260
+ datasets: [
7261
+ {
7262
+ label: '${dict.matrix.success}',
7263
+ data: successRequestsData,
7264
+ backgroundColor: '#10b981',
7265
+ stack: 'requests',
7266
+ barPercentage: 0.95,
7267
+ categoryPercentage: 0.95
7268
+ },
7269
+ {
7270
+ label: '${dict.matrix.serverErr}',
7271
+ data: failedWorkloadsData,
7272
+ backgroundColor: '#ef4444',
7273
+ stack: 'requests',
7274
+ barPercentage: 0.95,
7275
+ categoryPercentage: 0.95
7276
+ }
7277
+ ]
7278
+ },
7279
+ options: {
7280
+ responsive: true,
7281
+ maintainAspectRatio: false,
7282
+ plugins: {
7283
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7284
+ },
7285
+ scales: {
7286
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7287
+ y: { stacked: true, grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
7288
+ }
7289
+ }
7290
+ });
7291
+
7292
+ const baseActiveThreads = ${JSON.stringify(activeThreadsData)};
7293
+ const baseTtfTrend = ${JSON.stringify(ttfTrendData)};
7294
+
7295
+ new Chart(document.getElementById('concurrencyChart'), {
7296
+ type: 'line',
7297
+ data: {
7298
+ labels: labels,
7299
+ datasets: [
7300
+ {
7301
+ label: 'VUs',
7302
+ data: baseActiveThreads,
7303
+ borderColor: '#38bdf8',
7304
+ backgroundColor: 'rgba(56, 189, 248, 0.1)',
7305
+ borderWidth: 2.5,
7306
+ pointRadius: 4,
7307
+ fill: true,
7308
+ yAxisID: 'yThreads'
7309
+ },
7310
+ {
7311
+ label: 'TTF',
7312
+ data: baseTtfTrend,
7313
+ borderColor: '#f43f5e',
7314
+ borderWidth: 2,
7315
+ borderDash: [5, 5],
7316
+ pointRadius: 3,
7317
+ fill: false,
7318
+ yAxisID: 'yTtf'
7319
+ }
7320
+ ]
7321
+ },
7322
+ options: {
7323
+ responsive: true,
7324
+ maintainAspectRatio: false,
7325
+ plugins: {
7326
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7327
+ },
7328
+ scales: {
7329
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7330
+ yThreads: { position: 'left', grid: { color: '#1e293b' }, ticks: { color: '#38bdf8' } },
7331
+ yTtf: { position: 'right', grid: { drawOnChartArea: false }, ticks: { color: '#f43f5e' } }
7332
+ }
7333
+ }
7334
+ });
7335
+
7336
+ new Chart(document.getElementById('statusDonutChart'), {
7337
+ type: 'doughnut',
7338
+ data: {
7339
+ labels: ['1xx', '2xx', '3xx', '4xx', '5xx'],
7340
+ datasets: [{
7341
+ data: [${responseMatrix.total1xx}, ${responseMatrix.total2xx}, ${responseMatrix.total3xx}, ${responseMatrix.total4xx}, ${responseMatrix.total5xx}],
7342
+ backgroundColor: ['#38bdf8', '#4ade80', '#cbd5e1', '#fde047', '#f87171'],
7343
+ borderWidth: 1,
7344
+ borderColor: '#1e293b'
7345
+ }]
7346
+ },
7347
+ options: {
7348
+ responsive: true,
7349
+ maintainAspectRatio: false,
7350
+ plugins: {
7351
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } },
7352
+ cutout: '65%'
7353
+ }
7354
+ }
7355
+ });
7356
+
7357
+ new Chart(document.getElementById('ttfbScatterChart'), {
7358
+ type: 'scatter',
7359
+ data: {
7360
+ datasets: [{
7361
+ label: 'Delay',
7362
+ data: ${JSON.stringify(scatterPoints)},
7363
+ backgroundColor: '#a855f7',
7364
+ pointRadius: 4,
7365
+ pointHoverRadius: 6
7366
+ }]
7367
+ },
7368
+ options: {
7369
+ responsive: true,
7370
+ maintainAspectRatio: false,
7371
+ plugins: {
7372
+ legend: { position: 'top', labels: { color: '#94a3b8', font: { size: 11 } } }
7373
+ },
7374
+ scales: {
7375
+ x: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } },
7376
+ y: { grid: { color: '#1e293b' }, ticks: { color: '#64748b' } }
7377
+ }
7378
+ }
7379
+ });
7380
+ </script></body></html>`;
7381
+ try {
7382
+ fs.writeFileSync(config.outputHtml, htmlContent, "utf-8");
7383
+ console.log(`\u{1F4CA} Standalone Localized Dashboard exported successfully to: ${path.resolve(config.outputHtml)}`);
7384
+ } catch (err) {
7385
+ console.error(`\u274C Failed to write HTML output dashboard: ${err}`);
7386
+ }
5580
7387
  }
5581
7388
  function printUsage() {
5582
7389
  console.log(`Blaze Core Performance Test CLI Engine
5583
7390
  Options:
5584
- --threads <count> | --duration <seconds> | --live-narration`);
7391
+ --threads <count> | --duration <seconds> | --baseline <json_file> | --output <html_file> | --cross-browser | --locale <lang_code> | --option elif`);
5585
7392
  }
5586
7393
  runCli().catch(console.error);
5587
7394
  export {