Qwen CLI
Local-file provider for the Qwen CLI. Reads per-project chat transcripts under ~/.qwen/projects/<project>/chats/*.jsonl and aggregates per-model token totals. No network calls and no authentication.
At a glance
- Provider ID —
qwen_cli - Detection —
~/.qwen/projects/exists, or aqwenbinary onPATH - Auth — local file
- Type — coding agent
- Tracks:
- Total sessions, sessions today, sessions in the last 7 days
- Total input, output, cache-read, and reasoning tokens
- Per-model token totals with upstream provider hint
- Daily series for sessions and tokens
Setup
Auto-detection
OpenUsage registers the provider when ~/.qwen/projects/ exists or qwen is found on PATH. Run a Qwen CLI chat at least once to create the directory.
Manual configuration
{
"accounts": [
{
"id": "qwen_cli",
"provider": "qwen_cli",
"extra": {
"projects_dir": "~/.qwen/projects"
}
}
]
}
projects_dir replaces the default search with an explicit per-project root. The provider only counts .jsonl files that live under a chats segment of the path, so the override must point at the parent that contains <project>/chats/*.jsonl.
Data sources & how each metric is computed
The provider walks projects_dir recursively, picking up files whose name ends in .jsonl and whose path includes a chats segment. Each transcript is parsed as JSON-lines; only records with type: "assistant" and a non-empty usageMetadata block are counted.
Field mapping
Upstream usageMetadata fields → openusage metrics:
| Upstream | openusage metric |
|---|---|
promptTokenCount | total_input_tokens |
candidatesTokenCount | total_output_tokens |
thoughtsTokenCount | total_reasoning_tokens |
cachedContentTokenCount | total_cache_read |
total_cache_write is not exposed: Qwen CLI does not separate cache-creation tokens, so cache write is always 0. total_tokens is the sum of input, output, and reasoning tokens (cached are tracked separately).
Model
message.model is used when present. When missing, the record falls back to the literal string unknown so the per-model row still appears on the detail view. The upstream provider hint is hard-coded to qwen.
Session ID
When a record carries sessionId, that is used. Otherwise the provider derives a session ID from the file path: <project>-<filename-stem>. The project segment is the nearest ancestor directory that is not chats. Two transcripts under the same project that omit sessionId will keep separate identities as long as their file basenames differ.
Session counts
total_sessions— distinct session IDs across all parsed transcriptssessions_today— sessions with at least one assistant turn whosetimestamplands on the current UTC daysessions_7d— sessions with at least one assistant turn in the last 7 days
Timestamps are parsed as RFC 3339 (nano or second precision). When a turn carries no timestamp the file's mtime is used as fallback.
Daily series
DailySeries["sessions"] and DailySeries["tokens"] are populated by day for the analytics chart.
What's NOT tracked
- Cost in USD. Qwen CLI transcripts don't carry pricing and the provider does not run a pricing lookup, so there is no
total_cost_usdmetric.
Caveats
- Only files whose path contains a
chatssegment are scanned. Stray.jsonlfiles placed elsewhere under~/.qwen/projects/are ignored. - The default model string
unknownis intentional. If you see it on the detail view it means Qwen CLI wrote assistant turns without amodelfield.
Troubleshooting
- Tile is empty — run a Qwen CLI chat so a transcript lands under
~/.qwen/projects/<project>/chats/. Confirm withopenusage detect. - All usage attributed to
unknownmodel — open one of the JSONL transcripts and check whether assistant lines include a top-levelmodelfield. If not, the CLI version installed does not emit per-message model metadata. - Wrong directory walked — set
extra.projects_dirto the directory whose subtree contains<project>/chats/*.jsonl.
Related
- Codex CLI — sibling local-file coding-agent provider
- Gemini CLI — sibling local-file coding-agent provider