<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[CoraCode]]></title><description><![CDATA[Building things that work — products, tools, and open source experiments]]></description><link>https://blog.coracode.co.uk</link><image><url>https://cdn.hashnode.com/uploads/logos/62a0956b735319718350b514/a14839aa-de4f-4a67-a7a2-c2a6dfd28e61.png</url><title>CoraCode</title><link>https://blog.coracode.co.uk</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 15 May 2026 07:53:38 GMT</lastBuildDate><atom:link href="https://blog.coracode.co.uk/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How We Built the OpenClaw Session Viewer]]></title><description><![CDATA[OpenClaw agent sessions are stored as JSONL files — one JSON object per line, one file per session. This is great for reliability and append-only logging. It's terrible for reading.

A single active s]]></description><link>https://blog.coracode.co.uk/how-we-built-the-openclaw-session-viewer</link><guid isPermaLink="true">https://blog.coracode.co.uk/how-we-built-the-openclaw-session-viewer</guid><dc:creator><![CDATA[Dave K]]></dc:creator><pubDate>Wed, 13 May 2026 15:13:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/62a0956b735319718350b514/69290ee3-4261-4943-8728-5125f78aaf0c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>OpenClaw agent sessions are stored as JSONL files — one JSON object per line, one file per session. This is great for reliability and append-only logging. It's terrible for reading.</p>
</blockquote>
<p>A single active session can easily grow to 36MB+. Open that in a browser and your tab crashes. Open it in VS Code and the whole editor stutters. <code>grep</code> works, but it's not great when you want to understand a conversation flow, see what tools were called, or trace a chain of reasoning.</p>
<p>We needed a way to browse session logs like a conversation — not a log file.</p>
<h2>The Approach</h2>
<p>The obvious solution would be to load the file and render it in the browser. But that defeats the purpose — the browser still has to parse 36MB of JSON. So we went the other way:</p>
<p><strong>Server-side parsing.</strong> A Flask app reads the JSONL files on disk, extracts the conversation — user messages, assistant responses, tool calls, tool results — and returns paginated chunks of 100 messages at a time. The browser only ever sees what fits on one screen.</p>
<p>This means even the biggest session files load instantly. No loading spinner, no memory warning, just a conversation.</p>
<h2>Architecture</h2>
<p>The app is deliberately small — about 400 lines of Python, no database, no build step.</p>
<p>It reads from your OpenClaw agents directory, parses JSONL files, and renders them. That's it. No external API calls, no data leaving your machine.</p>
<h2>Key Design Decisions</h2>
<p><strong>Why Flask?</strong> Because the app is simple enough that a framework would add more complexity than it solves. Flask gives us routing, templating, and a test client. Everything else is just Python.</p>
<p><strong>Why Jinja2 templates instead of a SPA?</strong> Two reasons. First, the app works with JavaScript disabled — it's just HTML with pagination links. Second, server-side rendering means the initial page load is instant, no JS bundle to download.</p>
<p><strong>Why no database?</strong> The session files already <em>are</em> the database. They're append-only, sorted by time, and each one is self-contained. Adding a database would just duplicate data and add complexity.</p>
<h2>Features We're Proud Of</h2>
<p><strong>Copy to clipboard.</strong> Click the 📋 button on any message to copy its content. Useful for grabbing prompts, debugging tool calls, or sharing snippets.</p>
<p><strong>Expandable tool calls.</strong> Long JSON arguments are truncated by default with a "Show full" toggle. Tool results work the same way. Keeps the conversation readable without hiding important details.</p>
<p><strong>Pagination with page numbers.</strong> Not just prev/next. You get "1 2 3 … 20 21" so you can jump to any page.</p>
<p><strong>Prometheus metrics.</strong> The <code>/metrics</code> endpoint exposes session counts, message totals, and tool call stats per agent. We use this in our Grafana dashboards.</p>
<h2>CI/CD and Quality</h2>
<p>Every change goes through GitHub Actions:</p>
<ul>
<li><p><strong>Lint</strong> — flake8 catches syntax errors and style issues</p>
</li>
<li><p><strong>Tests</strong> — 10 pytest tests covering routes, agent stats, metrics, and caching</p>
</li>
<li><p><strong>Releases</strong> — push a <code>v*</code> tag and GitHub automatically creates a release with changelog</p>
</li>
</ul>
<p>Local development is one command: <code>make check</code> runs lint and tests together.</p>
<h2>What's Next</h2>
<p>Some ideas we're considering:</p>
<ul>
<li><p>Search within a session (Ctrl+F but server-side)</p>
</li>
<li><p>Side-by-side session comparison</p>
</li>
<li><p>Export a session as a readable transcript (PDF or markdown)</p>
</li>
<li><p>Docker Compose setup for easy deployment</p>
</li>
</ul>
<h2>Try It</h2>
<p>If you're an OpenClaw user and this sounds useful, give it a try:</p>
<p><a href="https://github.com/CoraCodeDev/oc-session-viewer"><strong>github.com/CoraCodeDev/oc-session-viewer</strong></a></p>
<p>MIT licensed, runs on any Python 3.10+ host. Pull requests welcome.</p>
<hr />
<p><em>Built by</em> <a href="https://coracode.co.uk"><em>CoraCode</em></a><em>.</em></p>
]]></content:encoded></item></channel></rss>