* Write a fake session JSONL with the given token counts and timestamps. * The session-tokens adapter reads from: * HOME/.claude/projects/{slug}/{sessionId}.jsonl * * slug is derived from CLAUDE_PROJECT_DIR = sandbox/project
(sandbox, sessionId, { outputTokens, durationMinutes })
| 59 | * slug is derived from CLAUDE_PROJECT_DIR = sandbox/project |
| 60 | */ |
| 61 | function writeSessionJSONL(sandbox, sessionId, { outputTokens, durationMinutes }) { |
| 62 | const slug = projectSlug(path.join(sandbox, 'project')); |
| 63 | const sessionsDir = path.join(sandbox, 'home', '.claude', 'projects', slug); |
| 64 | const now = Date.now(); |
| 65 | const firstTs = new Date(now - durationMinutes * 60 * 1000).toISOString(); |
| 66 | const lastTs = new Date(now).toISOString(); |
| 67 | |
| 68 | // Two message entries: first at session start, last at session end |
| 69 | const entry1 = JSON.stringify({ |
| 70 | timestamp: firstTs, |
| 71 | message: { |
| 72 | model: 'claude-sonnet-4-6', |
| 73 | usage: { input_tokens: 500, output_tokens: 0, |
| 74 | cache_creation_input_tokens: 0, cache_read_input_tokens: 0 }, |
| 75 | }, |
| 76 | }); |
| 77 | const entry2 = JSON.stringify({ |
| 78 | timestamp: lastTs, |
| 79 | message: { |
| 80 | model: 'claude-sonnet-4-6', |
| 81 | usage: { input_tokens: 0, output_tokens: outputTokens, |
| 82 | cache_creation_input_tokens: 0, cache_read_input_tokens: 0 }, |
| 83 | }, |
| 84 | }); |
| 85 | |
| 86 | fs.writeFileSync(path.join(sessionsDir, `${sessionId}.jsonl`), entry1 + '\n' + entry2 + '\n'); |
| 87 | } |
| 88 | |
| 89 | function runCostTracker(sandbox, sessionId, stateOverride) { |
| 90 | const stateFile = path.join(sandbox, 'project', '.planning', 'telemetry', 'cost-tracker-state.json'); |
no test coverage detected