Skip to content

Latest commit

 

History

History
468 lines (331 loc) · 32.1 KB

File metadata and controls

468 lines (331 loc) · 32.1 KB

Letzte Verifikation: 2026-06-22 Geprüfte Dateien: 14 Projektstand: v0.1.0 (committed) Cursor Rule: .cursor/rules/cursor-usage-dashboard.mdc

Cursor Usage Dashboard — Feature-Referenz

Master-Referenz für AI-Chats. Abdeckung: Hub, Analytics (Thin-Shell), Shared-Module, Backend.


1. Quick-Lookup

Aufgabe Primäre Datei(en)
CSV/API-Parsing, Event-Modell, Dedupe static/cursor-analytics/parser.js
KPIs, Filter, Aggregationen, Granularität static/cursor-analytics/metrics.js
Chart-Rendering, Legend-Persistenz, Zoom, Marker-Annotationen static/cursor-analytics/charts.js
Projekt-Marker (CRUD, Statistik, Sync, Popover) static/cursor-analytics/markers.js
Analytics-UI, Live-Fetch, Toolbar, Budget cursor-usage-analytics.html (inline <script>)
Live-API, Static-Serving, Event-Cache serve.py
Multi-User-Konfiguration users-config.js, serve.py (USER_TOKENS), .env
Navigation Hub index.html
Server-Start / Live-Setup start.ps1, setup-live.ps1

2. Architektur-Überblick

Muster: Analytics Thin-Shell + optionaler Hub — ein Parser in parser.js, kein Fork.

flowchart LR
  subgraph entry [Einstiegspunkte]
    hub[index.html]
    analytics[cursor-usage-analytics.html]
  end
  subgraph shared [Shared Frontend]
    parser[parser.js]
    metrics[metrics.js]
    charts[charts.js]
    markers[markers.js]
  end
  subgraph backend [Backend]
    serve[serve.py]
    cursorAPI[cursor.com API]
  end
  hub --> analytics
  analytics --> parser
  analytics --> metrics
  analytics --> charts
  analytics --> markers
  analytics -->|Live CSV| serve
  analytics -->|CSV| data[data/]
  serve --> cursorAPI
Loading
Einstiegspunkt Muster Datenquellen Shared-Module
Analytics Thin-Shell CSV, Live (Proxy), Beides (Merge) Ja — window.CursorAnalytics
Hub Static HTML Nein
Backend Python http.server Proxy zu cursor.com

Kein Build-Step: Vanilla HTML/CSS/JS, Chart.js 4.4.7 + chartjs-plugin-zoom 2.2.0 + chartjs-plugin-annotation 3.1.0 + Hammer.js 2.0.8 (CDN, defer).


3. Dateistruktur & Ladereihenfolge

Projektbaum (relevant)

serve.py                          # Static + API-Proxy
index.html                        # Hub
cursor-usage-analytics.html       # Analytics Thin-Shell
static/cursor-analytics/
  parser.js                       # Event-Modell
  metrics.js                      # Aggregationen
  markers.js                      # Projekt-Marker
  charts.js                       # Chart.js-Rendering
data/                             # CSV-Exports + project-markers.json (gitignored)
.env                              # Session-Tokens (gitignored)

Analytics — Script-Ladereihenfolge

  1. Head (defer): Hammer.js → Chart.js → chartjs-plugin-zoom → chartjs-plugin-annotation
  2. DOMContentLoaded → initWhenReady(): wartet auf Chart (Polling 50 ms)
  3. **ensureModules():** sequentiell parser.js?v=15metrics.js?v=15markers.js?v=15charts.js?v=15
  4. **syncFromServer()** (Marker) → **initMarkerUi()****initToolbar()** + **loadDefaultCsvs()**

Cache-Busting: Query ?v=16 auf Modul-URLs.

Backend — Routen

