diff --git a/dashboard/View-SwarmDashboard.ps1 b/dashboard/View-SwarmDashboard.ps1 new file mode 100644 index 0000000..d03173d --- /dev/null +++ b/dashboard/View-SwarmDashboard.ps1 @@ -0,0 +1,51 @@ +$traceFile = "C:\\PersonalRepo\\.planning\\traces.jsonl" +$clearString = [char]27 + "[2J" + [char]27 + "[H" + +while ($true) { + Write-Host -NoNewline $clearString + Write-Host "================================================================================" -ForegroundColor Cyan + Write-Host " LIVE SWARM DASHBOARD (2026 OTel Architecture) " -ForegroundColor White -BackgroundColor DarkBlue + Write-Host "================================================================================`n" -ForegroundColor Cyan + + if (Test-Path $traceFile) { + # Read the last 15 lines so the dashboard doesn't overflow + $rawLines = Get-Content -Path $traceFile -Tail 15 -ErrorAction SilentlyContinue + + $logs = @() + foreach ($line in $rawLines) { + try { + if (-not [string]::IsNullOrWhiteSpace($line)) { + $logs += $line | ConvertFrom-Json -ErrorAction Stop + } + } catch { + # Skip invalid JSON lines + } + } + + if ($logs.Count -gt 0) { + $formattedLogs = foreach ($log in $logs) { + $reason = $log.reasoning_path + if ($null -ne $reason -and $reason.Length -gt 60) { + $reason = $reason.Substring(0,57) + "..." + } + + [PSCustomObject]@{ + Time = [datetime]::Parse($log.timestamp).ToString("HH:mm:ss") + Span = if ($log.span_id) { $log.span_id } else { "N/A" } + Persona = if ($log.persona) { $log.persona } else { "Unknown" } + Action = if ($log.action_type) { $log.action_type } else { "Unknown" } + Tokens = if ($log.metrics.total_tokens) { $log.metrics.total_tokens } else { 0 } + Reasoning = $reason + } + } + $formattedLogs | Format-Table -AutoSize + } else { + Write-Host "No valid traces found yet." -ForegroundColor Yellow + } + } else { + Write-Host "Waiting for traces... ($traceFile not found)" -ForegroundColor Yellow + } + + Write-Host "`nPress Ctrl+C to exit. Refreshing every 2 seconds..." -ForegroundColor DarkGray + Start-Sleep -Seconds 2 +} diff --git a/dashboard/index.html b/dashboard/index.html new file mode 100644 index 0000000..f08ec95 --- /dev/null +++ b/dashboard/index.html @@ -0,0 +1,51 @@ + + + + + + Promptimprover Swarm Dashboard + + + +
+

Promptimprover Swarm Dashboard

+
+
+ + + + + + + + + + + + +
TimeSpanPersonaActionTokensReasoning
+
+ + + diff --git a/dashboard/server.js b/dashboard/server.js new file mode 100644 index 0000000..4e6d8c5 --- /dev/null +++ b/dashboard/server.js @@ -0,0 +1,62 @@ +// C:\PersonalRepo\portfolio\Promptimprover\dashboard\server.js +const http = require('http'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); +const { once } = require('events'); + +const TRACE_FILE = path.resolve('C:/PersonalRepo/.planning/traces.jsonl'); +const STATIC_DIR = path.join(__dirname); +const PORT = 3000; + +// Helper: read last N lines from trace file +async function readLastLines(filePath, maxLines = 20) { + if (!fs.existsSync(filePath)) return []; + const stream = fs.createReadStream(filePath, { encoding: 'utf8' }); + let data = ''; + stream.on('data', chunk => { data += chunk; }); + await once(stream, 'end'); + const lines = data.trim().split(/\r?\n/); + const recent = lines.slice(-maxLines); + // parse each JSON line safely + return recent.map(l => { + try { return JSON.parse(l); } catch (_) { return null; } + }).filter(Boolean); +} + +function serveStatic(res, filePath, contentType) { + fs.readFile(filePath, (err, content) => { + if (err) { + res.writeHead(404); + res.end('Not found'); + } else { + res.writeHead(200, { 'Content-Type': contentType }); + res.end(content); + } + }); +} + +const server = http.createServer(async (req, res) => { + const parsed = url.parse(req.url, true); + if (parsed.pathname === '/' || parsed.pathname === '/index.html') { + serveStatic(res, path.join(STATIC_DIR, 'index.html'), 'text/html'); + } else if (parsed.pathname === '/style.css') { + serveStatic(res, path.join(STATIC_DIR, 'style.css'), 'text/css'); + } else if (parsed.pathname === '/data') { + try { + const logs = await readLastLines(TRACE_FILE, 20); + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify(logs)); + } catch (e) { + res.writeHead(500); + res.end('Error reading trace file'); + } + } else { + res.writeHead(404); + res.end('Not found'); + } +}); + +server.listen(PORT, '127.0.0.1', () => { + console.log(`Promptimprover dashboard listening on http://127.0.0.1:${PORT}`); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..662fd35 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "name": "promptimprover", + "version": "0.1.0", + "scripts": { + "dashboard": "powershell -NoProfile -ExecutionPolicy Bypass -File ./run-dashboard.ps1", + "dashboard-web": "node dashboard/server.js" + } +} diff --git a/run-dashboard.ps1 b/run-dashboard.ps1 new file mode 100644 index 0000000..e9501e8 --- /dev/null +++ b/run-dashboard.ps1 @@ -0,0 +1,6 @@ +$script = Join-Path $PSScriptRoot "dashboard\View-SwarmDashboard.ps1" +if (Test-Path $script) { + powershell -NoProfile -ExecutionPolicy Bypass -File $script +} else { + Write-Host "Dashboard script not found at $script" -ForegroundColor Red +}