Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.
Documents
token-ledger
Try itAudit-grade SQLite ledger that records every model call's token usage and cost from OpenClaw sessions.
What it does
Records per-call usage (input, output, cache read/write, cost) into `~/.openclaw/ledger.db` from OpenClaw session JSONL files, cron runs, and DGX Spark local inference logs. Includes a watcher daemon installable as a macOS LaunchAgent, plus Spark token sync via API Hub. Supports low-token SQL-first finance reports with preset daily queries and source_kind breakdowns (interactive, cron, subagent, spark). Versioned pricing stored in `price_versions` table; deleted threads handled via `.jsonl.deleted*` detection.
When to use it
- Recording per-call token usage and cost into a SQLite ledger
- Installing the watcher daemon as a macOS LaunchAgent
- Querying daily cost, cached vs uncached mix, or Spark local usage
- Backfilling source_kind metadata onto existing records
The skill document
Token Ledger (SQLite)
Install with:
openclaw skills install @jonathanjing/token-ledger
What this skill provides
- A SQLite ledger at
~/.openclaw/ledger.dbwith per-call usage rows - A watcher daemon that tails OpenClaw session JSONL files and writes usage into SQLite (near-real-time)
- Spark token sync - pulls DGX Spark local inference logs via API Hub
- Deterministic, low-token SQL-first finance reports (no JSONL rescans)
Canonical usage definitions
input_tokens: uncached input tokens for the callcache_write_tokens: tokens written to cachecache_read_tokens: tokens read from cacheoutput_tokens: generated tokens- total_context_tokens =
input_tokens + cache_write_tokens + cache_read_tokens
Files & paths
| File | Path |
|---|---|
| SQLite DB | ~/.openclaw/ledger.db |
| Checkpoint | ~/.openclaw/ledger-checkpoint.json |
| Spark Checkpoint | ~/.openclaw/ledger-spark-checkpoint.json |
| Sessions JSONL | ~/.openclaw/agents/main/sessions/*.jsonl |
| Cron Runs | ~/.openclaw/cron/runs/**/*.jsonl |
| Spark Token Log (NFS) | ~/spark-nfs/.spark/token-ledger.jsonl |
Standard operations
One-shot backfill (safe)
python3 "{baseDir}/scripts/ledger_watcher.py" --once
Backfill source_kind for existing records
python3 "{baseDir}/scripts/ledger_watcher.py" --backfill
Sync Spark tokens only
python3 "{baseDir}/scripts/ledger_watcher.py" --sync-spark
Install / start daemon (macOS LaunchAgent)
python3 "{baseDir}/scripts/render_plist.py" \
> ~/Library/LaunchAgents/com.openclaw.token-ledger-watcher.plist
launchctl load ~/Library/LaunchAgents/com.openclaw.token-ledger-watcher.plist
launchctl list | rg token-ledger-watcher
Stop daemon
launchctl unload ~/Library/LaunchAgents/com.openclaw.token-ledger-watcher.plist
Quick sanity query
sqlite3 ~/.openclaw/ledger.db \
"SELECT provider, model, COUNT(*) calls, ROUND(SUM(cost_total),4) cost FROM calls WHERE ts >= date('now') GROUP BY 1,2 ORDER BY cost DESC LIMIT 20;"
How to build low-token Finance reports
Preferred flow:
- Run SQL queries directly against
ledger.db - Format results with a deterministic template (no long reasoning)
- Only if numbers look anomalous: drill into
callsfor the specific session/model
For daily reports, use:
- per-model totals
- cached vs uncached mix
- top sessions by cost
- source_kind breakdown (interactive | cron | spark)
Notes / caveats
- Provider billing can still exceed ledger totals due to retries/timeouts/streaming interruptions. Ledger is auditable, not magical.
- Keep pricing versioned. Do not retroactively reprice historical calls unless explicitly requested.
- Deleted threads: Watcher handles
.jsonl.deleted*files - Spark local tokens: Spark local calls are logged via
spark-token-ledger.jsonl. API Hub provides/spark/token-logendpoint for the watcher to pull (unidirectional sync). - Model normalization: Cloud models (Claude/GPT/Gemini) are normalized from
provider/modelformat. Local Spark models are normalized toqwen-spark-35borqwen-spark-27b.
Architecture
Use the bundled scripts as the source of truth; do not assume a separate workspace-specific reference file exists.
Data Flow
OpenClaw Sessions Cron Jobs DGX Spark
↓ ↓ ↓
*.jsonl cron/runs/*.jsonl spark-token-ledger.jsonl
↓ ↓ ↓
└────────────────────┬──────────────────────────┘
↓
ledger_watcher.py
↓
ledger.db (SQLite)
↓
SQL Queries / Reports
Key Tables
calls- Per-call usage recordsturns- Aggregated turn-level metrics (60s window)price_versions- Historical pricing for audit
Source Kind Detection
| Source | Detection |
|---|---|
| Interactive | Default, or from sessions.json |
| Cron | Path contains cron/runs OR session_key contains :cron: |
| Subagent | Session key contains :subagent: or :run: |
| Spark | Records from Spark token log sync |
Preset queries
# Today
today() {
sqlite3 ~/.openclaw/ledger.db "SELECT provider, model, COUNT(*) calls, SUM(input_tokens) input, SUM(output_tokens) output, ROUND(SUM(cost_total),4) cost FROM calls WHERE ts >= date('now') GROUP BY 1,2 ORDER BY cost DESC;"
}
# By source kind
today_by_source() {
sqlite3 ~/.openclaw/ledger.db "SELECT source_kind, COUNT(*) calls, ROUND(SUM(cost_total),4) cost FROM calls WHERE ts >= date('now') GROUP BY 1;"
}
# Spark usage (local models)
spark_usage() {
sqlite3 ~/.openclaw/ledger.db "SELECT model, COUNT(*) calls, SUM(input_tokens + output_tokens) tokens FROM calls WHERE provider = 'local-dgx-spark' GROUP BY model;"
}
Pricing integrity
Use the versioned price_versions table. The watcher persists every bundled
rate, its effective timestamp, and the provider pricing-page URL when the
database opens. Provider pricing changes over time; do not copy a current price
table into reports without recording its effective date and source.
Questions people ask
- Where is the SQLite database stored?
- The default database path is `~/.openclaw/ledger.db`, with checkpoints at `~/.openclaw/ledger-checkpoint.json` and `~/.openclaw/ledger-spark-checkpoint.json`.
- Which token fields are recorded per call?
- The `calls` table records `input_tokens` (uncached), `cache_write_tokens`, `cache_read_tokens`, `output_tokens`, and a `cost_total` derived from the versioned `price_versions` table.
- Does it automatically sync DGX Spark local inference usage?
- Yes. The watcher pulls `~/spark-nfs/.spark/token-ledger.jsonl` via the API Hub `/spark/token-log` endpoint, and writes those rows with `source_kind = spark` and `provider = local-dgx-spark`.
Related skills
Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.
Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.
Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.
Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.
Write, debug, and tune Playwright specs with locator strategy, trace diagnosis, and CI-aware timeouts.
More from jonathanjing
Browse all skillsOperate and extend the read-only OpenClaw dashboard backend, frontend tabs, and security model.
Monitor OpenClaw gateway health and report incidents to Discord with read-only checks and cooldown dedupe.
Multi-stage deep intelligence pipeline (Search → Filter → Fetch → Synthesize). Turns a query into a structured research report with full source citations.
Route user-selected photos from Meta Ray-Ban glasses or another camera through a configured messaging ingress to an approved specialist and destination. Use only for explicit, opt-in photo routing with allowlisted destinations and confirmation before cross-session forwarding or persistent writes.
Audit a named ClawHub skill or skill URL before installation by combining OpenClaw verification with bounded static analysis. Use when the user explicitly asks whether a skill is safe or requests a pre-install review; report evidence and uncertainty instead of treating a score as proof.
Develop, test, or integrate the OpenClaw Tally Node.js library for task-level cost, complexity, and efficiency analytics. Use when working on Tally's detector, SQLite ledger, analytics engine, or an explicit plugin/hook integration; installing this skill alone does not register hooks or slash commands.