toknmtr — self-hostable Claude Code usage & analytics dashboard

Server (SvelteKit + SQLite Docker container) + per-machine agent that parses
Claude Code JSONL transcripts. docker-compose one-command deploy; raw transcript
view + search gated behind SHOW_TRANSCRIPTS (hidden by default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-07-08 19:33:55 +01:00
commit 71a60ab054
74 changed files with 15613 additions and 0 deletions

66
agent/cursor.ts Normal file
View file

@ -0,0 +1,66 @@
/**
* toknmtr agent per-file byte-offset cursor.
*
* Persists, per transcript file, the byte offset up to which we've already parsed and
* pushed events, so re-runs only emit NEW bytes appended since last time. Stored as a
* single JSON file at ~/.toknmtr/cursors.json (one process at a time is assumed there's
* no file locking).
*
* Handles truncation/rotation: if a file's current size is smaller than the recorded
* offset, the file was truncated or replaced (e.g. a session id got reused, or the file
* was edited out from under us) getOffset() resets to 0 so the whole file is reparsed.
*/
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
export const CURSOR_FILE = join(homedir(), '.toknmtr', 'cursors.json');
interface CursorEntry {
offset: number;
size: number;
}
type CursorMap = Record<string, CursorEntry>;
let cache: CursorMap | null = null;
function load(): CursorMap {
if (cache) return cache;
try {
const parsed = JSON.parse(readFileSync(CURSOR_FILE, 'utf8')) as unknown;
cache = parsed && typeof parsed === 'object' ? (parsed as CursorMap) : {};
} catch {
cache = {};
}
return cache;
}
/** Persist the in-memory cursor map to disk. Call once after a batch of setOffset() calls. */
export function save(): void {
const map = load();
mkdirSync(dirname(CURSOR_FILE), { recursive: true });
writeFileSync(CURSOR_FILE, JSON.stringify(map, null, 2));
}
/**
* Byte offset to resume reading `filePath` from, given its current size on disk.
* Returns 0 (full reparse) if there's no recorded cursor, or if the file shrank since
* last run (truncated/rotated).
*/
export function getOffset(filePath: string, currentSize: number): number {
const entry = load()[filePath];
if (!entry) return 0;
if (entry.offset > currentSize) return 0;
return entry.offset;
}
/** Record the new offset/size for `filePath` in memory. Call save() to persist. */
export function setOffset(filePath: string, offset: number, size: number): void {
load()[filePath] = { offset, size };
}
/** Forget a file's cursor entirely (forces a full reparse on next run). */
export function resetOffset(filePath: string): void {
delete load()[filePath];
}