Route Methode Zweck
/ GET Static → index.html
/health GET Token-Status, Port
/api/summary?user= GET Proxy → cursor.com/api/usage-summary
/api/events?user=&startDate=&endDate= GET Proxy → Events (paginiert, gecacht)
/api/events POST JSON-Body: { user, startDate?, endDate? }
/api/markers GET Projekt-Marker (data/project-markers.json), optional ?user=
/api/markers PUT Marker-Store speichern (JSON-Body { version, markers })
/* (Datei existiert) GET Static aus PROJECT_DIR

Event-Cache: In-Memory, TTL CURSOR_EVENTS_CACHE_TTL (Default 120 s), Key user:startDate:endDate.


4. Layout / Sections

Hub (index.html)

  • Ein <main> mit Link zu Analytics
  • Inline-CSS, keine JS-Abhängigkeiten

Analytics (cursor-usage-analytics.html)

Section ID / Selektor Inhalt
Toolbar #main-toolbar Datenquelle, User, CSV, Live, Export, Marker Export/Import
Projekt-Marker #marker-card, #marker-table-body, #marker-charts-section Intervall-Statistik, Breakdown-Charts (Projekt/Kategorie), optionaler Hover-Popover ([data-marker-table-popover])
Projekt-Filter #project-filter Filter für Einzelanfragen-Tabelle
Marker-Dialog #marker-modal, #marker-form CRUD (Von/Bis/Projekt/Aufgabe/Notiz)
Zeitraum / Anfragen #date-range-panel Modus-Umschalter (data-selection-mode), Zeitraum-Presets (#time-range-group), Anfragen-Presets (#count-range-group, data-count), Custom datetime
Granularität #granularity-select (nur Zeitraum-Modus) event / quarter / hour / day / week / month
Drop-Zone #drop-zone Drag-and-Drop CSV (wird nach Load versteckt)
KPIs #kpi-grid Dynamisch gerendert
Übersicht-Chart #overview-section, #chart-overview-daily Zeitraum: Buckets nach Granularität; Anfragen: Buckets pro Event (wie Granularität „Pro Anfrage“)
Detail-Charts #chart-top-cost, #chart-top-tokens, … 8 Canvas-Elemente
Tabellen #daily-table-body, #expensive-table-body, #events-table-body Tages-, Teuerste-, Einzelanfragen (Spalte Projekt); markierte Zeilen: data-marker-id → Marker-Popover beim Hover (abschaltbar)
Pagination #events-pagination 50 Events/Seite
Budget #budget-input, #budget-panel Monatsbudget USD

5. DOM-Hook-Register

Analytics — Toolbar & State

Hook Typ Verwendung
`[data-source="csv live merge"]`
`[data-user="all info slope"]`
`[data-selection-mode="time count"]` Button
[data-hours="N"] Button Zeitraum-Preset (Stunden), nur im Modus time
[data-all="true"] Button Modus „Alle Events“ (Zeitraum)
[data-count="N"] Button Letzte N Anfragen (10–1000), nur im Modus count
#count-from, #count-to, #count-custom-apply Number / Button Bereich nach Rang (1 = neueste Anfrage), Modus countRange
[data-count-all="true"] Button Alle Anfragen (Count-Modus)
#granularity-select <select> Aggregation für Overview + Cumulative (nur selectionMode=time)
[data-chart-key] Button Chart-Höhe 90 %, Zoom-Reset
#status-line <p> Haupt-Status
#load-hint <p> Lade-Details
#marker-add-overview / [data-marker-add] Button Marker-Dialog (aktuelle Datum/Uhrzeit als Start)
#marker-export-btn, #marker-import-input Button / File Marker JSON Export/Import
[data-marker-table-popover] Checkbox Tabellen-Hover-Popover ein/aus (3× synchron: Teuerste Events, Einzelanfragen, Projekt-Marker)
[data-marker-display-host] Container Chart-Marker-Steuerung (Anzeigen, Beschriftungen, Projekt-Filter)
[data-marker-chart-visible] Button Marker-Linien/Boxen in Charts ein/aus
[data-marker-labels-visible] Button Marker-Beschriftungen in Charts ein/aus
[data-marker-project-filter] <select> Marker in Charts nach Projekt filtern
#marker-chart-popover <div> Gemeinsamer Marker-Info-Popover (Charts + Tabellen)
#project-filter <select> Events-Tabelle nach Projekt filtern

Analytics — Chart-Canvas-IDs

Mapping in CHART_CANVAS_IDS (inline JS):

Key Canvas-ID charts.js-Key
overview chart-overview-daily renderOverviewBuckets / renderOverviewTimeline
topCost chart-top-cost topCost
topTokens chart-top-tokens topTokens
tokenTypes chart-token-types tokenTypes
modelFamily chart-model-family modelFamily
byHour chart-by-hour byHour
cumulative chart-cumulative renderCumulativeBuckets / renderCumulativeTimeline
inputOutput chart-input-output inputOutput
cacheEfficiency chart-cache cacheEfficiency
byWeekday chart-weekday byWeekday
markerByProject chart-marker-by-project markerByProject
markerByCategory chart-marker-by-category markerByCategory
maxMode chart-max-mode maxMode

Analytics-Event (parser.js): { timestamp, dayKey, userLabel, model, kind, … costCents, source }.


6. Override-/Patch-Verhalten

Keine Monkey-Patches. CSV-Parsing ausschließlich in parser.js (parseUsageEventsCsv, normalizeApiEvent).


7. CSS-Architektur

  • Kein gemeinsames Stylesheet — Design-Tokens in :root in cursor-usage-analytics.html
  • Tokens: --bg, --surface, --surface-2, --text, --muted, --accent, --warn, --danger, --border, --radius
  • Analytics-Klassen: .dashboard-grid, .kpi-grid, .drop-zone, .live-loading, .events-pagination, .marker-chart-popover
  • Dynamischer Zustand: .btn--active, .btn--loading, .drop-zone--hidden, .status-error, [aria-pressed="true"] auf Chart-Höhe-Buttons

8. Initialisierungsfluss

Analytics

DOMContentLoaded
  → initWhenReady (poll Chart.js)
    → ensureModules (parser → metrics → markers → charts)
      → syncFromServer (Marker)
      → initMarkerUi + initToolbar
        → loadDefaultCsvs (fetch ./data/*.csv)
          → renderAll
            → filteredEvents → KPIs, Tabellen, charts.renderAll

Live-Pfad bei dataSource === 'live'|'merge':

applyRangeAndRender / live-refresh
  → fetchLiveEvents
    → GET /api/events?user=…&startDate=&endDate=
    → normalizeApiEvent (parser.js)
    → mergeEvents (bei incremental/beides)
    → renderAll

Client-Cache: liveFetchState (5 Min TTL), incremental overlap 5 Min.

Backend

python serve.py
  → load_dotenv(.env)
  → ThreadingHTTPServer(CURSOR_WEB_HOST:CURSOR_WEB_PORT)
  → CursorUsageHandler (GET/POST/PUT/OPTIONS)

Marker-Sync: GET/PUT /api/markersdata/project-markers.json (atomisches Schreiben). Client: localStorage + Server-Merge bei Start; Server gewinnt bei gleicher id und neuerem updatedAt.


9. Datenmodell & APIs

Projekt-Marker (markers.js)

Marker sind keine CSV-Daten — manuell gesetzte Metadaten zu Zeitintervallen.

{
  "version": 1,
  "markers": [
    {
      "id": "m-uuid",
      "user": "info",
      "start": "2026-06-20T14:30:00.000Z",
      "end": null,
      "project": "Cursor-Usage-Dashboard",
      "task": "REFERENCE.md",
      "note": "",
      "createdAt": "...",
      "updatedAt": "..."
    }
  ]
}
  • **end: null:** Intervall [start, nächster Marker) oder bis Filter-Ende (in UI mit * gekennzeichnet).
  • **user:** info | slope | all
  • Statistik (computeStats): Events im Intervall — nicht persistiert.

Chart-Annotationen: Overview + Cumulative nutzen Kategorie-Achse → Bucket-Index-Mapping via sortKey. Hover auf Annotationen öffnet #marker-chart-popover (showChartPopover). Timeline-Charts ergänzen den Chart.js-Tooltip um Projekt, Aufgabe und Notiz (markerTooltipLines in charts.js).

Marker-Info-Popover (Charts & Tabellen)

Gemeinsame UI-Komponente in markers.js (buildPopoverHtml, #marker-chart-popover):

Kontext Auslöser API
Charts Hover/Klick auf Marker-Annotation (Overview, Cumulative, Timeline) showChartPopover()
Tabellen Hover auf tr[data-marker-id] in #expensive-table-body, #events-table-body, #marker-table-body showTableMarkerPopover() via mountMarkerTableHover() (Analytics-HTML)

Popover-Inhalt: Projekt, Aufgabe, Benutzer, Von/Bis, Notiz (falls gesetzt), Intervall-Statistik (Events, Tokens, Kosten), Button „Bearbeiten“ → #marker-modal.

Tabellen abschalten: Checkbox [data-marker-table-popover] (i18n: showTableMarkerPopover) — drei synchronisierte Instanzen in den Toolbars von Teuerste Events, Einzelne Anfragen und Projekt-Marker. Persistenz: cursor-marker-chart-display → Feld showTablePopover (Default true). Bei Deaktivierung wird ein sichtbarer Popover sofort geschlossen.

Chart-Marker-Anzeige: Buttons [data-marker-chart-visible], [data-marker-labels-visible], Select [data-marker-project-filter] — ebenfalls in cursor-marker-chart-display (showMarkers, showLabels, projectFilter).

Gotcha Granularität: Bei Wechsel der Granularität verschieben sich Bucket-Grenzen — Marker-Positionen in Overview/Cumulative (Zeitraum-Modus mit grober Granularität) sind Näherungen. Marker-Boxen nutzen bucketIndexRangeForInterval (Überlappung von Intervall und sichtbaren Buckets; bei Pro-Anfrage-Buckets optional User-Filter).

Anfragen-Modus: filterEventsByCount liefert die neuesten N Events oder einen Von–Bis-Bereich (countRange, 1 = neueste). Live-Fetch nutzt heuristische Zeitfenster nach Anzahl (nicht mehr pauschal „gesamter Verlauf“), nur User mit Token (/health). Beim Wechsel zurück zu Zeitraum wird der Vollcache invalidiert.

Marker-Konvention (empfohlen)

Marker sind manuell — ohne einheitliche Benennung lassen sich Projekt- und Kategorie-Charts schwer vergleichen. Empfohlenes Schema:

Feld Bedeutung Beispiele
project Repo, Modul, Cursor-Modus oder Projektphase Cursor-Usage-Dashboard, Grow-Tagebuch/API, Agent, Editor
task Kategorie + Kurzbeschreibung (Kategorie: Aufgabe) Feature: Marker-Charts, Bugfix: Login-Timeout, Analyse: fullEntryService.js
note Freitext, Scope-Hinweise, Effizienz nur 2 Dateien, gesamtes Projekt
start / end Arbeitsintervall Bei Task-Start setzen, bei Task-Ende end setzen oder nächsten Marker starten

Kategorie-Prefix in task: Parser parseTaskCategory() erkennt Präfixe vor :, -, oder . Empfohlene Werte: Bugfix, Feature, Refactoring, Analyse, Dokumentation, Suche. Ohne Prefix → Gruppe „Ohne Kategorie“ in den Charts.

Modus (Agent vs. Editor): Cursor exportiert kein Agent/Editor-Feld in Events. Modus als project markieren (z. B. Agent, Editor) oder in note festhalten — dann über Marker-Statistik und Projekt-Charts auswertbar.

Workflow: Vor größeren Aufgaben Marker setzen (Marker setzen / Overview-Chart), nach Abschluss end setzen. 2–3 Wochen konsequent → belastbare Vergleiche (Modus, Kategorie, Projektphase).

Charts: aggregateEventsByMarkerDimension() gruppiert gefilterte Events per getMarkerForEvent (keine Doppelzählung bei überlappenden Intervallen). UI: Doughnut/Bar „Tokens & Kosten nach Projekt/Kategorie“ in #marker-charts-section.

Normalisiertes Event (Analytics)

Siehe parser.jsnormalizeEvent(). Wichtige Felder: timestamp, userLabel, model, kind, maxMode, totalTokens, costCents, isIncluded, source (csv|api).

maxMode: CSV-Spalte Max Mode (Yes/No); Live-API → Boolean. UI: Filter in Einzelanfragen, Chart „Max Mode“ in Detail-Charts (aggregateByMaxMode in metrics.js).

Kostenberechnung (Analytics)

Grundprinzip: Das Dashboard berechnet keine Kosten aus Token-Mengen und Modell-Preisen. Jede Anfrage erhält ein costCents-Feld aus der Cursor-Quelle (CSV-Spalte Cost oder Live-API). KPIs, Charts, Budget und Marker-Statistik summieren diese Werte — sie leiten sie nicht neu ab.

Ohne Gewähr: Alle angezeigten Kosten, Summen, Budget-Vergleiche und Prognosen sind rein informativ und ohne Gewähr. Sie sind keine offizielle Abrechnung von Cursor, können von der tatsächlichen Rechnung abweichen und dienen nicht steuerlichen oder vertraglichen Zwecken. Abweichungen sind u. a. möglich durch: veraltete oder unvollständige CSV-Exports, Parse-Fehler, API-Änderungen, Merge/Dedupe, Included-/Chargeable-Logik, Rundung, unvollständige Live-Daten oder die Monats-Prognose-Heuristik (Ø/Tag × 30). Maßgeblich ist ausschließlich die Abrechnung im Cursor-Dashboard bzw. bei Cursor.

Pro Event: costCents (parser.js)

Implementierung: parseCostCents() (CSV + API-Fallback) und normalizeApiEvent().

Quelle Eingabe Regel
CSV Spalte Cost (optional), Kind Siehe Tabelle unten
Live-API 1. chargedCents → 2. tokenUsage.totalCents → 3. usageBasedCosts (String wie CSV) Gerundet auf ganze Cent; costDisplay aus API-String oder $X.XX

**parseCostCents(costRaw, kindRaw) — Text → Cent:**

Cost / Kind costCents isIncluded Anzeige
leer, Included, Kind included 0 ja Original oder „Included“
No charge, Errored 0 nein Originaltext
Dollar-String (z. B. $0.04, 0,04) Math.round(USD × 100) nein Originaltext
nicht parsebar 0 nein Originaltext

API-Zusatz: isIncluded auch wenn costCents === 0 und Kind INCLUDED enthält oder !isChargeable. isChargeable = API-Flag oder costCents > 0.

Anzeige in „Einzelne Anfragen“: Bei Included → costDisplay (nicht $0.00); sonst USD-Format aus costCents / 100 (formatEventCost in Analytics-HTML).

Aggregation (Summen)

Alle folgenden Werte nutzen dieselbe Event-Liste nach Toolbar-Filter (Datenquelle, User, Zeitraum/Anfragen-Modus), sofern nicht anders vermerkt:

UI / Metrik Berechnung Besonderheit
KPI Gesamtkosten sum(costCents) Included = 0 $
KPI Ø/Tag, Prognose/Monat Kosten ÷ Tage im Filter × 30 Heuristik, kein Abrechnungswert von Cursor
Charts (Overview, Top-Kosten, kumuliert, …) Bucket-/Modell-Summen aus costCents in metrics.js / charts.js
Budget (aktueller Monat) sum(costCents) aller Events ab Monatsanfang Ignoriert Zeitraum-Toolbar; nutzt geladene Events (CSV + ggf. Live/Merge)
Projekt-Marker sum(costCents) im Intervall [start, end) in markers.jscomputeStats
Marker-Charts (Projekt/Kategorie) Summe Tokens/Kosten pro Event-Marker-Zuordnung markers.jsaggregateEventsByMarkerDimension
Max-Mode-Chart Summe nach maxMode Yes/No metrics.jsaggregateByMaxMode
Filter Min. Kosten ($) Events mit costCents ≥ eingegebener USD × 100 Nur Tabellenfilter, ändert keine Berechnung

Konfigurierbare Werte (keine Preisliste)

Einstellung Speicher Wirkung
Monatsbudget ($) localStorage cursor-analytics-monthly-budget-usd (Default 70) Vergleich „Ausgaben Monat vs. Budget“ — kein Faktor für costCents pro Event
Min. Kosten ($) Session (Input #events-min-cost) Filter in Einzelanfragen-Tabelle

Nicht unterstützt (Stand v0.1): Eigene $/Token-Raten, manuelle Kosten pro Modell, Schätzung fehlender CSV-Cost-Spalten aus Token-Zahlen, Umrechnung Währung. Fehlt Cost in CSV → costCents = 0 (Tokens werden trotzdem gezählt).

Datenquelle CSV vs. Live vs. Beides

  • CSV: Kosten wie im Export von Cursor.
  • Live: Kosten wie von der inoffiziellen API geliefert (chargedCents / usageBasedCosts).
  • Beides (Merge): parser.mergeEvents dedupliziert per eventDedupeKey; bei gleichem Key gewinnt der spätere Eintrag in der Liste — im Modus Beides typischerweise Live vor CSV (mergeEvents([allCsvEvents(), allLiveEvents()])). Keine Mittelung oder Neuberechnung.

CSV-Spalten (Analytics)

Pflicht: Date, Input (w/o Cache Write), Cache Read, Output Tokens, Total Tokens. Optional: Kind, Model, Max Mode, Input (w/ Cache Write), Cost.

Live-API (inoffiziell)

Upstream: https://cursor.com/api/usage-summary, https://cursor.com/api/dashboard/get-filtered-usage-events. Auth: Cookie WorkosCursorSessionToken.

localStorage-Keys

Key Default Zweck
cursor-analytics-monthly-budget-usd 70 Monatsbudget (Analytics)
cursor-analytics-granularity hour Overview-Aggregation (Zeitraum-Modus)
cursor-analytics-selection-mode time time = Zeitraum-Filter, count = letzte N Anfragen
cursor-analytics-count 50 Default-Anzahl im Anfragen-Modus
cursor-analytics-time-range hours / 24 h Aktiver Zeitraum-Filter (mode, hours, optional customFrom/customTo)
cursor-analytics-custom-range heute−2d / heute Von/Bis-Zeitraum (Analytics, JSON { customFrom, customTo })
cursor-analytics-chart-visibility {} Legend-Sichtbarkeit pro Chart-Key
cursor-marker-chart-display siehe unten Chart-Marker-Sichtbarkeit + Tabellen-Hover-Popover
cursor-usage-markers-v1 { version: 1, markers: [] } Projekt-Marker (Primary Client-Cache)
cursor-event-chart-markers-v1 Legacy-Key (Migration → cursor-usage-markers-v1)

cursor-marker-chart-display (JSON): { showMarkers: true, showLabels: true, projectFilter: 'all', showTablePopover: true } — geladen via loadMarkerChartDisplay() / saveMarkerChartDisplay() in markers.js.

Server-Datei: data/project-markers.json (liegt unter gitignored data/).


10. Impact-Checkliste bei Shared-Änderungen

Änderung an … Prüfen auch …
CSV-Spalten / Parser-Logik parser.js
User-IDs / CSV-Pfade users-config.js, serve.py USER_TOKENS, .env, config/users.example.json
Demo-Daten / Samples scripts/generate_demo_data.py, samples/, Marker-Seed in serve.py
Design-Tokens / Theme cursor-usage-analytics.html (:root)
Chart.js-Version (CDN) cursor-usage-analytics.html Head
API-Response-Format parser.js normalizeApiEvent, serve.py Proxy
serve.py-Routen Analytics fetch() (PROXY_BASE = ''), Marker /api/markers
Marker-Schema / Sync markers.js, serve.py, cursor-usage-analytics.html
Marker-Popover / Tabellen-Hover markers.js, charts.js (markerTooltipLines), i18n.js, cursor-usage-analytics.html

11. Bekannte Einschränkungen

  • Kosten in Analytics: übernommen/summiert aus CSV/API — ohne Gewähr, keine offizielle Abrechnung (siehe §9 Kostenberechnung)
  • Kein Prompt-Text in CSV/API — „Top teuerste Prompts“ nur als Event-Liste, nicht semantisch
  • Keine Git-/Commit-Metriken (Tokens pro Commit, Tokens pro geänderter Datei) — würde Cursor-Hooks, Git-Integration oder manuelle Effizienz-Felder am Marker erfordern; bewusst nicht im Scope v0.1
  • Agent/Editor/Auto als Modus nur über Marker-Konvention, nicht automatisch aus Events
  • Enterprise Admin API nicht implementiert
  • Anfragen-Modus: Pro-Anfrage-Bucket-Charts wie Zeitraum + Granularität event
  • Sehr große Event-Mengen → Browser-Performance
  • Session-Tokens laufen ab (401 → Hinweis in serve.py)
  • file://-Öffnung: Modul-Laden und CSV-Fetch schlagen fehl → python serve.py nötig

Verwandte Dokumentation

  • Setup: [README.md](../README.md)
  • Entwickler-Referenz: [docs/REFERENCE.md](REFERENCE.md)
  • Cursor Rule (Scope): [.cursor/rules/cursor-usage-dashboard.mdc](../.cursor/rules/cursor-usage-dashboard.mdc)