Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions docs/deployment-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,25 @@ Configure at two levels:

The controller automatically detects and scans all installed tools:

| Tool | Provider | Log Location |
|------|----------|-------------|
| Claude Code | Anthropic | `~/.claude/projects/` |
| Codex CLI | OpenAI | `~/.codex/sessions/` |
| Copilot CLI | GitHub | `~/.copilot/session-state/` |
| Tool | Provider | Usage Source |
|------|----------|--------------|
| Claude Code | Anthropic / compatible wrappers | `~/.config/claude/projects/`, `~/.claude/projects/` |
| Codex CLI | OpenAI | `~/.codex/sessions/`, `~/.codex/archived_sessions/` |
| Cursor | Cursor | Local `state.vscdb` credential + Cursor usage CSV API |
| Copilot CLI | GitHub | `~/.copilot/otel/`, `~/.copilot/session-state/` |
| Copilot for VS Code | GitHub | VS Code logs and `User/workspaceStorage/**/chatSessions/*.{json,jsonl}` |
| Gemini CLI | Google | `~/.gemini/tmp/` |
| Amp | Sourcegraph | `~/.local/share/amp/threads/` |
| Kimi Code | Moonshot | `$KIMI_CODE_HOME/sessions/`(默认 `~/.kimi-code/sessions/`) |
| Kimi CLI(旧版) | Moonshot | `~/.kimi/sessions/` |
| Qwen Code | Alibaba | `~/.qwen/tmp/` |
| Droid | — | `~/.factory/sessions/` |
| OpenCode | — | `~/.local/share/opencode/` |
| Antigravity | Google | `~/.gemini/antigravity/` interaction metadata |
| Amp | Model provider (Sourcegraph product) | `~/.local/share/amp/threads/` |
| Kimi Code | Moonshot | `$KIMI_CODE_HOME/sessions/` (default `~/.kimi-code/sessions/`) |
| Kimi CLI (legacy) | Moonshot | `~/.kimi/sessions/` |
| Qwen Code | Alibaba | `~/.qwen/projects/`, legacy `~/.qwen/tmp/` |
| Droid | Factory | `~/.factory/sessions/*.settings.json` |
| OpenCode | OpenCode | `~/.local/share/opencode/opencode.db`, legacy `storage/message/` |
| Pi / Oh My Pi | Multiple | `~/.pi/agent/sessions/`, `~/.omp/agent/sessions/` |

