@aivenlab/ats-simulator
v0.3.5
Published
Parasitic AI Agent Token simulator — writes mock usage into ~/.claude and ~/.codex with real JSONL schemas.
Maintainers
Readme
ats-simulator
Parasitic AI Agent Token simulator. Writes mock token-usage records into
~/.claude and ~/.codex using the exact JSONL schemas those CLIs
produce — so any third-party dashboard reading those directories sees the
simulated data with zero configuration.
npm install -g @aivenlab/ats-simulator
ats init # one-time bootstrap
ats generate # interactive wizard
ats list # what simulations are registered
ats show sim_xxx # drill into one
ats export sim_xxx # JSON / JSONL / CSV
ats update # self-update from npm
ats delete sim_xxx # strip one
ats clean # strip everything (atomic rollback)Why "parasitic"?
Most third-party token dashboards (cost trackers, usage analytics, MCP
integrations) hard-code the locations ~/.claude/ and ~/.codex/.
Building a separate, simulator-specific directory means you'd have to teach
every dashboard where to look — and they wouldn't.
ats-simulator writes fake data directly into those existing folders,
using the exact same record shapes that real Claude Code / Codex
sessions produce. Any tool that already reads your local state will Just
Work.
Quickstart
# 1. Install
npm install -g @aivenlab/ats-simulator
# 2. Initialize bookkeeping + snapshot your real history files
ats init
# 3. Generate one simulation (interactive)
ats generate
# → pick platform (Claude / Codex / OpenCode)
# → pick models
# → pick token total + date range + strategy
# → confirm
# 4. Now your dashboards see the simulated usageOr skip the wizard with flags:
ats generate \
--platform claude \
--models claude-fable-5 \
--total 50B \
--duration 7d \
--strategy average \
--name "AI Coding 2026" \
--yesSupported platforms
| Platform | Where data goes | Notes |
|---|---|---|
| Claude Code | ~/.claude/history.jsonl + ~/.claude/projects/<key>/<uuid>.jsonl | JSONL only — never touches sqlite |
| Codex | ~/.codex/history.jsonl + ~/.codex/sessions/ats-sim-*/usage.sim.jsonl | JSONL + sidecar; sqlite untouched |
Every record carries an __ats marker ({simId, model, generatedAt}) so
ats clean can strip them back out atomically. The Claude project dir
also carries a .ats-simulator-owned sentinel — cleaners refuse to
delete any directory that lacks this sentinel.
Cleanup safety
Every record carries an __ats marker. The simulator strips them back out
atomically:
ats delete <simId>— removes records for one sim; everything else untouchedats clean— removes every__atsrecord + the entire-ats-simulated-dataproject dir + everyats-sim-*Codex session dir~/.claude/history.jsonland~/.codex/history.jsonlare rewritten viawrite to .tmp + rename, so a crash mid-write can't corrupt your real history files~/.ats-simulator/backup/keeps a snapshot of both history files from the moment you ranats init
For Claude, the project dir carries a .ats-simulator-owned sentinel file.
ats clean only deletes directories that own this sentinel — real Claude
projects are never touched.
CLI commands
| Command | What it does |
|---|---|
| ats init | Bootstrap ~/.ats-simulator/, snapshot real history files |
| ats models | List available models by vendor |
| ats config [--set k=v ...] | View / update config |
| ats generate | Interactive wizard (6 steps) — produces one simulation |
| ats list / ats ls | Table of all simulations |
| ats show <id> | Drill into one simulation's per-model breakdown |
| ats delete <id> / ats rm <id> | y/N confirmed; strips records for one sim |
| ats clean | y/N confirmed; wipes ALL simulated data |
| ats export <id> [-f json\|jsonl\|csv] [-o dir] | Export to JSON / JSONL / CSV |
| ats models [add\|remove\|reset] | Manage the model roster (ats models to list) |
| ats update [--force] [-y] | Self-update: query npm, reinstall if newer |
ats generate flags
-p, --platform <name> claude | codex | opencode | all
-t, --total <amount> total tokens, e.g. 50B
-s, --start <YYYY-MM-DD> start date (with --end)
-e, --end <YYYY-MM-DD> end date (with --start)
-d, --duration <expr> 30d | 12w | 6m | 1y (ending today)
-m, --models <ids...> model ids, space-separated
--strategy <name> average | proportional | custom
-n, --name <name> simulation name
-P, --project-name <key> Claude project key (default -ats-simulated-data)
--on-existing <mode> fail | overwrite | reuse
-y, --yes skip confirmationDefault model roster
Real model IDs (matching what official CLIs write):
| Vendor | Models |
|---|---|
| Anthropic | claude-opus-5, claude-sonnet-5, claude-fable-5 |
| OpenAI | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna |
| xAI | grok-4.5-build, grok-4.5 |
| China / Moonshot / Zhipu | MiniMax-M3, k3, deepseek-v4-pro, glm-5.2 |
Add or hide models without rebuilding the CLI:
ats models add gpt-5.7-turbo --name "gpt-5.7-turbo" --vendor OpenAI \
--platforms claude,codex
ats models remove gpt-5.7-turbo
ats models reset # back to bundled defaultsUser-added models are persisted in ~/.ats-simulator/config.json under
userModels and merged with the bundled list at runtime.
Generated data shape
Every simulated assistant.message.usage block follows the real Claude
Code schema verbatim:
{
"input_tokens": 71185,
"output_tokens": 12562,
"cache_creation_input_tokens": 6743,
"cache_read_input_tokens": 48706,
"server_tool_use": { "web_search_requests": 0, "web_fetch_requests": 0 },
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 2698,
"ephemeral_5m_input_tokens": 4045
}
}Downstream tools that already parse Claude sessions (dashboards, billing reconcilers, etc.) produce meaningful output without changes.
Testing
npm test # run once
npm run test:watch
npm run lint # type-check64 tests cover token parsing, distribution math, marker construction, cleaner atomicity, schema validation.
File map
src/
├── cli.ts # commander entry
├── commands/ # one file per CLI subcommand
├── lib/
│ ├── paths.ts # ~/.claude, ~/.codex, ~/.ats-simulator paths
│ ├── markers.ts # __ats marker construction & detection
│ ├── io.ts # atomic JSONL read/write + snapshot
│ ├── token.ts # 50B ↔ 50_000_000_000
│ ├── distribution.ts # average / proportional / custom
│ ├── duration.ts # 30d / 12w / 6m / 1y parsing
│ ├── simulator.ts # core: dates + allocations → usage records
│ ├── writers/{claude,codex,opencode}.ts
│ ├── cleaners/{claude,codex,opencode}.ts
│ └── state.ts # concurrent-safe state.json read/write
├── constants/models.ts
└── types/index.tsRequirements
- Node.js ≥ 18 (Node 24+ recommended for the OpenCode writer, which uses
the built-in
node:sqlitemodule) - npm, pnpm, or yarn
Platform support
| OS | Status | Notes |
|---|---|---|
| Linux | ✅ | Primary platform. OpenCode at ~/.local/share/opencode/. |
| macOS | ✅ | OpenCode at ~/Library/Application Support/opencode/. |
| Windows | ✅ | Requires Node ≥ 18. ats command installed via npm .cmd shim. OpenCode at %LOCALAPPDATA%\opencode\. SQLite writer requires Node 24+ (or Node 22 with --experimental-sqlite). |
Tested on: Linux (Node 24). macOS / Windows should work but are not
exercised by CI. File paths use os.homedir() + path.join (no ~
expansion). JSONL writes use \n (no platform-dependent line endings).
