{"id":"0ab2c830-ef7f-4363-933b-8f85bbf7bafc","entityType":"agent","slug":"clawhub-joeyiptk-llm-cost-tracker-openrouter-api","name":"LLM Cost Tracker","canonicalUrl":"https://www.xpersona.co/agent/clawhub-joeyiptk-llm-cost-tracker-openrouter-api","canonicalPath":"/agent/clawhub-joeyiptk-llm-cost-tracker-openrouter-api","generatedAt":"2026-10-12T12:01:31.444Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":null},"description":"Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cos...","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 964 downloads reported by the source. Last updated 10/12/2026.","installCommand":"clawhub skill install s17av7k4d5aq1pr3yswt5phdg985p5pm:llm-cost-tracker-openrouter-api","sourceUrl":"https://clawhub.ai/joeyiptk/llm-cost-tracker-openrouter-api","homepage":"https://clawhub.ai/joeyiptk/skills/llm-cost-tracker-openrouter-api","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/joeyiptk/llm-cost-tracker-openrouter-api","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/joeyiptk/skills/llm-cost-tracker-openrouter-api","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"LLM Cost Tracker technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":null},"stars":null,"forks":null,"downloads":964,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.1.2","tractionLabel":"964 downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:58:47.056Z","emptyReason":null},"lastUpdatedAt":"2026-10-12T00:58:47.533Z","lastCrawledAt":"2026-10-12T00:58:47.056Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-13T00:58:47.056Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.2","createdAt":"2026-05-28T16:05:37.744Z","changelog":"## 1.1.2 (2026-05-28) - Added `--days` flag to `collect_usage.py` to allow multi-day collection. - Fixed HKT/UTC logic gap by using a 48-hour window in scheduled runs. - Improved collection reliability for rolling 24h reports. - Excluded local `usage.db` from snapshots (C7 fix).","fileCount":13,"zipByteSize":18598},{"version":"1.1.1","createdAt":"2026-04-30T03:10:57.164Z","changelog":"## 1.1.1 (2026-04-30) - Added: scripts/setup.py - Updated: SKILL.md, version.txt ### Changed - Clarified billing rules: use `usage.cost.total` as canonical source, not token-count math - Split token categories: prompt, cached (cacheRead), cache_write, completion, reasoning, total - Updated cron examples in SKILL.md with `--no-deliver` flag for collect cron - Removed \"forward output exactly\" requirement from report triggers","fileCount":13,"zipByteSize":18408},{"version":"1.0.0","createdAt":"2026-04-28T04:40:49.684Z","changelog":"## 1.0.0 — Initial Release - Track LLM token usage and billed cost from OpenRouter API - Report time windows: rolling 24h, 7d, 30d, 90d, 365d - Per-model breakdown with top-3 rankings - Append-only SQLite DB (request_facts table) - Auto-detects sessions directory on any machine - Optional daily cron: midnight collection + 9 AM report to Telegram - DB health check, schema validation, prune/cleanup tools ───","fileCount":12,"zipByteSize":26607}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17av7k4d5aq1pr3yswt5phdg985p5pm:llm-cost-tracker-openrouter-api","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17av7k4d5aq1pr3yswt5phdg985p5pm:llm-cost-tracker-openrouter-api` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/joeyiptk/llm-cost-tracker-openrouter-api before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-12T12:01:31.443Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-joeyiptk-llm-cost-tracker-openrouter-api/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":null},"readme":"Skill: LLM Cost Tracker\n\nOwner: joeyiptk\n\nSummary: Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cos...\n\nTags: automation:1.0.0, configuration:1.1.2, cost-tracking:1.0.0, latest:1.1.2, llm:1.0.0, monitoring:1.0.0, openrouter:1.0.0, token-metrics:1.0.0\n\nVersion history:\n\nv1.1.2 | 2026-05-28T16:05:37.744Z | user\n\n## 1.1.2 (2026-05-28)\n- Added `--days` flag to `collect_usage.py` to allow multi-day collection.\n- Fixed HKT/UTC logic gap by using a 48-hour window in scheduled runs.\n- Improved collection reliability for rolling 24h reports.\n- Excluded local `usage.db` from snapshots (C7 fix).\n\nv1.1.1 | 2026-04-30T03:10:57.164Z | user\n\n## 1.1.1 (2026-04-30)\n- Added: scripts/setup.py\n- Updated: SKILL.md, version.txt\n\n### Changed\n- Clarified billing rules: use `usage.cost.total` as canonical source, not token-count math\n- Split token categories: prompt, cached (cacheRead), cache_write, completion, reasoning, total\n- Updated cron examples in SKILL.md with `--no-deliver` flag for collect cron\n- Removed \"forward output exactly\" requirement from report triggers\n\nv1.0.0 | 2026-04-28T04:40:49.684Z | user\n\n## 1.0.0 — Initial Release\n\n- Track LLM token usage and billed cost from OpenRouter API\n- Report time windows: rolling 24h, 7d, 30d, 90d, 365d\n- Per-model breakdown with top-3 rankings\n- Append-only SQLite DB (request_facts table)\n- Auto-detects sessions directory on any machine\n- Optional daily cron: midnight collection + 9 AM report to Telegram\n- DB health check, schema validation, prune/cleanup tools\n───\n\nArchive index:\n\nArchive v1.1.2: 13 files, 18598 bytes\n\nFiles: CHANGELOG.md (1100b), config/env.json (82b), requirements.txt (16b), scripts/collect_usage.py (18228b), scripts/prune_usage.py (2625b), scripts/run_tracker.py (18991b), scripts/setup.py (1838b), SETUP_GUIDE.md (3318b), SETUP.md (780b), skill-card.md (2243b), SKILL.md (2662b), version.txt (6b), _meta.json (150b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: llm-cost-tracker\nversion: 1.1.2\ndescription: >\n  Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cost\", \"token usage\", \"openrouter cost\", \"llm spend\", \"daily cost report\" → runs run_tracker.py - \"collect usage data\" → runs collect_usage.py (populates DB silently) When \"collect usage data\" is received (e.g. from a cron job), run: python3 scripts/collect_usage.py and return \"Done\" — do NOT send any message to Telegram or any chat.\nread_when: >\n  - Running a cost/token usage report - Scheduling a daily/weekly cost report - Setting up on a new machine\n---\n# llm-cost-tracker\n\nTrack and report LLM token usage and cost for OpenClaw via OpenRouter.\n\n## Quick Start\n\n```bash\npython3 scripts/collect_usage.py --init      # first-time: create DB + backfill\npython3 scripts/run_tracker.py               # telegram report (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output debug     # per-request debug\n```\n\n## Core Design\n\n- **Source of truth:** `usage.cost.total` from OpenRouter — never recomputed from token counts\n- **Data source:** OpenClaw session JSONL files (including `.reset` files from context compaction)\n- **Deduplication:** by `openrouter_request_id` — safe to re-run backfill anytime\n- **Time windows:** 24h rolling (UTC); 7d/30d/90d/365d calendar days (configurable timezone)\n\n## Schema: `request_facts`\n\n| Column | Source |\n|--------|--------|\n| openrouter_request_id | responseId (unique key) |\n| created_at_utc | entry timestamp |\n| model | model ID |\n| prompt_tokens | usage.input |\n| completion_tokens | usage.output |\n| cached_tokens | usage.cacheRead |\n| cache_write_tokens | usage.cacheWrite |\n| reasoning_tokens | usage.reasoning |\n| total_tokens | usage.totalTokens |\n| billed_cost | usage.cost.total (canonical) |\n\n## Configuration\n\n`config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- `SESSIONS_DIR`: override session file location (empty = auto-detect)\n- `TIMEZONE` / `UTC_OFFSET_HOURS`: for calendar-day window calculations\n- API key: auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var\n\n## Scheduled Reports\n\n```bash\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn7a3t7cye5xwvkpam99tawre585q0zr\",\n  \"slug\": \"llm-cost-tracker-openrouter-api\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1779984337744\n}\n\nFile v1.1.2:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to this skill are documented here.\n\n## 1.1.2 (2026-05-28)\n- Added `--days` flag to `collect_usage.py` to allow multi-day collection.\n- Fixed HKT/UTC logic gap by using a 48-hour window in scheduled runs.\n- Improved collection reliability for rolling 24h reports.\n- Excluded local `usage.db` from snapshots (C7 fix).\n\n## 1.1.1\n- Added: scripts/setup.py\n- Updated: SKILL.md, version.txt\n\n## 1.1.0 (2026-04-30)\n\n### Changed\n- Clarified billing rules: use `usage.cost.total` as canonical source, not token-count math\n- Split token categories: prompt, cached (cacheRead), cache_write, completion, reasoning, total\n- Updated cron examples in SKILL.md with `--no-deliver` flag for collect cron\n- Removed \"forward output exactly\" requirement from report triggers\n\n### Added\n- 90-day time window in addition to 24h/7d/30d/365d\n- `/365d` trend line in Telegram report format\n\n## 1.0.0 (2026-04-05)\n\n- Initial release\n- Track and report LLM token usage and cost via OpenRouter\n- Support for 24h rolling and 7d/30d/365d calendar windows\n- SQLite append-only DB with deduplication\n\nFile v1.1.2:SETUP_GUIDE.md\n\n# llm-cost-tracker — Setup Guide\n\n## First-Time Setup\n\n```bash\ncd <path-to-llm-cost-tracker>\npython3 scripts/collect_usage.py --init\n```\n\nThis creates the DB, backfills all session files (including reset files), and runs a health check.\n\n## Scripts\n\n| Script | Purpose |\n|--------|---------|\n| `collect_usage.py` | Scan session JSONL files → populate DB |\n| `run_tracker.py` | Generate reports, health check, debug |\n| `prune_usage.py` | Delete old rows with dry-run support |\n\n### collect_usage.py\n\n```bash\npython3 scripts/collect_usage.py --init          # first-run wizard\npython3 scripts/collect_usage.py --backfill      # re-scan all sessions\npython3 scripts/collect_usage.py                  # collect today only\npython3 scripts/collect_usage.py --date 2026-04-24\npython3 scripts/collect_usage.py --verify         # DB health check\n```\n\n### run_tracker.py\n\n```bash\npython3 scripts/run_tracker.py                    # telegram (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output json      # JSON for scripting\npython3 scripts/run_tracker.py --output debug     # per-request debug\npython3 scripts/run_tracker.py --health           # quick health check\n```\n\n### prune_usage.py\n\n```bash\npython3 scripts/prune_usage.py 2024-04-25 --dry-run   # preview\npython3 scripts/prune_usage.py 2024-04-25 --vacuum     # delete + reclaim\n```\n\n## Configuration\n\nEdit `config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- **SESSIONS_DIR**: Override if auto-detection fails. Leave empty for auto-detect.\n- **TIMEZONE / UTC_OFFSET_HOURS**: For calendar-day windows (default: HKT +8).\n- **API key**: Auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var. Not stored in env.json.\n\n### Auto-detected paths\n\nSessions directory search order:\n1. `SESSIONS_DIR` from env.json\n2. `~/.openclaw/agents/main/sessions`\n3. `/data/.openclaw/agents/main/sessions`\n4. `~/Library/Application Support/openclaw/agents/main/sessions`\n\n## Cron Setup\n\n```bash\n# Midnight: collect data (silent)\nopenclaw cron add \\\n  --name \"llm-cost:collect\" \\\n  --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add \\\n  --name \"llm-cost:daily\" \\\n  --message \"llm cost\" \\\n  --cron \"0 9 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated\n```\n\nVerify: `openclaw cron list`\n\n## Troubleshooting\n\n**Sessions directory not found** — Set `SESSIONS_DIR` in `config/env.json`.\n\n**DB is empty** — Run `python3 scripts/collect_usage.py --init`.\n\n**Cost seems wrong** — Run `--output debug` to inspect per-request costs. Compare with `--health` which shows API key total vs DB total.\n\n**Migrating to a new machine** — Copy the whole folder, then run `--init` again. Cron jobs must be re-created.\n\n## File Structure\n\n```\nllm-cost-tracker/\n├── SKILL.md\n├── SETUP_GUIDE.md\n├── requirements.txt\n├── config/\n│   ├── env.json       (optional config)\n│   └── usage.db       (created on first --init)\n└── scripts/\n    ├── collect_usage.py\n    ├── run_tracker.py\n    └── prune_usage.py\n```\n\nFile v1.1.2:SETUP.md\n\n# Setup — llm-cost-tracker\n\n## Requirements\n- Python 3.7+\n- `tabulate>=0.9.0`\n\n## Install\n```bash\npip install tabulate>=0.9.0\n```\n\n## First-time setup\n```bash\ncd skills/llm-cost-tracker\npython3 scripts/collect_usage.py --init\n```\n\n## Configuration (optional)\nCreate `config/env.json` to override defaults:\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n## Cron jobs\n```bash\n# Midnight: collect usage data (silent)\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```\n\nFile v1.1.2:skill-card.md\n\n## Description:\n\nTracks OpenClaw LLM token usage and billed cost from OpenRouter and reports rolling and calendar-window spend with model breakdowns.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[joeyiptk](https://clawhub.ai/user/joeyiptk)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and OpenClaw users use this skill to collect local OpenClaw usage history and generate OpenRouter cost and token reports for daily or periodic spend monitoring.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The pruning script's dry-run mode is reported to delete rows instead of only previewing them.\n\nMitigation: Do not use scripts/prune_usage.py --dry-run until fixed; back up config/usage.db and verify the cutoff before running any pruning command.\n\nRisk: The skill reads OpenClaw session history and creates a local usage database.\n\nMitigation: Install only where local session-history access is acceptable, restrict the configured sessions directory, and protect the generated usage database.\n\nRisk: Normal reports automatically handle OpenRouter API credentials for account-summary data.\n\nMitigation: Use an explicit least-privileged OpenRouter API key where possible and avoid running setup or tracker commands as root.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/joeyiptk/skills/llm-cost-tracker-openrouter-api)\n- [Setup Guide](artifact/SETUP_GUIDE.md)\n- [Setup](artifact/SETUP.md)\n- [Changelog](artifact/CHANGELOG.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, JSON, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown and terminal text reports, with optional JSON output for scripting]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Reports cover 24h, 7d, 30d, 90d, and 365d windows with model-level cost and token breakdowns.]\n\n## Skill Version(s):\n\n1.1.2 (source: frontmatter, changelog, version.txt, server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.1.2:config/env.json\n\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n\nFile v1.1.2:requirements.txt\n\ntabulate>=0.9.0\n\nFile v1.1.2:version.txt\n\n1.1.2\n\nArchive v1.1.1: 13 files, 18408 bytes\n\nFiles: CHANGELOG.md (819b), config/env.json (82b), requirements.txt (16b), scripts/collect_usage.py (17989b), scripts/prune_usage.py (2625b), scripts/run_tracker.py (19017b), scripts/setup.py (1838b), SETUP_GUIDE.md (3318b), SETUP.md (780b), skill-card.md (2454b), SKILL.md (2662b), version.txt (6b), _meta.json (150b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: llm-cost-tracker\nversion: 1.1.1\ndescription: >\n  Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cost\", \"token usage\", \"openrouter cost\", \"llm spend\", \"daily cost report\" → runs run_tracker.py - \"collect usage data\" → runs collect_usage.py (populates DB silently) When \"collect usage data\" is received (e.g. from a cron job), run: python3 scripts/collect_usage.py and return \"Done\" — do NOT send any message to Telegram or any chat.\nread_when: >\n  - Running a cost/token usage report - Scheduling a daily/weekly cost report - Setting up on a new machine\n---\n# llm-cost-tracker\n\nTrack and report LLM token usage and cost for OpenClaw via OpenRouter.\n\n## Quick Start\n\n```bash\npython3 scripts/collect_usage.py --init      # first-time: create DB + backfill\npython3 scripts/run_tracker.py               # telegram report (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output debug     # per-request debug\n```\n\n## Core Design\n\n- **Source of truth:** `usage.cost.total` from OpenRouter — never recomputed from token counts\n- **Data source:** OpenClaw session JSONL files (including `.reset` files from context compaction)\n- **Deduplication:** by `openrouter_request_id` — safe to re-run backfill anytime\n- **Time windows:** 24h rolling (UTC); 7d/30d/90d/365d calendar days (configurable timezone)\n\n## Schema: `request_facts`\n\n| Column | Source |\n|--------|--------|\n| openrouter_request_id | responseId (unique key) |\n| created_at_utc | entry timestamp |\n| model | model ID |\n| prompt_tokens | usage.input |\n| completion_tokens | usage.output |\n| cached_tokens | usage.cacheRead |\n| cache_write_tokens | usage.cacheWrite |\n| reasoning_tokens | usage.reasoning |\n| total_tokens | usage.totalTokens |\n| billed_cost | usage.cost.total (canonical) |\n\n## Configuration\n\n`config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- `SESSIONS_DIR`: override session file location (empty = auto-detect)\n- `TIMEZONE` / `UTC_OFFSET_HOURS`: for calendar-day window calculations\n- API key: auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var\n\n## Scheduled Reports\n\n```bash\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn7a3t7cye5xwvkpam99tawre585q0zr\",\n  \"slug\": \"llm-cost-tracker-openrouter-api\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1777518657164\n}\n\nFile v1.1.1:CHANGELOG.md\n\n# Changelog\n\nAll notable changes to this skill are documented here.\n\n## 1.1.1\n- Added: scripts/setup.py\n- Updated: SKILL.md, version.txt\n\n## 1.1.0 (2026-04-30)\n\n### Changed\n- Clarified billing rules: use `usage.cost.total` as canonical source, not token-count math\n- Split token categories: prompt, cached (cacheRead), cache_write, completion, reasoning, total\n- Updated cron examples in SKILL.md with `--no-deliver` flag for collect cron\n- Removed \"forward output exactly\" requirement from report triggers\n\n### Added\n- 90-day time window in addition to 24h/7d/30d/365d\n- `/365d` trend line in Telegram report format\n\n## 1.0.0 (2026-04-05)\n\n- Initial release\n- Track and report LLM token usage and cost via OpenRouter\n- Support for 24h rolling and 7d/30d/365d calendar windows\n- SQLite append-only DB with deduplication\n\nFile v1.1.1:SETUP_GUIDE.md\n\n# llm-cost-tracker — Setup Guide\n\n## First-Time Setup\n\n```bash\ncd <path-to-llm-cost-tracker>\npython3 scripts/collect_usage.py --init\n```\n\nThis creates the DB, backfills all session files (including reset files), and runs a health check.\n\n## Scripts\n\n| Script | Purpose |\n|--------|---------|\n| `collect_usage.py` | Scan session JSONL files → populate DB |\n| `run_tracker.py` | Generate reports, health check, debug |\n| `prune_usage.py` | Delete old rows with dry-run support |\n\n### collect_usage.py\n\n```bash\npython3 scripts/collect_usage.py --init          # first-run wizard\npython3 scripts/collect_usage.py --backfill      # re-scan all sessions\npython3 scripts/collect_usage.py                  # collect today only\npython3 scripts/collect_usage.py --date 2026-04-24\npython3 scripts/collect_usage.py --verify         # DB health check\n```\n\n### run_tracker.py\n\n```bash\npython3 scripts/run_tracker.py                    # telegram (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output json      # JSON for scripting\npython3 scripts/run_tracker.py --output debug     # per-request debug\npython3 scripts/run_tracker.py --health           # quick health check\n```\n\n### prune_usage.py\n\n```bash\npython3 scripts/prune_usage.py 2024-04-25 --dry-run   # preview\npython3 scripts/prune_usage.py 2024-04-25 --vacuum     # delete + reclaim\n```\n\n## Configuration\n\nEdit `config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- **SESSIONS_DIR**: Override if auto-detection fails. Leave empty for auto-detect.\n- **TIMEZONE / UTC_OFFSET_HOURS**: For calendar-day windows (default: HKT +8).\n- **API key**: Auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var. Not stored in env.json.\n\n### Auto-detected paths\n\nSessions directory search order:\n1. `SESSIONS_DIR` from env.json\n2. `~/.openclaw/agents/main/sessions`\n3. `/data/.openclaw/agents/main/sessions`\n4. `~/Library/Application Support/openclaw/agents/main/sessions`\n\n## Cron Setup\n\n```bash\n# Midnight: collect data (silent)\nopenclaw cron add \\\n  --name \"llm-cost:collect\" \\\n  --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add \\\n  --name \"llm-cost:daily\" \\\n  --message \"llm cost\" \\\n  --cron \"0 9 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated\n```\n\nVerify: `openclaw cron list`\n\n## Troubleshooting\n\n**Sessions directory not found** — Set `SESSIONS_DIR` in `config/env.json`.\n\n**DB is empty** — Run `python3 scripts/collect_usage.py --init`.\n\n**Cost seems wrong** — Run `--output debug` to inspect per-request costs. Compare with `--health` which shows API key total vs DB total.\n\n**Migrating to a new machine** — Copy the whole folder, then run `--init` again. Cron jobs must be re-created.\n\n## File Structure\n\n```\nllm-cost-tracker/\n├── SKILL.md\n├── SETUP_GUIDE.md\n├── requirements.txt\n├── config/\n│   ├── env.json       (optional config)\n│   └── usage.db       (created on first --init)\n└── scripts/\n    ├── collect_usage.py\n    ├── run_tracker.py\n    └── prune_usage.py\n```\n\nFile v1.1.1:SETUP.md\n\n# Setup — llm-cost-tracker\n\n## Requirements\n- Python 3.7+\n- `tabulate>=0.9.0`\n\n## Install\n```bash\npip install tabulate>=0.9.0\n```\n\n## First-time setup\n```bash\ncd skills/llm-cost-tracker\npython3 scripts/collect_usage.py --init\n```\n\n## Configuration (optional)\nCreate `config/env.json` to override defaults:\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n## Cron jobs\n```bash\n# Midnight: collect usage data (silent)\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```\n\nFile v1.1.1:skill-card.md\n\n## Description: <br>\nTracks OpenClaw LLM token usage and billed cost from OpenRouter and generates cost reports across rolling and calendar windows. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[joeyiptk](https://clawhub.ai/user/joeyiptk) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and OpenClaw users use this skill to collect local session usage into a SQLite database and report OpenRouter token spend by time window and model. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires access to OpenClaw session metadata and an OpenRouter API key for cost reporting. <br>\nMitigation: Install only where that local metadata and credential access is acceptable, and prefer the documented environment variable or existing OpenClaw auth profile for key handling. <br>\nRisk: The documented prune dry-run command can delete local usage history despite claiming to preview changes. <br>\nMitigation: Back up config/usage.db and avoid running scripts/prune_usage.py --dry-run in this version until the cleanup behavior is fixed or manually verified. <br>\nRisk: Scheduled collection jobs run silently and can repeatedly read local session files. <br>\nMitigation: Review cron entries before enabling them, especially the silent collection job, and scope SESSIONS_DIR to the intended OpenClaw sessions path. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/joeyiptk/llm-cost-tracker-openrouter-api) <br>\n- [Setup Guide](artifact/SETUP_GUIDE.md) <br>\n- [Setup](artifact/SETUP.md) <br>\n- [Changelog](artifact/CHANGELOG.md) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Markdown, Shell commands, Configuration, Guidance] <br>\n**Output Format:** [Markdown reports with inline shell commands and optional JSON output from tracker scripts] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Reports may include 24h, 7d, 30d, 90d, and 365d usage windows with model breakdowns.] <br>\n\n## Skill Version(s): <br>\n1.1.1 (source: SKILL.md frontmatter, version.txt, CHANGELOG, server release evidence) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.1.1:config/env.json\n\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n\nFile v1.1.1:requirements.txt\n\ntabulate>=0.9.0\n\nFile v1.1.1:version.txt\n\n1.1.1\n\nArchive v1.0.0: 12 files, 26607 bytes\n\nFiles: CHANGELOG.md (504b), config/env.json (53b), portable_setup.py (5662b), requirements.txt (32b), scripts/collect_usage.py (20981b), scripts/health_check.py (8360b), scripts/prune_usage.py (2625b), scripts/run_tracker.py (25479b), SETUP_GUIDE.md (7684b), SKILL.md (8860b), version.txt (6b), _meta.json (150b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: llm-cost-tracker\nversion: 1.0.0\ndescription: >\n  Track OpenClaw LLM token usage and cost from OpenRouter API.\n  Reports: last 24h, 7d, 30d, 365d with model breakdown and DB health footer.\n  Run manually or schedule via `openclaw cron add`.\n  Skills that trigger this:\n    - \"llm cost\", \"token usage\", \"openrouter cost\", \"llm spend\", \"daily cost report\" → runs run_tracker.py (report)\n    - \"collect usage data\" → runs scripts/collect_usage.py (populates DB silently, no output)\n  IMPORTANT — report triggers: After running run_tracker.py --output telegram, you must\n    forward the script's output EXACTLY as your final reply — do not summarise, reformat,\n    shorten, omit sections, or add your own commentary. The script output IS the reply.\n  When \"collect usage data\" is received (e.g. from a midnight cron job), run:\n    python3 scripts/collect_usage.py\n    and return \"Done\" — do NOT send any message to Telegram or any chat.\nread_when:\n  - Running a cost/token usage report\n  - Scheduling a daily/weekly cost report\n  - Setting up on a new machine\n  - Checking which models are driving the most spend\n---\n\n# llm-cost-tracker\n\n> See [CHANGELOG.md](./CHANGELOG.md) for version history.\n\nTrack and report LLM token usage and cost for OpenClaw sessions powered by OpenRouter.\n\n## Core Design\n\n**Source of truth:** `usage.cost.total` from OpenRouter's API response — this is the\nactual billed amount after cache discounts, reasoning charges, and all other\npricing adjustments. We never recompute it from token counts and price lists.\n\n**Append-only fact table:** one row per completed OpenRouter request, keyed by\n`openrouter_request_id` (the generation/response ID). Duplicates from retries\nor stream reconnects are handled idempotently.\n\n**Token categories** (from OpenRouter usage, kept SEPARATE):\n- `prompt_tokens` = `usage.input` — raw prompt tokens before cache discount\n- `cached_tokens` = `usage.cacheRead` — cached read tokens (discounted billing)\n- `cache_write_tokens` = `usage.cacheWrite` — cache build tokens (separate cost)\n- `completion_tokens` = `usage.output` — generated output tokens\n- `reasoning_tokens` = `usage.reasoning` — thinking tokens (if model exposes)\n- `total_tokens` = `usage.totalTokens` — sum of all above (should equal the sum)\n\n**Billing rules:**\n- Billed cost = `usage.cost.total` directly — do NOT use `prompt_tokens * input_price + completion_tokens * output_price` as the primary formula\n- OpenRouter applies cache discounts, reasoning charges, and all other pricing adjustments automatically in `cost.total`\n- `cached_tokens` and `cache_write_tokens` are separate billing categories from `prompt_tokens` — do NOT add them together for a \"total prompt\" count\n- Only use `cost.total`; if absent, fall back to `cost.input + cost.output + cost.cacheRead + cost.cacheWrite`\n\n**Time windows:**\n- last 24h = rolling, now_utc − 24 hours (UTC)\n- last 7d / 30d / 90d / 365d = calendar days (HKT), inclusive of today\n\n## Quick Start\n\n```bash\ncd skills/llm-cost-tracker\n\n# First-time setup (creates DB, backfills, verifies) — run once on a new machine\npython3 scripts/collect_usage.py --init\n\n# Telegram-formatted report (default)\npython3 scripts/run_tracker.py --output telegram\n\n# Full terminal report\npython3 scripts/run_tracker.py --output terminal\n\n# Per-request debug view\npython3 scripts/run_tracker.py --output debug --debug-hours 24\n```\n\n## Optional: Scheduled Reports\n\nIf you want reports delivered automatically to Telegram every day:\n\n```bash\n# Midnight collection (silent, populates DB — run daily)\nopenclaw cron add \\\n  --name \"llm-cost:collect\" \\\n  --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --no-deliver \\\n  --description \"Populate request_facts from session files\"\n\n# 9 AM report (delivers to Telegram)\nopenclaw cron add \\\n  --name \"llm-cost:daily\" \\\n  --message \"llm cost\" \\\n  --cron \"0 9 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --description \"Daily LLM cost report to Telegram\"\n```\n\n## Report Layout\n\n```\n📊 LLM Cost Report — Apr 26, 2026 18:34 HKT\n\n• Messages (24h): 124\n• Est. Tokens (24h): 4.75M\n• Est. Cost (24h): $0.4933\n• Total Spend (API key): $93.67\n• Limit Remaining: $45.74\n\n🏆 Top Models (24h):\n  1. minimax-m2.7: $10.7432\n  2. minimax-m2.5:free: $0.0000\n  3. delivery-mirror: $0.0000\n\n📈 Trend:\n• Last 24h: $0.4933\n• Last 7 days: $0.8542\n• Last 30 days: $10.7432\n• Last 90 days: $10.7432\n• Last 365 days: $10.7432\n\n💾 DB: 1.6 MB · 1,872 rows · Since Apr 5, 2026\n\n_Sent via llm-cost-tracker_\n```\n\n> **Note:** \"Last 7/30/90/365 days\" uses calendar days (HKT), inclusive of today.\n> \"Last 24h\" uses a rolling 24-hour window (UTC). The DB footer shows\n> the oldest record in the DB — useful for deciding when to prune old data.\n\n## First-Time Setup\n\n**One command sets up everything — DB creation, backfill, and health check:**\n\n```bash\ncd skills/llm-cost-tracker\npython3 scripts/collect_usage.py --init\n```\n\n> ⚠️ **Don't forget — set up your scheduled jobs next!**  \n> Without them, no new data gets collected after the backfill finishes.\n> Run these two commands before you close this terminal:\n>\n> ```bash\n> # Midnight: collect usage data (silent, no output to Telegram)\n> openclaw cron add \\\n>   --name \"llm-cost:collect\" \\\n>   --message \"collect usage data\" \\\n>   --cron \"5 0 * * *\" \\\n>   --tz \"Asia/Hong_Kong\" \\\n>   --session isolated \\\n>   --no-deliver \\\n>   --description \"Populate request_facts from session files\"\n>\n> # 9 AM: daily cost report delivered to Telegram\n> openclaw cron add \\\n>   --name \"llm-cost:daily\" \\\n>   --message \"llm cost\" \\\n>   --cron \"0 9 * * *\" \\\n>   --tz \"Asia/Hong_Kong\" \\\n>   --session isolated \\\n>   --description \"Daily LLM cost report to Telegram\"\n> ```\n\nThis runs:\n1. Creates `config/usage.db` with the correct schema (if it doesn't exist)\n2. Finds your OpenClaw sessions directory automatically\n3. Backfills all available session JSONL files\n4. Runs a health check to verify everything is working\n5. Prints DB summary (size, row count, date range)\n\n**Ongoing / step by step (alternative to `--init`):**\n\n```bash\n# Backfill all historical sessions\npython3 scripts/collect_usage.py --backfill\n\n# Verify DB health\npython3 scripts/run_tracker.py --health\n\n# Run first report\npython3 scripts/run_tracker.py --output telegram\n```\n\n> `--init` is idempotent — re-running it is safe. It will append any new sessions\n> since the last backfill rather than overwriting anything.\n\n## DB Maintenance\n\nThe report footer (`💾 DB: ...`) shows current DB size, row count, and the oldest\nrecord date. Over time the DB grows. To prune old data:\n\n```bash\n# Preview how many rows would be deleted (dry run)\npython3 scripts/prune_usage.py 2024-04-25 --dry-run\n\n# Delete rows older than 2024-04-25 and reclaim disk space\npython3 scripts/prune_usage.py 2024-04-25 --vacuum\n```\n\n> **Retention tip:** 2-year retention = delete rows older than ~730 days ago.\n> A quarterly prune is recommended — pick a date, run dry-run first,\n> then execute without `--dry-run` and add `--vacuum` to reclaim space.\n\n## Architecture\n\n```\nSession JSONL files  ──▶  collect_usage.py  ──▶  request_facts (SQLite)\n                                                 │\n                                                 └──▶  run_tracker.py  ──▶  Report\n                                                                 │\nOpenRouter API  ──▶  get_openrouter_total_usage()  ───────────────┘\n```\n\n## Schema\n\nTable: `request_facts` (one row per OpenRouter request)\n\n| Column | Type | Notes |\n|--------|------|-------|\n| openrouter_request_id | TEXT UNIQUE | responseId / generation ID |\n| created_at_utc | TEXT | ISO timestamp in UTC |\n| model | TEXT | model ID |\n| provider | TEXT | provider (openrouter, etc.) |\n| status | TEXT | stopReason (completed, toolUse, etc.) |\n| prompt_tokens | INTEGER | usage.input |\n| completion_tokens | INTEGER | usage.output |\n| total_tokens | INTEGER | usage.totalTokens |\n| reasoning_tokens | INTEGER | usage.reasoning |\n| cached_tokens | INTEGER | usage.cacheRead |\n| cache_write_tokens | INTEGER | usage.cacheWrite |\n| billed_cost | REAL | usage.cost.total (canonical) |\n| streamed | INTEGER | 1=yes, 0=no |\n| raw_usage_json | TEXT | full usage object as JSON |\n\n## Validation\n\nTo validate against OpenRouter's own total:\n1. OpenRouter dashboard → API Keys → check `total_usage` against `run_tracker.py` sum\n2. Run `--output debug` to inspect per-request `billed_cost` and `raw_usage_json`\n3. The OpenRouter API total (all-time) is shown in every report as a reference point\n\nKnown gap: session JSONL files only go back to ~April 5 2026. Earlier usage\nappears in OpenRouter's all-time total but not in the per-request breakdown.\n\n---\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7a3t7cye5xwvkpam99tawre585q0zr\",\n  \"slug\": \"llm-cost-tracker-openrouter-api\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1777351249684\n}\n\nFile v1.0.0:CHANGELOG.md\n\n# Changelog\n\n## 1.0.0 — 2026-04-28\n### Added\n- Initial release\n- Track LLM token usage and billed cost from OpenRouter API\n- Report time windows: rolling 24h, 7d, 30d, 90d, 365d\n- Per-model breakdown with top-3 rankings\n- Append-only SQLite DB (request_facts table)\n- Auto-detects sessions directory on any machine\n- Optional daily cron: midnight collection + 9 AM report to Telegram\n- DB health check, schema validation, prune/cleanup tools\n- Portable — works on any machine with OpenClaw installed\n\nFile v1.0.0:SETUP_GUIDE.md\n\n# llm-cost-tracker Setup Guide\n\nPortable setup for any machine running OpenClaw. All paths are auto-detected.\n\n---\n\n## Quick Start (New Machine)\n\n```bash\n# 1. Navigate to wherever you installed the skill\ncd <path-to-llm-cost-tracker>\n\n# 2. Verify setup\npython3 scripts/run_tracker.py --health\n\n# 3. Backfill historical data from existing session files\npython3 scripts/collect_usage.py --backfill\n\n# 4. Run a test report\npython3 scripts/run_tracker.py --output telegram\n\n# 5. Schedule cron jobs (see Cron Setup section below)\n```\n\nDone. The skill works on any machine with OpenClaw installed.\n\n---\n\n## What Each Script Does\n\n| Script | Purpose |\n|--------|---------|\n| `collect_usage.py` | Scan session JSONL files → populate `request_facts` DB |\n| `run_tracker.py` | Generate usage reports, health check, debug |\n| `prune_usage.py` | Prune old DB rows with dry-run support |\n\n### collect_usage.py\n\n```bash\n# Backfill all historical sessions (run once on new machine)\npython3 scripts/collect_usage.py --backfill\n\n# Collect today's requests only\npython3 scripts/collect_usage.py\n\n# Collect a specific UTC date\npython3 scripts/collect_usage.py --date 2026-04-24\n\n# Verify DB health (no writes)\npython3 scripts/collect_usage.py --verify\n\n# Preview without writing\npython3 scripts/collect_usage.py --dry-run\n```\n\n### run_tracker.py\n\n```bash\n# Telegram report (default)\npython3 scripts/run_tracker.py --output telegram\n\n# Full terminal report\npython3 scripts/run_tracker.py --output terminal\n\n# JSON for programmatic use\npython3 scripts/run_tracker.py --output json\n\n# Per-request debug view\npython3 scripts/run_tracker.py --output debug --debug-hours 24\n\n# Health check\npython3 scripts/run_tracker.py --health\n```\n\n---\n\n## Path Detection (Auto-Detected)\n\nThe skill auto-detects paths. You only need to set `env.json` if auto-detection fails.\n\n### Sessions Directory\n\nSearched in order:\n1. `config/env.json` → `SESSIONS_DIR` (explicit override)\n2. `~/.openclaw/agents/main/sessions` (Linux/macOS default)\n3. `~/Library/Application Support/openclaw/agents/main/sessions` (macOS)\n4. `%USERPROFILE%\\.openclaw\\agents\\main\\sessions` (Windows)\n\n### Database\n\n`config/usage.db` relative to skill directory (portable — lives inside the skill folder).\n\n### OpenRouter API Key\n\n**Source of truth:** `~/.openclaw/openclaw.json` → `env.OPENROUTER_API_KEY`\n\nThe skill resolves the API key from (priority order):\n1. `config/env.json` → `OPENROUTER_API_KEY` (for legacy/migration use only)\n2. Environment variable `OPENROUTER_API_KEY` (set automatically by OpenClaw from `openclaw.json`)\n\n**For portability:** On a new machine, ensure `~/.openclaw/openclaw.json` has your `env.OPENROUTER_API_KEY` configured. The skill will pick it up automatically. Do NOT store the API key in `config/env.json` — it belongs in OpenClaw's central config.\n\n**Verification:**\n```bash\n# Should show your API key (redacted)\npython3 scripts/run_tracker.py --health\n# or\npython3 scripts/health_check.py\n```\n\n---\n\n## Cron Setup (Per Machine)\n\nRun on the OpenClaw Gateway host.\n\n**Midnight collection** (silent, populates DB):\n```bash\nopenclaw cron add \\\n  --name \"llm-cost:collect\" \\\n  --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --no-deliver \\\n  --description \"Collect OpenRouter usage into request_facts DB\"\n```\n\n**9 AM report** (delivers to Telegram):\n```bash\nopenclaw cron add \\\n  --name \"llm-cost:daily\" \\\n  --message \"llm cost\" \\\n  --cron \"0 9 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --description \"Daily LLM cost report to Telegram\"\n```\n\nVerify:\n```bash\nopenclaw cron list\n```\n\nRemove old crons if any:\n```bash\nopenclaw cron remove llm-cost:collect\nopenclaw cron remove llm-cost:daily\n```\n\n---\n\n## Configuration (Optional)\n\nCreate or edit `config/env.json`:\n\n```json\n{\n  \"OPENROUTER_API_KEY\": \"sk-or-v1-...\",\n  \"SESSIONS_DIR\": \"~/.openclaw/agents/main/sessions\"\n}\n```\n\nOnly needed if:\n- Auto-detection fails for sessions directory\n- API key isn't in the environment variable\n\nOn first run, the skill auto-detects all paths — you only need this file if something isn't found.\n\n---\n\n## Schema\n\n`request_facts` table — one row per OpenRouter request.\n\n| Column | Type | Description |\n|--------|------|-------------|\n| openrouter_request_id | TEXT UNIQUE | Response ID (deduplication key) |\n| created_at_utc | TEXT | Request timestamp in UTC |\n| model | TEXT | Model ID |\n| status | TEXT | stopReason (completed, toolUse, etc.) |\n| prompt_tokens | INTEGER | usage.input |\n| completion_tokens | INTEGER | usage.output |\n| cached_tokens | INTEGER | usage.cacheRead |\n| cache_write_tokens | INTEGER | usage.cacheWrite |\n| reasoning_tokens | INTEGER | usage.reasoning |\n| total_tokens | INTEGER | usage.totalTokens |\n| billed_cost | REAL | usage.cost.total (canonical) |\n| streamed | INTEGER | 1=yes, 0=no |\n| raw_usage_json | TEXT | Full usage object as JSON |\n\nIndexes on `created_at_utc` and `model` for fast rolling window queries.\n\n---\n\n## Billing Rules\n\n**Canonical cost** = `usage.cost.total` from OpenRouter response.\n\nDo NOT use `prompt_tokens × price + completion_tokens × price` as the primary path.\nOpenRouter already applied all adjustments in `cost.total`:\n- Cache discounts (cached_tokens billed at discounted rate)\n- Reasoning charges (reasoning_tokens)\n- Any model-specific pricing\n\nToken categories are separate — do NOT add cached_tokens to prompt_tokens.\n`total_tokens = prompt + completion + cached + cache_write [+ reasoning]`\n\n---\n\n## Rolling Windows\n\nUTC-based rolling windows, NOT calendar days:\n\n| Window | Query |\n|--------|-------|\n| last 24h | `WHERE created_at_utc >= now_utc − 24 hours` |\n| last 7d | `WHERE created_at_utc >= now_utc − 7 days` |\n| last 30d | `WHERE created_at_utc >= now_utc − 30 days` |\n| last 365d | `WHERE created_at_utc >= now_utc − 365 days` |\n\n---\n\n## Troubleshooting\n\n### \"Sessions directory not found\"\n\n```bash\n# Check if OpenClaw sessions exist (auto-detected path)\nls ~/.openclaw/agents/main/sessions/\n```\n\nIf found elsewhere, set in `config/env.json`:\n```json\n{ \"SESSIONS_DIR\": \"~/.openclaw/agents/main/sessions\" }\n```\n\n### \"DB is empty\"\n\nRun backfill:\n```bash\npython3 scripts/collect_usage.py --backfill\n```\n\n### \"billed_cost seems wrong\"\n\nRun debug view to compare against raw usage:\n```bash\npython3 scripts/run_tracker.py --output debug --debug-hours 24\n```\n\nValidate against OpenRouter dashboard → API Keys → `total_usage`.\n\n### \"duplicate request IDs\" in debug\n\nThat's fine — the `ON CONFLICT` upsert handles duplicates idempotently.\nRun with `--verify` to check actual duplicate count:\n```bash\npython3 scripts/collect_usage.py --verify\n```\n\n### Migrating from old schema\n\nIf you had old `daily_summary` / `model_usage` data:\n- Old tables were dropped (2026-04-25 migration)\n- Old data cannot be migrated — only new `request_facts` from session JSONL\n- Run `--backfill` to repopulate from session files (goes back to first JSONL file)\n\n---\n\n## Portability\n\nThe skill is self-contained in one directory:\n```\nllm-cost-tracker/\n├── SKILL.md           ← you're reading this\n├── SETUP_GUIDE.md\n├── requirements.txt\n├── config/\n│   ├── env.json       ← optional (auto-detected if absent)\n│   └── usage.db       ← SQLite DB (created on first --backfill)\n└── scripts/\n    ├── collect_usage.py\n    ├── run_tracker.py\n    └── prune_usage.py\n```\n\nCopy the entire `llm-cost-tracker/` folder to a new machine and run:\n```bash\npython3 scripts/run_tracker.py --health\npython3 scripts/collect_usage.py --backfill\n```\n\nThe cron jobs must be re-created on the new machine (`openclaw cron add`).\n\nFile v1.0.0:config/env.json\n\n{\n  \"SESSIONS_DIR\": \"\",\n  \"OPENROUTER_API_KEY\": \"\"\n}\n\nFile v1.0.0:requirements.txt\n\nrequests>=2.31.0\ntabulate>=0.9.0\n\nFile v1.0.0:version.txt\n\n1.0.0","readmeExcerpt":"Skill: LLM Cost Tracker Owner: joeyiptk Summary: Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cos... Tags: automation:1.0.0, configuration:1.1.2, cost-tracking:1.0.0, latest:1.1.2, llm:1.0.0, monitoring:1.0.0, openrouter:1.0.0, token-metrics:1.0.0 Version history: v1.1.2 | 2026-05-28T16:05:37.744Z | user 1.1.2","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"python3 scripts/collect_usage.py --init      # first-time: create DB + backfill\npython3 scripts/run_tracker.py               # telegram report (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output debug     # per-request debug"},{"language":"json","snippet":"{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}"},{"language":"bash","snippet":"openclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated"},{"language":"bash","snippet":"cd <path-to-llm-cost-tracker>\npython3 scripts/collect_usage.py --init"},{"language":"bash","snippet":"python3 scripts/collect_usage.py --init          # first-run wizard\npython3 scripts/collect_usage.py --backfill      # re-scan all sessions\npython3 scripts/collect_usage.py                  # collect today only\npython3 scripts/collect_usage.py --date 2026-04-24\npython3 scripts/collect_usage.py --verify         # DB health check"},{"language":"bash","snippet":"python3 scripts/run_tracker.py                    # telegram (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output json      # JSON for scripting\npython3 scripts/run_tracker.py --output debug     # per-request debug\npython3 scripts/run_tracker.py --health           # quick health check"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: llm-cost-tracker\nversion: 1.1.2\ndescription: >\n  Track OpenClaw LLM token usage and cost from OpenRouter API. Reports: last 24h, 7d, 30d, 90d, 365d with model breakdown. Skills that trigger this: - \"llm cost\", \"token usage\", \"openrouter cost\", \"llm spend\", \"daily cost report\" → runs run_tracker.py - \"collect usage data\" → runs collect_usage.py (populates DB silently) When \"collect usage data\" is received (e.g. from a cron job), run: python3 scripts/collect_usage.py and return \"Done\" — do NOT send any message to Telegram or any chat.\nread_when: >\n  - Running a cost/token usage report - Scheduling a daily/weekly cost report - Setting up on a new machine\n---\n# llm-cost-tracker\n\nTrack and report LLM token usage and cost for OpenClaw via OpenRouter.\n\n## Quick Start\n\n```bash\npython3 scripts/collect_usage.py --init      # first-time: create DB + backfill\npython3 scripts/run_tracker.py               # telegram report (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output debug     # per-request debug\n```\n\n## Core Design\n\n- **Source of truth:** `usage.cost.total` from OpenRouter — never recomputed from token counts\n- **Data source:** OpenClaw session JSONL files (including `.reset` files from context compaction)\n- **Deduplication:** by `openrouter_request_id` — safe to re-run backfill anytime\n- **Time windows:** 24h rolling (UTC); 7d/30d/90d/365d calendar days (configurable timezone)\n\n## Schema: `request_facts`\n\n| Column | Source |\n|--------|--------|\n| openrouter_request_id | responseId (unique key) |\n| created_at_utc | entry timestamp |\n| model | model ID |\n| prompt_tokens | usage.input |\n| completion_tokens | usage.output |\n| cached_tokens | usage.cacheRead |\n| cache_write_tokens | usage.cacheWrite |\n| reasoning_tokens | usage.reasoning |\n| total_tokens | usage.totalTokens |\n| billed_cost | usage.cost.total (canonical) |\n\n## Configuration\n\n`config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- `SESSIONS_DIR`: override session file location (empty = auto-detect)\n- `TIMEZONE` / `UTC_OFFSET_HOURS`: for calendar-day window calculations\n- API key: auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var\n\n## Scheduled Reports\n\n```bash\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7a3t7cye5xwvkpam99tawre585q0zr\",\n  \"slug\": \"llm-cost-tracker-openrouter-api\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1779984337744\n}"},{"path":"CHANGELOG.md","content":"# Changelog\n\nAll notable changes to this skill are documented here.\n\n## 1.1.2 (2026-05-28)\n- Added `--days` flag to `collect_usage.py` to allow multi-day collection.\n- Fixed HKT/UTC logic gap by using a 48-hour window in scheduled runs.\n- Improved collection reliability for rolling 24h reports.\n- Excluded local `usage.db` from snapshots (C7 fix).\n\n## 1.1.1\n- Added: scripts/setup.py\n- Updated: SKILL.md, version.txt\n\n## 1.1.0 (2026-04-30)\n\n### Changed\n- Clarified billing rules: use `usage.cost.total` as canonical source, not token-count math\n- Split token categories: prompt, cached (cacheRead), cache_write, completion, reasoning, total\n- Updated cron examples in SKILL.md with `--no-deliver` flag for collect cron\n- Removed \"forward output exactly\" requirement from report triggers\n\n### Added\n- 90-day time window in addition to 24h/7d/30d/365d\n- `/365d` trend line in Telegram report format\n\n## 1.0.0 (2026-04-05)\n\n- Initial release\n- Track and report LLM token usage and cost via OpenRouter\n- Support for 24h rolling and 7d/30d/365d calendar windows\n- SQLite append-only DB with deduplication"},{"path":"SETUP_GUIDE.md","content":"# llm-cost-tracker — Setup Guide\n\n## First-Time Setup\n\n```bash\ncd <path-to-llm-cost-tracker>\npython3 scripts/collect_usage.py --init\n```\n\nThis creates the DB, backfills all session files (including reset files), and runs a health check.\n\n## Scripts\n\n| Script | Purpose |\n|--------|---------|\n| `collect_usage.py` | Scan session JSONL files → populate DB |\n| `run_tracker.py` | Generate reports, health check, debug |\n| `prune_usage.py` | Delete old rows with dry-run support |\n\n### collect_usage.py\n\n```bash\npython3 scripts/collect_usage.py --init          # first-run wizard\npython3 scripts/collect_usage.py --backfill      # re-scan all sessions\npython3 scripts/collect_usage.py                  # collect today only\npython3 scripts/collect_usage.py --date 2026-04-24\npython3 scripts/collect_usage.py --verify         # DB health check\n```\n\n### run_tracker.py\n\n```bash\npython3 scripts/run_tracker.py                    # telegram (default)\npython3 scripts/run_tracker.py --output terminal  # full terminal report\npython3 scripts/run_tracker.py --output json      # JSON for scripting\npython3 scripts/run_tracker.py --output debug     # per-request debug\npython3 scripts/run_tracker.py --health           # quick health check\n```\n\n### prune_usage.py\n\n```bash\npython3 scripts/prune_usage.py 2024-04-25 --dry-run   # preview\npython3 scripts/prune_usage.py 2024-04-25 --vacuum     # delete + reclaim\n```\n\n## Configuration\n\nEdit `config/env.json` (all fields optional):\n\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n- **SESSIONS_DIR**: Override if auto-detection fails. Leave empty for auto-detect.\n- **TIMEZONE / UTC_OFFSET_HOURS**: For calendar-day windows (default: HKT +8).\n- **API key**: Auto-detected from `~/.openclaw/agents/main/agent/auth-profiles.json` or `OPENROUTER_API_KEY` env var. Not stored in env.json.\n\n### Auto-detected paths\n\nSessions directory search order:\n1. `SESSIONS_DIR` from env.json\n2. `~/.openclaw/agents/main/sessions`\n3. `/data/.openclaw/agents/main/sessions`\n4. `~/Library/Application Support/openclaw/agents/main/sessions`\n\n## Cron Setup\n\n```bash\n# Midnight: collect data (silent)\nopenclaw cron add \\\n  --name \"llm-cost:collect\" \\\n  --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated \\\n  --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add \\\n  --name \"llm-cost:daily\" \\\n  --message \"llm cost\" \\\n  --cron \"0 9 * * *\" \\\n  --tz \"Asia/Hong_Kong\" \\\n  --session isolated\n```\n\nVerify: `openclaw cron list`\n\n## Troubleshooting\n\n**Sessions directory not found** — Set `SESSIONS_DIR` in `config/env.json`.\n\n**DB is empty** — Run `python3 scripts/collect_usage.py --init`.\n\n**Cost seems wrong** — Run `--output debug` to inspect per-request costs. Compare with `--health` which shows API key total vs DB total.\n\n**Migrating to a new machine** — Copy the whole folder, then run `--init` again. Cron jobs must be re-created.\n\n## File Structure\n\n```\nllm-cost-tracker/\n├"},{"path":"SETUP.md","content":"# Setup — llm-cost-tracker\n\n## Requirements\n- Python 3.7+\n- `tabulate>=0.9.0`\n\n## Install\n```bash\npip install tabulate>=0.9.0\n```\n\n## First-time setup\n```bash\ncd skills/llm-cost-tracker\npython3 scripts/collect_usage.py --init\n```\n\n## Configuration (optional)\nCreate `config/env.json` to override defaults:\n```json\n{\n  \"SESSIONS_DIR\": \"\",\n  \"TIMEZONE\": \"Asia/Hong_Kong\",\n  \"UTC_OFFSET_HOURS\": 8\n}\n```\n\n## Cron jobs\n```bash\n# Midnight: collect usage data (silent)\nopenclaw cron add --name \"llm-cost:collect\" --message \"collect usage data\" \\\n  --cron \"5 0 * * *\" --tz \"Asia/Hong_Kong\" --session isolated --no-deliver\n\n# 9 AM: daily report to Telegram\nopenclaw cron add --name \"llm-cost:daily\" --message \"llm cost\" \\\n  --cron \"0 9 * * *\" --tz \"Asia/Hong_Kong\" --session isolated\n```"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1278,"uniquenessScore":42,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-12T00:58:47.533Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-12T12:01:31.444Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}