The scanner implementations are compatibility-audited against the overlapping
MIT-licensed [tokscale](https://github.com/junhoyeo/tokscale) parsers. Some
legacy IDE artifacts expose interaction timestamps but no reliable token
counters; AIUsage keeps those as zero-token activity instead of estimating
billable usage from conversation content.
31 changes: 25 additions & 6 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,31 @@
- scheduling automatic sync to an AIUsage Worker
- diagnosing configuration and connectivity issues

Kimi scanning supports both legacy Kimi CLI data in `~/.kimi/sessions/` and
new Kimi Code data in `$KIMI_CODE_HOME/sessions/` (default:
`~/.kimi-code/sessions/`). It reads only token counters and session metadata
from `wire.jsonl`; conversation content is not uploaded. The new wire-format
parser references the MIT-licensed [tokscale](https://github.com/junhoyeo/tokscale)
implementation.
The local scanners have been compatibility-audited against the overlapping
parsers in the MIT-licensed [tokscale](https://github.com/junhoyeo/tokscale)
project. AIUsage keeps tool-specific safeguards where the source semantics
differ, rather than treating every local record as billable token usage.

## Local scanner coverage

| Tool | Sources and compatibility behavior |
|------|------------------------------------|
| Claude Code | `~/.config/claude/projects/` and `~/.claude/projects/` JSONL; deduplicates parent/sidechain replays, merges streaming snapshots per token field, and honors wrapper providers. Aggregate `stats-cache.json` is intentionally not converted into guessed per-message usage. |
| Codex CLI | Active and archived `~/.codex` sessions; fork-aware replay boundaries, inherited baselines, `last_token_usage`, and total-delta fallback. |
| Cursor | Reads the local `state.vscdb` credential and requests Cursor's token-strategy usage CSV; the database is snapshotted when locked. |
| Copilot CLI | OpenTelemetry JSONL under `~/.copilot/otel/` plus `session-state` shutdown totals; granular inference spans supersede same-trace aggregates. |
| Copilot for VS Code | Chat logs, legacy session JSON, and modern CRDT `workspaceStorage/**/chatSessions/*.jsonl`; modern sessions contribute real token fields, while legacy records remain interaction-only. |
| Gemini CLI | Session JSON/JSONL, headless stats, and `$set.messages` updates under `~/.gemini/tmp/`, with last-write-wins request deduplication. |
| Antigravity | `~/.gemini/antigravity/brain` and browser-recording metadata; currently reports interaction counts because those artifacts do not expose reliable token counters. |
| Amp | `~/.local/share/amp/threads/`; reconciles the usage ledger with message usage so partial ledgers are completed without double-counting. |
| Kimi CLI / Kimi Code | Legacy `~/.kimi/sessions/` and `$KIMI_CODE_HOME/sessions/` (default `~/.kimi-code/sessions/`) `wire.jsonl`; handles progressive status snapshots and nested agent sessions. |
| Qwen Code | Current `~/.qwen/projects/` and legacy `~/.qwen/tmp/` chat JSONL, with session/position deduplication and cache-aware input accounting. |
| Droid | `~/.factory/sessions/*.settings.json`; uses persisted token totals first and the transcript only as a model fallback. |
| OpenCode | OpenCode 1.2+ `opencode.db` through read-only `sqlite3`, plus legacy `storage/message/*.json`. |
| Pi / Oh My Pi | `~/.pi/agent/sessions/` and `~/.omp/agent/sessions/` JSONL, including provider, cache-write, and session metadata. |

Only token counters and session metadata are aggregated or uploaded. Conversation
content and local credentials are never uploaded.

## Install

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aiusage/cli",
"version": "1.7.3",
"version": "1.7.4",
"description": "Track and sync AI tool token usage across devices.",
"license": "MIT",
"private": false,
Expand Down
53 changes: 34 additions & 19 deletions packages/cli/src/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ async function countFiles(dir: string, exts: string[], cap = 1000): Promise<numb

// 每个工具的数据目录、检测的文件扩展名
interface ToolDef {
dir: string;
dirs: string[];
label: string;
exts: string[];
}
Expand Down Expand Up @@ -152,30 +152,45 @@ export async function runDoctor(lang: Lang = 'zh'): Promise<Check[]> {
const g3 = s.groupTools;
const home = homedir();
const tools: ToolDef[] = [
{ dir: join(home, '.claude', 'projects'), label: 'Claude Code', exts: ['.jsonl'] },
{ dir: join(home, '.codex'), label: 'Codex CLI', exts: ['.jsonl'] },
{ dir: join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage'), label: 'Cursor', exts: ['.vscdb'] },
{ dir: join(home, '.copilot', 'session-state'), label: 'Copilot CLI', exts: ['.jsonl'] },
{ dir: join(home, 'Library', 'Application Support', 'Code', 'logs'), label: 'Copilot VS Code', exts: ['.log'] },
{ dir: join(home, '.gemini', 'tmp'), label: 'Gemini CLI', exts: ['.json'] },
{ dir: join(home, '.gemini', 'antigravity'), label: 'Antigravity', exts: ['.json'] },
{ dir: join(home, '.qwen', 'tmp'), label: 'Qwen Code', exts: ['.jsonl'] },
{ dir: join(resolveKimiCodeHome(home), 'sessions'), label: 'Kimi Code', exts: ['.jsonl'] },
{ dir: join(home, '.kimi', 'sessions'), label: 'Kimi CLI (legacy)', exts: ['.jsonl'] },
{ dir: join(home, '.local', 'share', 'amp', 'threads'), label: 'Amp', exts: ['.json'] },
{ dir: join(home, '.factory', 'sessions'), label: 'Droid', exts: ['.jsonl', '.json'] },
{ dir: join(home, '.local', 'share', 'opencode'), label: 'OpenCode', exts: ['.json'] },
{ dir: join(home, '.pi', 'agent', 'sessions'), label: 'Pi', exts: ['.jsonl'] },
{ dirs: [join(home, '.config', 'claude', 'projects'), join(home, '.claude', 'projects')], label: 'Claude Code', exts: ['.jsonl'] },
{ dirs: [join(home, '.codex')], label: 'Codex CLI', exts: ['.jsonl'] },
{ dirs: [join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage')], label: 'Cursor', exts: ['.vscdb'] },
{ dirs: [join(home, '.copilot', 'session-state'), join(home, '.copilot', 'otel')], label: 'Copilot CLI', exts: ['.jsonl'] },
{
dirs: [
join(home, 'Library', 'Application Support', 'Code', 'logs'),
join(home, 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage'),
],
label: 'Copilot VS Code',
exts: ['.log', '.json', '.jsonl'],
},
{ dirs: [join(home, '.gemini', 'tmp')], label: 'Gemini CLI', exts: ['.json', '.jsonl'] },
{ dirs: [join(home, '.gemini', 'antigravity')], label: 'Antigravity', exts: ['.json'] },
{ dirs: [join(home, '.qwen', 'projects'), join(home, '.qwen', 'tmp')], label: 'Qwen Code', exts: ['.jsonl'] },
{ dirs: [join(resolveKimiCodeHome(home), 'sessions')], label: 'Kimi Code', exts: ['.jsonl'] },
{ dirs: [join(home, '.kimi', 'sessions')], label: 'Kimi CLI (legacy)', exts: ['.jsonl'] },
{ dirs: [join(home, '.local', 'share', 'amp', 'threads')], label: 'Amp', exts: ['.json'] },
{ dirs: [join(home, '.factory', 'sessions')], label: 'Droid', exts: ['.settings.json'] },
{ dirs: [join(home, '.local', 'share', 'opencode')], label: 'OpenCode', exts: ['.db', '.json'] },
{ dirs: [join(home, '.pi', 'agent', 'sessions'), join(home, '.omp', 'agent', 'sessions')], label: 'Pi / OMP', exts: ['.jsonl'] },
];

for (const tool of tools) {
try {
await stat(tool.dir);
} catch {
let installed = false;
let n = 0;
for (const dir of tool.dirs) {
try {
await stat(dir);
installed = true;
n += await countFiles(dir, tool.exts);
} catch {
// 检查同一工具的其他兼容目录。
}
}
if (!installed) {
checks.push({ group: g3, name: tool.label, status: 'warn', message: s.notInstalled });
continue;
}
const n = await countFiles(tool.dir, tool.exts);
if (n > 0) {
checks.push({ group: g3, name: tool.label, status: 'ok', message: s.hasData(n) });
} else {
Expand Down
Loading