#!/usr/bin/env pwsh # CASAN H6 AgentOps Dashboard Generator (auto-refresh HTML) # Usage: # generate-agentops-dashboard.ps1 [-FeatureId ] [-OutputHtml ] param( [string]$FeatureId = "latest", [string]$OutputHtml = "" ) $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent $projectRoot = (Resolve-Path (Join-Path $scriptDir "../../..")).Path $metricsLog = if ($env:CASAN_TELEMETRY_METRICS_LOG) { $env:CASAN_TELEMETRY_METRICS_LOG } elseif ($env:CASAN_DASHBOARD_METRICS) { $env:CASAN_DASHBOARD_METRICS } else { Join-Path $projectRoot ".specify/logs/cost/metrics.jsonl" } $alertLog = if ($env:CASAN_TELEMETRY_ALERTS_LOG) { $env:CASAN_TELEMETRY_ALERTS_LOG } elseif ($env:CASAN_DASHBOARD_ALERTS) { $env:CASAN_DASHBOARD_ALERTS } else { Join-Path $projectRoot ".specify/agentops/alerts.log" } if (!$OutputHtml) { $OutputHtml = Join-Path $projectRoot "docs/output/casan/agentops-dashboard.html" } if (!(Test-Path (Split-Path $OutputHtml -Parent))) { New-Item -ItemType Directory -Force -Path (Split-Path $OutputHtml -Parent) | Out-Null } $generatedAt = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") # ── Read metrics ─────────────────────────────────────────────────────────── $metrics = @() if (Test-Path $metricsLog) { $metrics = Get-Content $metricsLog | ForEach-Object { try { $_ | ConvertFrom-Json } catch { $null } } | Where-Object { $_ } } $alerts = @() if (Test-Path $alertLog) { $alerts = Get-Content $alertLog | ForEach-Object { try { $_ | ConvertFrom-Json } catch { $null } } | Where-Object { $_ } } # ── Aggregations ─────────────────────────────────────────────────────────── $totalRuns = $metrics.Count $totalCost = [math]::Round(($metrics | Measure-Object -Property cost_estimate -Sum).Sum, 4) $totalTokens = ($metrics | Measure-Object -Property tokens_estimated -Sum).Sum $avgLatency = if ($totalRuns) { [math]::Round(($metrics | Measure-Object -Property latency_ms -Average).Average, 0) } else { 0 } $failedRuns = ($metrics | Where-Object { $_.status -eq "failed" }).Count $passRate = if ($totalRuns) { [math]::Round(($totalRuns - $failedRuns) / $totalRuns * 100, 1) } else { 100 } $totalAlerts = $alerts.Count # Per-step table rows $stepRows = $metrics | Group-Object step | ForEach-Object { $g = $_ $avgLat = [math]::Round(($g.Group | Measure-Object latency_ms -Average).Average, 0) $totCost = [math]::Round(($g.Group | Measure-Object cost_estimate -Sum).Sum, 4) $runs = $g.Group.Count $fails = ($g.Group | Where-Object { $_.status -eq "failed" }).Count "$($g.Name)$runs${avgLat}ms$$totCost$fails" } # Alert rows $alertRows = $alerts | Select-Object -Last 20 | ForEach-Object { $sev = if ($_.severity -eq "WARN") { "style='color:orange'" } else { "style='color:red'" } $msg = $_.body.message "$($_.severity)$($_.timestamp)$msg" } $html = @" CASAN AgentOps Dashboard — $FeatureId

CASAN AgentOps Dashboard

Feature: $FeatureId  |  Generated: $generatedAt
$totalRuns
Total Runs
${passRate}%
Pass Rate
${avgLatency}ms
Avg Latency
$totalTokens
Total Tokens (est.)
\$$totalCost
Est. Cost (USD)
$totalAlerts
Alerts

Per-Step Breakdown

$($stepRows -join "`n")
StepRunsAvg LatencyEst. CostFailures

Recent Alerts (last 20)

$(if($alertRows){$alertRows -join "`n"} else {""})
SeverityTimestampMessage
No alerts — all clear ✓
"@ Set-Content -Path $OutputHtml -Value $html -Encoding UTF8 Write-Output "AGENTOPS_DASHBOARD_GENERATED: $OutputHtml"