Install
openclaw skills install @jonathanjing/token-ledgerAudit-grade token and cost ledger for OpenClaw. Use when you need to (1) record every model call's usage (input/output/cache read/cache write/cost) into SQLite, (2) install/manage the ledger watcher LaunchAgent, (3) query ledger.db for daily usage/cost, fixed overhead, or historical billing reconciliation, or (4) generate low-token financial reports from SQL.
openclaw skills install @jonathanjing/token-ledgerInstall with:
openclaw skills install @jonathanjing/token-ledger
~/.openclaw/ledger.db with per-call usage rowsinput_tokens: uncached input tokens for the callcache_write_tokens: tokens written to cachecache_read_tokens: tokens read from cacheoutput_tokens: generated tokensinput_tokens + cache_write_tokens + cache_read_tokens| 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 |
python3 "{baseDir}/scripts/ledger_watcher.py" --once
python3 "{baseDir}/scripts/ledger_watcher.py" --backfill
python3 "{baseDir}/scripts/ledger_watcher.py" --sync-spark
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
launchctl unload ~/Library/LaunchAgents/com.openclaw.token-ledger-watcher.plist
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;"
Preferred flow:
ledger.dbcalls for the specific session/modelFor daily reports, use:
.jsonl.deleted* filesspark-token-ledger.jsonl. API Hub provides /spark/token-log endpoint for the watcher to pull (unidirectional sync).provider/model format. Local Spark models are normalized to qwen-spark-35b or qwen-spark-27b.Use the bundled scripts as the source of truth; do not assume a separate workspace-specific reference file exists.
OpenClaw Sessions Cron Jobs DGX Spark
↓ ↓ ↓
*.jsonl cron/runs/*.jsonl spark-token-ledger.jsonl
↓ ↓ ↓
└────────────────────┬──────────────────────────┘
↓
ledger_watcher.py
↓
ledger.db (SQLite)
↓
SQL Queries / Reports
calls - Per-call usage recordsturns - Aggregated turn-level metrics (60s window)price_versions - Historical pricing for audit| 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 |
# 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;"
}
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.